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


23
24
25
26
27
28
29
30
# File 'lib/operators/op.rb', line 23

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



13
14
15
# File 'lib/operators/op.rb', line 13

def x
  @x
end

Class Method Details

.init_simplify_dictObject

Initializes the simplification dictionary (one for each class)

  • returns: ‘Hash` with simplification dictionary



211
212
213
# File 'lib/operators/op.rb', line 211

def self.init_simplify_dict
  @simplify_dict = { }
end

.numeric_to_const(x) ⇒ Object



32
33
34
35
36
37
38
# File 'lib/operators/op.rb', line 32

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



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

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


251
252
253
# File 'lib/operators/op.rb', line 251

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


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

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


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

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


143
144
145
# File 'lib/operators/op.rb', line 143

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


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

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

#-@Object

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

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


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

def -@
  CAS.invert(self)
end

#/(op) ⇒ Object

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

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


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

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


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

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


280
281
282
# File 'lib/operators/op.rb', line 280

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`


266
267
268
269
270
271
272
273
274
275
# File 'lib/operators/op.rb', line 266

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`


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

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)


45
46
47
48
49
# File 'lib/operators/op.rb', line 45

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


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

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


19
20
21
22
# File 'lib/Mr.CAS/graphviz.rb', line 19

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


329
330
331
# File 'lib/functions/fnc-conditions.rb', line 329

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


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

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


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

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

#inspectObject

Inspector for the current object

* **returns**: `String`


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

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


315
316
317
# File 'lib/functions/fnc-box-conditions.rb', line 315

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


191
192
193
194
195
196
197
198
# File 'lib/operators/op.rb', line 191

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


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

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


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

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


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

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


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/operators/op.rb', line 108

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



120
121
122
123
# File 'lib/Mr.CAS/c.rb', line 120

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`


135
136
137
# File 'lib/operators/op.rb', line 135

def to_code
  "#{@x}"
end

#to_sObject

Convert expression to string

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


128
129
130
# File 'lib/operators/op.rb', line 128

def to_s
  "#{@x}"
end