ragni-cas 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
File without changes
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # __ __ _ _ _ ___ _ _
4
+ # | \/ |__ _| |_| |__ _| |__ | _ \ |_ _ __ _(_)_ _
5
+ # | |\/| / _` | _| / _` | '_ \ | _/ | || / _` | | ' \
6
+ # |_| |_\__,_|\__|_\__,_|_.__/ |_| |_|\_,_\__, |_|_||_|
7
+ # |___/
8
+
9
+ module CAS
10
+ {
11
+ # Terminal nodes
12
+ CAS::Constant => Proc.new { |_v| "#{x}", nil },
13
+ CAS::Variable => Proc.new { |_v| "#{name}", nil },
14
+ CAS::PI_CONSTANT => Proc.new { |_v| "pi", nil },
15
+ CAS::INFINITY_CONSTANT => Proc.new { |_v| "Inf", nil },
16
+ CAS::NEG_INFINITY_CONSTANT => Proc.new { |_v| "(-Inf)", nil },
17
+ CAS::E_CONSTANT => Proc.new { |_v|
18
+ },
19
+ # Base functions
20
+ CAS::Sum => Proc.new { "(#{x.to_c} + #{y.to_c})" },
21
+ CAS::Diff => Proc.new { "(#{x.to_c} - #{y.to_c})" },
22
+ CAS::Prod => Proc.new { "(#{x.to_c} * #{y.to_c})" },
23
+ CAS::Pow => Proc.new { "pow(#{x.to_c}, #{y.to_c})" },
24
+ CAS::Div => Proc.new { "(#{x.to_c}) / (#{y.to_c} + )" },
25
+ CAS::Sqrt => Proc.new { "sqrt(#{x.to_c})" },
26
+ CAS::Invert => Proc.new { "(-#{x.to_c})" },
27
+ CAS::Abs => Proc.new { "fabs(#{x.to_c})" },
28
+
29
+ # Trigonometric functions
30
+ CAS::Sin => Proc.new { "sin(#{x.to_c})" },
31
+ CAS::Asin => Proc.new { "asin(#{x.to_c})" },
32
+ CAS::Cos => Proc.new { "cos(#{x.to_c})" },
33
+ CAS::Acos => Proc.new { "acos(#{x.to_c})" },
34
+ CAS::Tan => Proc.new { "tan(#{x.to_c})" },
35
+ CAS::Atan => Proc.new { "atan(#{x.to_c})" },
36
+
37
+ # Trascendent functions
38
+ CAS::Exp => Proc.new { "exp(#{x.to_c})" },
39
+ CAS::Ln => Proc.new { "log(#{x.to_c})" },
40
+
41
+ # Box Conditions
42
+ # CAS::BoxConditionOpen => Proc.new {
43
+ # ["double __t_#{x.object_id} = #{x.to_c};",
44
+ # "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
45
+ # },
46
+ # CAS::BoxConditionUpperClosed => Proc.new {
47
+ # ["double __t_#{x.object_id} = #{x.to_c};",
48
+ # "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
49
+ # },
50
+ # CAS::BoxConditionLowerClosed => Proc.new {
51
+ # ["double __t_#{x.object_id} = #{x.to_c};",
52
+ # "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
53
+ # },
54
+ # CAS::BoxConditionClosed => Proc.new {
55
+ # ["double __t_#{x.object_id} = #{x.to_c};",
56
+ # "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
57
+ # },
58
+
59
+ # Conditions
60
+ CAS::Equal => Proc.new { "(#{x.to_c} == #{y.to_c})" },
61
+ CAS::Smaller => Proc.new { "(#{x.to_c} < #{y.to_c})" },
62
+ CAS::Greater => Proc.new { "(#{x.to_c} > #{y.to_c})" },
63
+ CAS::SmallerEqual => Proc.new { "(#{x.to_c} <= #{y.to_c})" },
64
+ CAS::GreaterEqual => Proc.new { "(#{x.to_c} >= #{y.to_c})" },
65
+
66
+ # Piecewise
67
+ CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
68
+ CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
69
+ CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
70
+ }.each do |cls, blk|
71
+ cls.send(:define_method, "__to_matlab", &blk)
72
+ end
73
+
74
+ class Op
75
+ def to_c_lib(name)
76
+ CAS::Help.assert(name, String)
77
+ [CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
78
+ end
79
+ end
80
+
81
+ end
@@ -1,85 +1,6 @@
1
1
  #!/usr/vin/env ruby
2
2
 
3
3
  module CAS
4
- # ___
5
- # / __|_ _ _ __
6
- # \__ \ || | ' \
7
- # |___/\_,_|_|_|_|
8
-
9
- ##
10
- # **Sum basic operation**. As for now it is implemented as a simple
11
- # binary operation. It will be implemented as n-ary op.
12
- class Sum < CAS::BinaryOp
13
- # Performs the sum between two `CAS::Op`
14
- #
15
- # ```
16
- # d
17
- # ---- (f(x) + g(x)) = f'(x) + g'(x)
18
- # dx
19
- # ```
20
- #
21
- # * **argument**: `CAS::Op` argument of derivative
22
- # * **returns**: `CAS::Op` derivative
23
- def diff(v)
24
- left, right = super v
25
-
26
- return left if right == CAS::Zero
27
- return right if left == CAS::Zero
28
- left + right
29
- end
30
-
31
- # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
32
- # or `Float` (depends upon promotions).
33
- # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
34
- # as keys, and a `Numeric` as a value. In this case it will call
35
- # the `Fixnum#overloaded_plus`, that is the old plus function.
36
- #
37
- # * **argument**: `Hash` with feed dictionary
38
- # * **returns**: `Numeric`
39
- def call(f)
40
- CAS::Help.assert(f, Hash)
41
- return @x.call(f).overloaded_plus(@y.call(f))
42
- end
43
-
44
- # Convert expression to string
45
- #
46
- # * **returns**: `String` to print on screen
47
- def to_s
48
- "(#{@x} + #{@y})"
49
- end
50
-
51
- # Same as `CAS::Op`
52
- #
53
- # Simplifcation engine supports:
54
- #
55
- # * x + 0 = x
56
- # * 0 + y = y
57
- # * x + x = 2 x
58
- # * x + (-x) = 0
59
- # * x + (-y) = x - y
60
- # * 1 + 2 = 3 (constants reduction)
61
- #
62
- # * **returns**: `CAS::Op` simplified version
63
- def simplify
64
- super
65
- return @y if @x == CAS::Zero
66
- return @x if @y == CAS::Zero
67
- return @x * 2.0 if @x == @y
68
- return CAS::Zero if @x == -@y or -@x == @y
69
- return (@x - @y.x) if @y.is_a? CAS::Invert
70
- return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
71
- return self
72
- end
73
-
74
- # Convert expression to code (internal, for `CAS::Op#to_proc` method)
75
- #
76
- # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
77
- def to_code
78
- "(#{@x.to_code} + #{@y.to_code})"
79
- end
80
- end # Sum
81
- CAS::Sum.init_simplify_dict
82
-
83
4
  # ___ _ __ __
84
5
  # | \(_)/ _|/ _|
85
6
  # | |) | | _| _/
@@ -149,83 +70,6 @@ module CAS
149
70
  end # Difference
150
71
  CAS::Diff.init_simplify_dict
151
72
 
152
- # ___ _
153
- # | _ \_ _ ___ __| |
154
- # | _/ '_/ _ \/ _` |
155
- # |_| |_| \___/\__,_|
156
-
157
- ##
158
- # Product class. Performs the product between two elements.
159
- # This class will be soon modified as an n-ary operator.
160
- class Prod < CAS::BinaryOp
161
- # Performs the product between two `CAS::Op`
162
- #
163
- # ```
164
- # d
165
- # ---- (f(x) * g(x)) = f'(x) * g(x) + f(x) * g'(x)
166
- # dx
167
- # ```
168
- #
169
- # * **argument**: `CAS::Op` argument of derivative
170
- # * **returns**: `CAS::Op` derivative
171
- def diff(v)
172
- left, right = super v
173
- return left * @y if right == CAS::Zero
174
- return right * @x if left == CAS::Zero
175
- (left * @y) + (right * @x)
176
- end
177
-
178
- # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
179
- # or `Float` (depends upon promotions).
180
- # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
181
- # as keys, and a `Numeric` as a value. In this case it will call
182
- # the `Fixnum#overloaded_plus`, that is the old plus function.
183
- #
184
- # * **argument**: `Hash` with feed dictionary
185
- # * **returns**: `Numeric`
186
- def call(f)
187
- CAS::Help.assert(f, Hash)
188
-
189
- return @x.call(f).overloaded_mul(@y.call(f))
190
- end
191
-
192
- # Convert expression to string
193
- #
194
- # * **returns**: `String` to print on screen
195
- def to_s
196
- "(#{@x} * #{@y})"
197
- end
198
-
199
- # Same as `CAS::Op`
200
- #
201
- # Simplifcation engine supports:
202
- #
203
- # * x * 0 = x * y = 0
204
- # * 1 * y = y
205
- # * x * 1 = x
206
- # * x * x = x²
207
- # * a * b = c (constants reduction)
208
- #
209
- # * **returns**: `CAS::Op` simplified version
210
- def simplify
211
- super
212
- return CAS::Zero if (@x == CAS::Zero or @y == CAS::Zero)
213
- return @y if @x == CAS::One
214
- return @x if @y == CAS::One
215
- return @x ** 2.0 if @x == @y
216
- return CAS.const(self.call({})) if @x.is_a? CAS::Constant and @y.is_a? CAS::Constant
217
- return self
218
- end
219
-
220
- # Convert expression to code (internal, for `CAS::Op#to_proc` method)
221
- #
222
- # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
223
- def to_code
224
- "(#{@x.to_code} * #{@y.to_code})"
225
- end
226
- end # Prod
227
- CAS::Prod.init_simplify_dict
228
-
229
73
  # ___
230
74
  # | _ \_____ __ __
231
75
  # | _/ _ \ V V /
@@ -300,7 +144,7 @@ module CAS
300
144
  return self if (@x == CAS::Infinity and @y == CAS::Infinity)
301
145
  return self if (@x == CAS::Infinity and @y == CAS::Zero)
302
146
  return self if (@x == CAS::Zero and @y == CAS::Infinity)
303
-
147
+
304
148
  return CAS::Zero if @x == CAS::Zero
305
149
  return CAS::One if @x == CAS::One
306
150
  return @x if @y == CAS::One
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module CAS
4
+
5
+ # ___ _
6
+ # | _ \_ _ ___ __| |
7
+ # | _/ '_/ _ \/ _` |
8
+ # |_| |_| \___/\__,_|
9
+
10
+ ##
11
+ # Product class. Performs the product between two elements.
12
+ # This class will be soon modified as an n-ary operator.
13
+ class Prod < CAS::NaryOp
14
+ # The new element of a sum accumulates inside the
15
+ # vector that holds the elements
16
+ def *(op)
17
+ CAS::Help.assert(op, CAS::Op)
18
+ @x << op
19
+ self
20
+ end
21
+
22
+ # Performs the product between two `CAS::Op`
23
+ #
24
+ # ```
25
+ # d
26
+ # ---- (f(x) * g(x) * h(x)) = f'(x) * g(x) * h(x) +
27
+ # dx
28
+ # + f(x) * g'(x) * h(x) +
29
+ #
30
+ # + f(x) * g(x) * h'(x)
31
+ # ```
32
+ #
33
+ # * **argument**: `CAS::Op` argument of derivative
34
+ # * **returns**: `CAS::Op` derivative
35
+ def diff(v)
36
+ xdiff = @x.map { |y| y.diff(v) }
37
+
38
+ xdiff.each_with_index { |y, i|
39
+ xdiff[i] = y * CAS::Prod.new(@x[0...i] + @x[(i + 1)..-1])
40
+ }
41
+
42
+ return CAS::Sum.new(xdiff)
43
+ end
44
+
45
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
46
+ # or `Float` (depends upon promotions).
47
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
48
+ # as keys, and a `Numeric` as a value. In this case it will call
49
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
50
+ #
51
+ # * **argument**: `Hash` with feed dictionary
52
+ # * **returns**: `Numeric`
53
+ def call(f)
54
+ CAS::Help.assert(f, Hash)
55
+
56
+ return @x.inject { |p, y| p = p.overloaded_mul(y.call(f)) }
57
+ end
58
+
59
+ # Convert expression to string
60
+ #
61
+ # * **returns**: `String` to print on screen
62
+ def to_s
63
+ "(#{@x.map(&:to_s).join(" * ")})"
64
+ end
65
+
66
+ # Same as `CAS::Op`
67
+ #
68
+ # Simplifcation engine supports:
69
+ #
70
+ # * x * 0 = x * y = 0
71
+ # * 1 * y = y
72
+ # * x * 1 = x
73
+ # * x * x = x²
74
+ # * a * b = c (constants reduction)
75
+ #
76
+ # * **returns**: `CAS::Op` simplified version
77
+ def simplify
78
+ super
79
+ return CAS::Zero if @x.include? CAS::Zero
80
+ @x = @x - [CAS::One]
81
+ return CAS::One if @x.size == 0
82
+ return @x[0] if @x.size == 1
83
+
84
+ @x = self.__reduce_constants(@x) do |cs, xs|
85
+ [cs.inject { |t, c| t *= c.call({}) }] + xs
86
+ end
87
+
88
+ @x = self.__reduce_multeplicity(@x) do |op, count|
89
+ count > 1 ? (op ** count) : op
90
+ end
91
+ return self
92
+ end
93
+
94
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
95
+ #
96
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
97
+ def to_code
98
+ "(#{@x.map(&:to_code).join(" * ")})"
99
+ end
100
+ end # Prod
101
+ CAS::Prod.init_simplify_dict
102
+ end
@@ -0,0 +1,151 @@
1
+ module CAS
2
+ # ___
3
+ # / __|_ _ _ __
4
+ # \__ \ || | ' \
5
+ # |___/\_,_|_|_|_|
6
+
7
+ ##
8
+ # **Sum basic operation**. As for now it is implemented as a simple
9
+ # binary operation. It will be implemented as n-ary op.
10
+ class Sum < CAS::NaryOp
11
+ # Performs the sum between arbitrary number of `CAS::Op`
12
+ #
13
+ # ```
14
+ # d
15
+ # ---- (f(x) + g(x) + h(x)) = f'(x) + g'(x) + h'(x)
16
+ # dx
17
+ # ```
18
+ #
19
+ # * **argument**: `CAS::Op` argument of derivative
20
+ # * **returns**: `CAS::Op` derivative
21
+ def diff(v)
22
+ @x.map { |x| x.diff(v) }.inject { |sum_x, dx| sum_x += dx }
23
+ end
24
+
25
+ # The added element of a sum accumulates inside the
26
+ # vector that holds the elements
27
+ def +(op)
28
+ CAS::Help.assert(op, CAS::Op)
29
+ @x << op
30
+ self
31
+ end
32
+
33
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
34
+ # or `Float` (depends upon promotions).
35
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
36
+ # as keys, and a `Numeric` as a value. In this case it will call
37
+ # the `Fixnum#overloaded_plus`, that is the old plus function.
38
+ #
39
+ # * **argument**: `Hash` with feed dictionary
40
+ # * **returns**: `Numeric`
41
+ def call(f)
42
+ CAS::Help.assert(f, Hash)
43
+ return @x.inject { |val, x_i| val += x_i.call(f) }
44
+ end
45
+
46
+ # Convert expression to string
47
+ #
48
+ # * **returns**: `String` to print on screen
49
+ def to_s
50
+ "(#{@x.map(&:to_s).join(" + ")})"
51
+ end
52
+
53
+ # Same as `CAS::Op`
54
+ #
55
+ # Simplifcation engine supports:
56
+ #
57
+ # * x + 0 = x
58
+ # * 0 + y = y
59
+ # * x + x = 2 x
60
+ # * x + (-x) = 0
61
+ # * x + (-y) = x - y
62
+ # * 1 + 2 = 3 (constants reduction)
63
+ #
64
+ # * **returns**: `CAS::Op` simplified version
65
+ def simplify
66
+ super
67
+ return @x[0] if @x.size == 1
68
+
69
+ # return CAS::Zero if @x == -@y or -@x == @y
70
+ # return (@x - @y.x) if @y.is_a? CAS::Invert
71
+ # return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
72
+ # Removing Zeros
73
+ @x = @x - [CAS::Zero]
74
+ return CAS::Zero if @x.size == 0
75
+ # Reduce constants
76
+ @x = self.__reduce_constants(@x) do |cs, xs|
77
+ xs + [cs.inject { |t, c| t += c.call({}) }]
78
+ end
79
+ # Multeplicity and associativity executed
80
+ return self.reduce_associativity
81
+ end
82
+
83
+ # Reduces from an associative point of view, by a segregation
84
+ # of "negative" and positive elements. Negatives comes from
85
+ # Diff operations and Invert operations. All the others are considered
86
+ # positive. This function implements an internal heuristic. Should
87
+ # not be used outside
88
+ #
89
+ # * **returns**: A `CAS::Diff` or a `CAS::Sum`
90
+ def reduce_associativity
91
+ pos, neg = [], []
92
+
93
+ @x.each do |x_el|
94
+ case x_el
95
+ when CAS::Invert
96
+ neg << x_el.x
97
+ when CAS::Diff
98
+ pos << x_el.x
99
+ neg << x_el.y
100
+ else
101
+ pos << x_el
102
+ end
103
+ end
104
+
105
+ pos, neg = self.reduce_associativity_array pos, neg
106
+ pos = self.__reduce_multeplicity(pos)
107
+ neg = self.__reduce_multeplicity neg
108
+
109
+ # TODO : Add rules for simplifications
110
+ left, right = nil, nil
111
+ left = CAS::Sum.new(pos) if pos.size > 1
112
+ left = pos[0] if pos.size == 1
113
+ right = CAS::Sum.new(neg) if neg.size > 1
114
+ right = neg[0] if neg.size == 1
115
+
116
+ return CAS::Zero unless left || right
117
+ return left unless right
118
+ return -right unless left
119
+ return left - right
120
+ end
121
+
122
+ # Reduce the positive and the negative associative part of
123
+ # the sum to perform the symbolic difference. Does not take into account
124
+ # multeplicity
125
+ #
126
+ # * **requires**: positive `Array`
127
+ # * **requires**: negative `Array`
128
+ # * **returns**: positive, reduced `Array` and negative `Array`
129
+ def reduce_associativity_array(p_old, n_old)
130
+ p_del, n_del = [], []
131
+ p_old.each do |p|
132
+ n_old.each do |n|
133
+ if p == n
134
+ p_del << p
135
+ n_del << n
136
+ end
137
+ end
138
+ end
139
+
140
+ return (p_old - p_del), (n_old - n_del)
141
+ end
142
+
143
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
144
+ #
145
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
146
+ def to_code
147
+ "(#{@x.map(&:to_code).join(" + ")})"
148
+ end
149
+ end # Sum
150
+ CAS::Sum.init_simplify_dict
151
+ end