Class: CAS::NaryOp

Inherits:
Op
  • Object
show all
Defined in:
lib/operators/nary-op.rb,
lib/Mr.CAS/graphviz.rb

Overview

This is an attempt to build some sort of node in the graph that has arbitrary number of childs node. It should help implement more easily some sort of better simplifications engine

This is an incredibly experimental feature.

Direct Known Subclasses

Function, Prod, Sum

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Op

#!=, #*, #**, #+, #-, #-@, #/, #as_proc, #equal, #greater, #greater_equal, init_simplify_dict, #limit, numeric_to_const, simplify_dict, #simplify_dictionary, #smaller, #smaller_equal, #to_c_lib

Constructor Details

#initialize(*xs) ⇒ NaryOp

Initialize a new empty N-elements operation container. This is a virtual class, and other must inherit from this basical container

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


18
19
20
21
22
23
24
25
26
27
# File 'lib/operators/nary-op.rb', line 18

def initialize(*xs)
  @x = []
  xs.flatten.each do |x|
    if x.is_a? Numeric
      x = Op.numeric_to_const x
    end
    CAS::Help.assert(x, CAS::Op)
    @x << x
  end
end

Instance Attribute Details

#xObject (readonly)

List of arguments of the operation



11
12
13
# File 'lib/operators/nary-op.rb', line 11

def x
  @x
end

Instance Method Details

#==(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


153
154
155
156
157
158
159
160
161
162
163
# File 'lib/operators/nary-op.rb', line 153

def ==(op)
  # CAS::Help.assert(op, CAS::Op)
  if op.is_a? CAS::NaryOp
    return false if @x.size != op.x.size
    0.upto(@x.size - 1) do |i|
      return false if @x[i] != op.x[i]
    end
    return true
  end
  false
end

#__reduce_constants(xs) ⇒ Object

Collects all the constants and tries to reduce them to a single constant. Requires a block to understand what it should do with the constants

  • requires: input ‘Array` of `CAS::Op`

  • returns: new ‘Array` of `CAS::Op`

  • block: yields an ‘Array` of `CAS::Constant` and an `Array` of others `CAS::Op`, requires an `Array` back



220
221
222
223
224
225
226
227
228
# File 'lib/operators/nary-op.rb', line 220

def __reduce_constants(xs)
  const = []
  xs.each { |x| const << x if x.is_a? CAS::Constant }
  if const.size > 0
    yield const, (xs - const)
  else
    xs
  end
end

#__reduce_multeplicity(xs) ⇒ Object

Reduce multeplicity will scan for elements that are equal in the definition of the sum and will reduce their multeplicity. A block can be used to do something different. For example in nary-product we use it like this:

“‘ ruby end “`

In general it works like that:

“‘

a + a + b + c => 2 * a + b + c
a * a * b * a => (a ** b) * b

“‘ But operates only on Array level! This is an internal function and should never be used

* **requires**: An `Array`
* **returns**: An `Array` with multeplicity reduced
* **block**: yields the count and the op. Get the value to insert in a new
  `Array` that is the returned `Array`


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/operators/nary-op.rb', line 197

def __reduce_multeplicity(xs)
  count = Hash.new(0)
  xs.each do |x|
    e = x
    count.keys.each { |d| e = d if x == d  }
    count[e] += 1
  end
  count.map do |k, v|
    if block_given?
      yield(k, v)
    else
      v > 1 ? CAS.const(v) * k : k
    end
  end
end

#argsObject

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

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


168
169
170
171
172
# File 'lib/operators/nary-op.rb', line 168

def args
  r = []
  @x.each { |x| r += x.args }
  return r.uniq
end

#call(fd) ⇒ 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**: `Array` of `Numeric`


76
77
78
79
# File 'lib/operators/nary-op.rb', line 76

def call(fd)
  CAS::Help.assert(fd, Hash)
  return @x.map { |x| x.call(fd) }
end

#depend?(v) ⇒ Boolean

Returns the dependencies of the operation. Require a ‘CAS::Variable` and it is one of the recursive method (implicit tree resolution)

* **argument**: `CAS::Variable` instance
* **returns**: `TrueClass` or `FalseClass`

Returns:

  • (Boolean)


34
35
36
37
# File 'lib/operators/nary-op.rb', line 34

def depend?(v)
  CAS::Help.assert(v, CAS::Op)
  @x.include? v
end

#diff(v) ⇒ Object

Return a list of derivative using the chain rule. The input is a operation:

“‘

f(x) = g(x) + h(x) + l(x) + m(x)

d f(x)
------ = g'(x) + h'(x) + l'(x) + m'(x)
  dx

d f(x)
------ = 1
d g(x)

“‘

* **argument**: `CAS::Op` object of the derivative
* **returns**: `CAS::NaryOp` of derivative


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

def diff(v)
  CAS::Help.assert(v, CAS::Op)
  if self.depend?(v)
    return @x.map { |x| x.diff(v) }
  end
  return CAS::Zero
end

#dot_graphObject

Return the local Graphviz node of the tree

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


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

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

#inspectObject

Inspector for the current object

* **returns**: `String`


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

def inspect
  "#{self.class}(#{@x.map(&:inspect).join(", ")})"
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


131
132
133
134
135
136
137
138
# File 'lib/operators/nary-op.rb', line 131

def simplify
  hash = self.to_s
  @x = @x.map { |x| x.simplify }
  while self.to_s != hash
    hash = self.to_s
    @x = @x.map { |x| x.simplify }
  end
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::NaryOp` (`self`) with substitution performed


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/operators/nary-op.rb', line 94

def subs(dt)
  CAS::Help.assert(dt, Hash)
  @x = @x.map { |z| z.subs(dt) || z }
  @x.each_with_index do |x, k|
    sub = dt.keys.select { |e| e == x }[0]
    if sub
      if dt[sub].is_a? CAS::Op
        @x[k] = dt[sub]
      elsif dt[sub].is_a? Numeric
        @x[k] = CAS::const dt[sub]
      else
        raise CAS::CASError, "Impossible subs. Received a #{dt[sub].class} = #{dt[sub]}"
      end
    end
  end
  return self
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`


122
123
124
# File 'lib/operators/nary-op.rb', line 122

def to_code
  return "(#{@x.map(&:to_code).join(", ")})"
end

#to_sObject

Convert expression to string

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


115
116
117
# File 'lib/operators/nary-op.rb', line 115

def to_s
  return "(#{@x.map(&:to_s).join(", ")})"
end