Class: CAS::Op

Inherits:
Object
  • Object
show all
Defined in:
lib/operators/op.rb,
lib/Mr.CAS/c.rb,
lib/Mr.CAS/c-opt.rb,
lib/Mr.CAS/graphviz.rb,
lib/functions/fnc-conditions.rb,
lib/functions/fnc-box-conditions.rb

Overview

_ _ _

/ __|___ _ _| |_ __ _(_)_ _  ___ _ _ ___

| (__/ _ \ ' \ _/ _` | | ' / -_) '_(_-<

\___\___/_||_\__\__,_|_|_||_\___|_| /__/

Direct Known Subclasses

Abs, Acos, Asin, Atan, BinaryOp, Constant, Cos, Exp, Invert, Ln, NaryOp, Sin, Sqrt, Tan, Variable

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(x) ⇒ Op

Initialize a new empty operation container. This is a virtual class and the other must inherit from this basic container. Some methods raise a `CAS::CASError` if called. The input element is a Numric, to create a constant. `CAS::Op` specifies operations with a single variable

* **argument**: `Numeric` to be converted in `CAS::Constant` or `CAS::Op` child operation
* **returns**: `CAS::Op` instance

46
47
48
49
50
51
52
53
# File 'lib/operators/op.rb', line 46

def initialize(x)
  if x.is_a? Numeric
    x = Op.numeric_to_const x
  end
  CAS::Help.assert(x, CAS::Op)

  @x = x
end

Instance Attribute Details

#xObject (readonly)

Argument of the operation


36
37
38
# File 'lib/operators/op.rb', line 36

def x
  @x
end

Class Method Details

.init_simplify_dictObject

Initializes the simplification dictionary (one for each class)

  • returns: `Hash` with simplification dictionary


234
235
236
# File 'lib/operators/op.rb', line 234

def self.init_simplify_dict
  @simplify_dict = { }
end

.numeric_to_const(x) ⇒ Object


55
56
57
58
59
60
61
# File 'lib/operators/op.rb', line 55

def self.numeric_to_const(x)
  if CAS::NumericToConst[x]
    return CAS::NumericToConst[x]
  else
    return CAS::const x
  end
end

.simplify_dict(k) ⇒ Object

Returns an element of a


239
240
241
242
243
244
# File 'lib/operators/op.rb', line 239

def self.simplify_dict(k)
  @simplify_dict.keys.each do |op|
    return @simplify_dict[op] if op.simplify == k.simplify
  end
  return nil
end

Instance Method Details

#!=(op) ⇒ Object

Disequality operator, the standard operator is overloaded :warning: this operates on the graph, not on the math See `CAS::equal`, etc.

* **argument**: `CAS::Op` to be tested against
* **returns**: `FalseClass` if equal, `TrueClass` if differs

274
275
276
# File 'lib/operators/op.rb', line 274

def !=(op)
  not self.==(op)
end

#*(op) ⇒ Object

Returns a product of two `CAS::Op`s

* **argument**: `CAS::Op` tree
* **returns**: `CAS::Op` new object

182
183
184
# File 'lib/operators/op.rb', line 182

def *(op)
  CAS::Prod.new [self, op]
end

#**(op) ⇒ Object

Returns the power of two `CAS::Op`s

* **argument**: `CAS::Op` tree
* **returns**: `CAS::Op` new object

198
199
200
# File 'lib/operators/op.rb', line 198

def **(op)
  CAS.pow(self, op)
end

#+(op) ⇒ Object

Returns a sum of two `CAS::Op`s

* **argument**: `CAS::Op` tree
* **returns**: `CAS::Op` new object

166
167
168
# File 'lib/operators/op.rb', line 166

def +(op)
  CAS::Sum.new [self, op]
end

#-(op) ⇒ Object

Returns a difference of two `CAS::Op`s

* **argument**: `CAS::Op` tree
* **returns**: `CAS::Op` new object

174
175
176
# File 'lib/operators/op.rb', line 174

def -(op)
  CAS::Diff.new self, op
end

#[email protected]Object

Unary operator for inversion of a `CAS::Op`

* **returns**: `CAS::Op` new object

205
206
207
# File 'lib/operators/op.rb', line 205

def [email protected]
  CAS.invert(self)
end

#/(op) ⇒ Object

Returns a division of two `CAS::Op`s

* **argument**: `CAS::Op` tree
* **returns**: `CAS::Op` new object

190
191
192
# File 'lib/operators/op.rb', line 190

def /(op)
  CAS::Div.new self, op
end

#==(op) ⇒ Object

Equality operator, the standard operator is overloaded :warning: this operates on the graph, not on the math See `CAS::equal`, etc.

* **argument**: `CAS::Op` to be tested against
* **returns**: `TrueClass` if equal, `FalseClass` if differs

259
260
261
262
263
264
265
266
# File 'lib/operators/op.rb', line 259

def ==(op)
  # CAS::Help.assert(op, CAS::Op)
  if op.is_a? CAS::Op
    return false if op.is_a? CAS::BinaryOp
    return (self.class == op.class and @x == op.x)
  end
  false
end

#argsObject

Returns a list of all `CAS::Variable`s of the current tree

* **returns**: `Array` of `CAS::Variable`s

303
304
305
# File 'lib/operators/op.rb', line 303

def args
  @x.args.uniq
end

#as_proc(bind = nil) ⇒ Object

Evaluates the proc against a given context. It is like having a snapshot of the tree transformed in a callable object. Obviously **if the tree changes, the generated proc does notchanges**. The proc takes as input a feed dictionary in which each variable is identified through the `CAS::Variable#name` key.

The proc is evaluated in the context devined by the input `Binding` object If `nil` is passed, the `eval` will run in this local context

* **argument**: `Binding` or `NilClass` that is the context of the Ruby VM
* **returns**: `Proc` object with a single argument as an `Hash`

289
290
291
292
293
294
295
296
297
298
# File 'lib/operators/op.rb', line 289

def as_proc(bind=nil)
  args_ext = self.args.map { |e| "#{e} = fd[\"#{e}\"];" }
  code = "Proc.new do |fd|; #{args_ext.join " "} #{self.to_code}; end"
  if bind # All objects have eval value, we bind when not nil
    # CAS::Help.assert(bind, Binding)
    bind.eval(code)
  else
    eval(code)
  end
end

#call(f) ⇒ Object

Call resolves the operation tree in a `Numeric` (if `Fixnum`) or `Float` (depends upon promotions). As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name` as keys, and a `Numeric` as a value

“` ruby x, y = CAS::vars :x, :y f = (x ** 2) + (y ** 2) f.call(=> 1, y => 2) # => 2 “`

* **argument**: `Hash` with feed dictionary
* **returns**: `Numeric`

112
113
114
115
116
# File 'lib/operators/op.rb', line 112

def call(f)
  CAS::Help.assert(f, Hash)

  @x.call(f)
end

#depend?(v) ⇒ Boolean

Return the dependencies of the operation. Requires a `CAS::Variable` and it is one of the recursve method (implicit tree resolution)

* **argument**: `CAS::Variable` instance
* **returns**: `TrueClass` if depends, `FalseClass` if not

Returns:

  • (Boolean)

68
69
70
71
72
# File 'lib/operators/op.rb', line 68

def depend?(v)
  CAS::Help.assert(v, CAS::Op)

  @x.depend? v
end

#diff(v) ⇒ Object

Return the derivative of the operation using the chain rule The input is a `CAS::Op` because it can handle derivatives with respect to functions. E.g.:

“`

f(x) = (ln(x))**2
g(x) = ln(x)

d f(x)
------ = 2 ln(x)
d g(x)

“`

* **argument**: `CAS::Op` object of the derivative
* **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants

89
90
91
92
93
94
95
96
# File 'lib/operators/op.rb', line 89

def diff(v)
  CAS::Help.assert(v, CAS::Op)

  if @x.depend? v
    return @x.diff(v)
  end
  CAS::Zero
end

#dot_graphObject

Return the local Graphviz node of the tree

* **returns**: `String` of local Graphiz node

42
43
44
45
# File 'lib/Mr.CAS/graphviz.rb', line 42

def dot_graph
  cls = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
  "#{cls} -> #{@x.dot_graph}\n"
end

#equal(v) ⇒ Object

Shortcut for creating equality condition.

* **argument**: `CAS::Op` ther element of the condition
* **returns**: `CAS::Equal` new instance

344
345
346
# File 'lib/functions/fnc-conditions.rb', line 344

def equal(v)
  CAS.equal(self, v)
end

#greater(v) ⇒ Object

Shortcut for creating greater kind condition.

* **argument**: `CAS::Op` ther element of the condition
* **returns**: `CAS::Greater` new instance

352
353
354
# File 'lib/functions/fnc-conditions.rb', line 352

def greater(v)
  CAS.greater(self, v)
end

#greater_equal(v) ⇒ Object

Shortcut for creating a greater equal kind condition.

* **argument**: `CAS::Op` ther element of the condition
* **returns**: `CAS::GreaterEqual` new instance

368
369
370
# File 'lib/functions/fnc-conditions.rb', line 368

def greater_equal(v)
  CAS.greater_equal(self, v)
end

#inspectObject

Inspector for the current object

* **returns**: `String`

249
250
251
# File 'lib/operators/op.rb', line 249

def inspect
  "#{self.class}(#{@x.inspect})"
end

#limit(a, b, type = :closed) ⇒ Object

Shortcut for creating a new box condition. It requires limits and type:

* **argument**: `CAS::Constant` lower limit
* **argument**: `CAs::Constant` upper limit
* **argument**: `Symbol` of condition type it can be:
   - `:closed` for `CAs::BoxConditionClosed`
   - `:open` for `CAs::BoxConditionOpen`
   - `:upper_closed` for `CAs::BoxConditionUpperClosed`
   - `:lower_closed` for `CAs::BoxConditionLowerClosed`
* **returns**: `CAS::BoxCondition` new instance

338
339
340
# File 'lib/functions/fnc-box-conditions.rb', line 338

def limit(a, b, type=:closed)
  return CAS::box(self, a, b, type)
end

#simplifyObject

Simplification callback. It simplify the subgraph of each node until all possible simplification are performed (thus the execution time is not deterministic).

* **returns**: `CAS::Op` simplified version

214
215
216
217
218
219
220
221
# File 'lib/operators/op.rb', line 214

def simplify
  hash = @x.to_s
  @x = @x.simplify
  while @x.to_s != hash
    hash = @x.to_s
    @x = @x.simplify
  end
end

#simplify_dictionaryObject

Simplify dictionary performs a dictionary simplification that is the class variable `@simplify_dict`

* **returns**: `CAS::Op` self

227
228
229
# File 'lib/operators/op.rb', line 227

def simplify_dictionary
  self.class.simplify_dict(@x) || self
end

#smaller(v) ⇒ Object

Shortcut for creating a smaller kind condition.

* **argument**: `CAS::Op` ther element of the condition
* **returns**: `CAS::Smaller` new instance

360
361
362
# File 'lib/functions/fnc-conditions.rb', line 360

def smaller(v)
  CAS.smaller(self, v)
end

#smaller_equal(v) ⇒ Object

Shortcut for creating a smaller equal kind condition.

* **argument**: `CAS::Op` ther element of the condition
* **returns**: `CAS::SmallerEqual` new instance

376
377
378
# File 'lib/functions/fnc-conditions.rb', line 376

def smaller_equal(v)
  CAS.smaller_equal(self, v)
end

#subs(dt) ⇒ Object

Perform substitution of a part of the graph using a data table:

“` ruby x, y = CAS::vars :x, :y f = (x ** 2) + (y ** 2) puts f # => (x^2) + (y^2) puts f.subs(=> CAS::ln(y)) # => (ln(y)^2) + (y^2) “`

* **argument**: `Hash` with substitution table
* **returns**: `CAS::Op` (`self`) with substitution performed

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/operators/op.rb', line 131

def subs(dt)
  CAS::Help.assert(dt, Hash)
  sub = dt.keys.select { |e| e == @x }[0]
  if sub
    if dt[sub].is_a? CAS::Op
      @x = dt[sub]
    elsif dt[sub].is_a? Numeric
      @x = CAS::const dt[sub]
    else
      raise CAS::CASError, "Impossible subs. Received a #{dt[@x].class} = #{dt[@x]}"
    end
  else
    @x.subs(dt)
  end
  return self
end

#to_c_lib(name) ⇒ Object


143
144
145
146
# File 'lib/Mr.CAS/c.rb', line 143

def to_c_lib(name)
  CAS::Help.assert(name, String)
  [CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
end

#to_codeObject

Convert expression to code (internal, for `CAS::Op#to_proc` method)

* **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`

158
159
160
# File 'lib/operators/op.rb', line 158

def to_code
  "#{@x}"
end

#to_sObject

Convert expression to string

* **returns**: `String` to print on screen

151
152
153
# File 'lib/operators/op.rb', line 151

def to_s
  "#{@x}"
end