ragni-cas 0.2.2 → 0.2.3

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.
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module CAS
4
+ # ___ _
5
+ # | __|_ ___ __ ___ _ _ ___ _ _| |_
6
+ # | _|\ \ / '_ \/ _ \ ' \/ -_) ' \ _|
7
+ # |___/_\_\ .__/\___/_||_\___|_||_\__|
8
+ # |_|
9
+
10
+ ##
11
+ # Representation for the `e^x` function. It is implemented
12
+ # as a `CAS::Op`
13
+ class Exp < CAS::Op
14
+ # Return the derivative of the `sin(x)` function using the chain
15
+ # rule. The input is a `CAS::Op` because it can handle derivatives
16
+ # with respect to functions.
17
+ #
18
+ # ```
19
+ # d
20
+ # -- exp(f(x)) = f'(x) exp(f(x))
21
+ # dx
22
+ # ```
23
+ #
24
+ # * **argument**: `CAS::Op` object of the derivative
25
+ # * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
26
+ def diff(v)
27
+ if @x.depend? v
28
+ return @x.diff(v) * CAS.exp(@x)
29
+ else
30
+ return CAS::Zero
31
+ end
32
+ end
33
+
34
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
35
+ # or `Float` (depends upon promotions).
36
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
37
+ # as keys, and a `Numeric` as a value
38
+ #
39
+ # * **argument**: `Hash` with feed dictionary
40
+ # * **returns**: `Numeric`
41
+ def call(f)
42
+ CAS::Help.assert(f, Hash)
43
+ Math::exp(@x.call(f))
44
+ end
45
+
46
+ # Convert expression to string
47
+ #
48
+ # * **returns**: `String` to print on screen
49
+ def to_s
50
+ "exp(#{@x})"
51
+ end
52
+
53
+ # Simplification callback. It simplify the subgraph of each node
54
+ # until all possible simplification are performed (thus the execution
55
+ # time is not deterministic).
56
+ #
57
+ # * **returns**: `CAS::Op` simplified version
58
+ def simplify
59
+ super
60
+ return @x.x if @x.is_a? CAS::Ln
61
+ return self.simplify_dictionary
62
+ end
63
+
64
+ def self.init_simplify_dict
65
+ @simplify_dict = {
66
+ CAS::Zero => CAS::One,
67
+ CAS::One => CAS::E,
68
+ CAS::Infinity => CAS::Infinity
69
+ }
70
+ end
71
+
72
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
73
+ #
74
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
75
+ def to_code
76
+ "Math::exp(#{@x.to_code})"
77
+ end
78
+
79
+ # Returns the latex representation of the current Op.
80
+ #
81
+ # * **returns**: `String`
82
+ def to_latex
83
+ "e^{#{@x.to_latex}}"
84
+ end
85
+ end # Exp
86
+
87
+ # Shortcut for `CAS::Exp#new`
88
+ #
89
+ # * **argument**: `CAS::Op` argument of the function
90
+ # * **returns**: `CAS::Exp` operation
91
+ def self.exp(x)
92
+ CAS::Exp.new x
93
+ end
94
+
95
+ # _ _ _ _
96
+ # | | ___ __ _ __ _ _ _(_) |_| |_ _ __
97
+ # | |__/ _ \/ _` / _` | '_| | _| ' \| ' \
98
+ # |____\___/\__, \__,_|_| |_|\__|_||_|_|_|_|
99
+ # |___/
100
+
101
+ ##
102
+ # Representation for the `log(x)` function. It is implemented
103
+ # as a `CAS::Op`
104
+ class Ln < CAS::Op
105
+ # Return the derivative of the `log(x)` function using the chain
106
+ # rule. The input is a `CAS::Op` because it can handle derivatives
107
+ # with respect to functions.
108
+ #
109
+ # ```
110
+ # d f'(x)
111
+ # -- log(f(x)) = -------
112
+ # dx f(x)
113
+ # ```
114
+ #
115
+ # * **argument**: `CAS::Op` object of the derivative
116
+ # * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
117
+ def diff(v)
118
+ if @x.depend? v
119
+ return CAS::One / @x
120
+ else
121
+ return CAS::Zero
122
+ end
123
+ end
124
+
125
+ # Call resolves the operation tree in a `Numeric` (if `Fixnum`)
126
+ # or `Float` (depends upon promotions).
127
+ # As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
128
+ # as keys, and a `Numeric` as a value
129
+ #
130
+ # * **argument**: `Hash` with feed dictionary
131
+ # * **returns**: `Numeric`
132
+ def call(f)
133
+ # I'm leaving to Math the honor
134
+ # of handling negative values...
135
+ CAS::Help.assert(f, Hash)
136
+ Math::log(@x.call(f))
137
+ end
138
+
139
+ # Convert expression to string
140
+ #
141
+ # * **returns**: `String` to print on screen
142
+ def to_s
143
+ "log(#{@x})"
144
+ end
145
+
146
+ # Simplification callback. It simplify the subgraph of each node
147
+ # until all possible simplification are performed (thus the execution
148
+ # time is not deterministic).
149
+ #
150
+ # * **returns**: `CAS::Op` simplified version
151
+ def simplify
152
+ super
153
+ return @x.x if @x.is_a? CAS::Exp
154
+ return self.simplify_dictionary
155
+ end
156
+
157
+ def self.init_simplify_dict
158
+ @simplify_dict = {
159
+ CAS::Zero => CAS.invert(CAS::Infinity),
160
+ CAS::One => CAS::Zero,
161
+ CAS::E => CAS::One
162
+ }
163
+ end
164
+
165
+ # Convert expression to code (internal, for `CAS::Op#to_proc` method)
166
+ #
167
+ # * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
168
+ def to_code
169
+ "Math::log(#{@x.to_code})"
170
+ end
171
+
172
+ # Returns the latex representation of the current Op.
173
+ #
174
+ # * **returns**: `String`
175
+ def to_latex
176
+ "\\log\\left( #{@x.to_latex} \\right)"
177
+ end
178
+ end # Ln
179
+ CAS::Ln.init_simplify_dict
180
+
181
+ # Shortcut for `CAS::Ln#new`
182
+ #
183
+ # * **argument**: `CAS::Op` argument of the function
184
+ # * **returns**: `CAS::Ln` operation
185
+ def self.ln(x)
186
+ CAS::Ln.new x
187
+ end
188
+
189
+ # Shortcut for `CAS::Ln#new`
190
+ #
191
+ # * **argument**: `CAS::Op` argument of the function
192
+ # * **returns**: `CAS::Ln` operation
193
+ def self.log(x)
194
+ CAS::Ln.new x
195
+ end
196
+ end
@@ -0,0 +1,358 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module CAS
4
+ # ___ _ _
5
+ # / __|___ _ _ __| |_ __ _ _ _| |_
6
+ # | (__/ _ \ ' \(_-< _/ _` | ' \ _|
7
+ # \___\___/_||_/__/\__\__,_|_||_\__|
8
+
9
+ ##
10
+ # Constant is a `CAS::Op` container for a `Numeric` value, that is
11
+ # not a `CAS::Variable`, thus its derivative it is always zero
12
+ class Constant < CAS::Op
13
+ def initialize(x)
14
+ @x = x
15
+ end
16
+
17
+ # Evaluates the derivative of a constant. The derivative is
18
+ # always a `CAS::Zero`
19
+ #
20
+ # ```
21
+ # d
22
+ # -- c = 0
23
+ # dx
24
+ # ```
25
+ def diff(_v)
26
+ CAS::Zero
27
+ end
28
+
29
+ # Calling a constant will return the value of the constant
30
+ # itself.
31
+ #
32
+ # * **argument**: Unused argument
33
+ # * **returns**: `Numeric` value of the constant
34
+ def call(_f)
35
+ @x
36
+ end
37
+
38
+ # There is no dependency in a constant, thus this method will
39
+ # always return false
40
+ #
41
+ # * **argument**: Unused argument
42
+ # * **returns**: `FalseClass`
43
+ def depend?(_v)
44
+ false
45
+ end
46
+
47
+ # The string representation of a constant is the value
48
+ # of the constant
49
+ #
50
+ # * **returns**: `String`
51
+ def to_s
52
+ "#{@x}"
53
+ end
54
+
55
+ # Subs for a constant is a dummy method that returns always `self`
56
+ #
57
+ # * **argument**: Unused argument
58
+ # * **returns**: `CAS::Constant` that represent `self`
59
+ def subs(_dt)
60
+ return self
61
+ end
62
+
63
+ # Simplification callback. It simplify the subgraph of each node
64
+ # until all possible simplification are performed (thus the execution
65
+ # time is not deterministic).
66
+ #
67
+ # * **returns**: `CAS::Op` simplified version
68
+ def simplify
69
+ return self
70
+ end
71
+ @@simplify_dict = { }
72
+
73
+ # Args of a constant is an empty `Array`
74
+ #
75
+ # * **returns**: `Array` empty
76
+ def args
77
+ []
78
+ end
79
+
80
+ # Check if a constant is equal to another `CAS::Op` object
81
+ #
82
+ # * **argument**: `CAs::Op`
83
+ # * **returns**: `TrueClass` or `FalseClass`
84
+ def ==(op)
85
+ if op.is_a? CAS::Constant
86
+ return @x == op.x
87
+ else
88
+ return false
89
+ end
90
+ end
91
+
92
+ # Inspection for `CAS::Constant` class
93
+ #
94
+ # * **returns**: `String`
95
+ def inspect
96
+ "Const(#{self})"
97
+ end
98
+
99
+ # Return the local Graphviz node of the tree
100
+ #
101
+ # * **returns**: `String` of local Graphiz node
102
+ def dot_graph
103
+ n = "#{self.class.to_s.gsub("CAS::", "")}_#{self.object_id}"
104
+ "#{n};\n #{n} [label=\"#{@x}\"];"
105
+ end
106
+ end
107
+
108
+ # Allows to define a series of new constants.
109
+ #
110
+ # ``` ruby
111
+ # a, b = CAS::const 1.0, 100
112
+ # ```
113
+ #
114
+ # * **argument**: `Array` of Numeric
115
+ # * **returns**: `Array` of `CAS::Contant`
116
+ def self.const(*val)
117
+ #(val = [val]) if val.size == 1
118
+ ret = []
119
+ val.each do |n|
120
+ ret << (NumericToConst[n] ? NumericToConst[n] : CAS::Constant.new(n))
121
+ end
122
+ return (ret.size == 1 ? ret[0] : ret)
123
+ end
124
+
125
+ # _______ ___ ___
126
+ # |_ / __| _ \/ _ \
127
+ # / /| _|| / (_) |
128
+ # /___|___|_|_\\___/
129
+
130
+ ##
131
+ # Class that represents the constant Zero (0)
132
+ class ZERO_CONSTANT < CAS::Constant
133
+ # Initializer for the zero constant
134
+ #
135
+ # * **returns**: `CAS::ZERO_CONSTANT` new instance
136
+ def initialize
137
+ @x = 0.0
138
+ end
139
+
140
+ # String representation for the constant
141
+ #
142
+ # * **returns**: `String`
143
+ def to_s
144
+ "0"
145
+ end
146
+ end # Zero
147
+
148
+ # Zero (0) constant representation
149
+ Zero = CAS::ZERO_CONSTANT.new
150
+
151
+ # ___
152
+ # / _ \ _ _ ___
153
+ # | (_) | ' \/ -_)
154
+ # \___/|_||_\___|
155
+
156
+ ##
157
+ # Class that represents the constant One (1)
158
+ class ONE_CONSTANT < CAS::Constant
159
+ # Initializer for the one constant
160
+ #
161
+ # * **returns**: `CAS::ONE_CONSTANT` new instance
162
+ def initialize
163
+ @x = 1.0
164
+ end
165
+
166
+ # String representation for the constant
167
+ #
168
+ # * **returns**: `String`
169
+ def to_s
170
+ "1"
171
+ end
172
+ end # Zero
173
+
174
+ # One (1) constant representation
175
+ One = CAS::ONE_CONSTANT.new
176
+
177
+ # _____
178
+ # |_ _|_ __ _____
179
+ # | | \ V V / _ \
180
+ # |_| \_/\_/\___/
181
+
182
+ ##
183
+ # Class that represents the constant Two (2)
184
+ class TWO_CONSTANT < CAS::Constant
185
+ # Initializer for the two constant
186
+ #
187
+ # * **returns**: `CAS::TWO_CONSTANT` new instance
188
+ def initialize
189
+ @x = 2.0
190
+ end
191
+
192
+ # String representation for the constant
193
+ #
194
+ # * **returns**: `String`
195
+ def to_s
196
+ "2"
197
+ end
198
+ end # Zero
199
+
200
+ # Two (2) constant representation
201
+ Two = CAS::TWO_CONSTANT.new
202
+
203
+ # ___ ___
204
+ # | _ \_ _|
205
+ # | _/| |
206
+ # |_| |___|
207
+
208
+ ##
209
+ # Class that represents the constant Pi (π)
210
+ class PI_CONSTANT < CAS::Constant
211
+ # Initializer for the pi constant
212
+ #
213
+ # * **returns**: `CAS::PI_CONSTANT` new instance
214
+ def initialize
215
+ @x = Math::PI
216
+ end
217
+
218
+ # String representation for the constant
219
+ #
220
+ # * **returns**: `String`
221
+ def to_s
222
+ "π"
223
+ end
224
+ end
225
+
226
+ # Pi (3.14...) constant representation
227
+ Pi = CAS::PI_CONSTANT.new
228
+
229
+ # ___
230
+ # | __|
231
+ # | _|
232
+ # |___|
233
+
234
+ ##
235
+ # Class that represents the constant E (e)
236
+ class E_CONSTANT < CAS::Constant
237
+ # Initializer for the E constant
238
+ #
239
+ # * **returns**: `CAS::E_CONSTANT` new instance
240
+ def initialize
241
+ @x = Math::E
242
+ end
243
+
244
+ # String representation for the constant
245
+ #
246
+ # * **returns**: `String`
247
+ def to_s
248
+ "e"
249
+ end
250
+ end
251
+
252
+ # E (2.57...) constant representation
253
+ E = CAS::E_CONSTANT.new
254
+
255
+ # ___ __ _ _ _
256
+ # |_ _|_ _ / _(_)_ _ (_) |_ _ _
257
+ # | || ' \| _| | ' \| | _| || |
258
+ # |___|_||_|_| |_|_||_|_|\__|\_, |
259
+ # |__/
260
+
261
+ ##
262
+ # Class that represents the constant Infinity (∞)
263
+ class INFINITY_CONSTANT < CAS::Constant
264
+ # Initializer for the infinity constant
265
+ #
266
+ # * **returns**: `CAS::INFINITY_CONSTANT` new instance
267
+ def initialize
268
+ @x = (1.0/0.0)
269
+ end
270
+
271
+ # String representation for the constant
272
+ #
273
+ # * **returns**: `String`
274
+ def to_s
275
+ "∞"
276
+ end
277
+ end
278
+
279
+ # Infinity constant representation
280
+ Infinity = CAS::INFINITY_CONSTANT.new
281
+
282
+ # _ _ ___ __ _ _ _
283
+ # | \| |___ __ _|_ _|_ _ / _(_)_ _ (_) |_ _ _
284
+ # | .` / -_) _` || || ' \| _| | ' \| | _| || |
285
+ # |_|\_\___\__, |___|_||_|_| |_|_||_|_|\__|\_, |
286
+ # |___/ |__/
287
+
288
+ ##
289
+ # Class that represents the constant Negative Infinity (-∞)
290
+ class NEG_INFINITY_CONSTANT < CAS::Constant
291
+ # Initializer for the negative infinity constant
292
+ #
293
+ # * **returns**: `CAS::NEG_INFINITY_CONSTANT` new instance
294
+ def initialize
295
+ @x = -(1.0/0.0)
296
+ end
297
+
298
+ # String representation for the constant
299
+ #
300
+ # * **returns**: `String`
301
+ def to_s
302
+ "-∞"
303
+ end
304
+ end
305
+
306
+ # Negative Infinity constant representation
307
+ NegInfinity = CAS::NEG_INFINITY_CONSTANT.new
308
+
309
+ # _
310
+ # ___/ |
311
+ # |___| |
312
+ # |_|
313
+
314
+ ##
315
+ # Class that represents the constant Minus One (-1)
316
+ class MINUS_ONE_CONSTANT < CAS::Constant
317
+ # Initializer for the minus one constant
318
+ #
319
+ # * **returns**: `CAS::MINUS_ONE_CONSTANT` new instance
320
+ def initialize
321
+ @x = -1.0
322
+ end
323
+
324
+ # String representation for the constant
325
+ #
326
+ # * **returns**: `String`
327
+ def to_s
328
+ "-1"
329
+ end
330
+ end
331
+
332
+ # Minus One (-1) constant representation
333
+ MinusOne = CAS::MINUS_ONE_CONSTANT.new
334
+
335
+ # Series of useful numeric constant, Based upon
336
+ # `Numeric` keys, with `CAs::Constant` value
337
+ NumericToConst = {
338
+ 0 => CAS::Zero,
339
+ 0.0 => CAS::Zero,
340
+ 1 => CAS::One,
341
+ 1.0 => CAS::One,
342
+ 2 => CAS::Two,
343
+ 2.0 => CAS::Two,
344
+ Math::PI => CAS::Pi,
345
+ Math::E => CAS::E,
346
+ (1.0/0.0) => CAS::Infinity,
347
+ }
348
+
349
+ class Constant
350
+ @@simplify_dict = {
351
+ 0 => CAS::Zero,
352
+ 1 => CAS::One,
353
+ Math::PI => CAS::Pi,
354
+ Math::E => CAS::E,
355
+ (1.0/0.0) => CAS::Infinity
356
+ }
357
+ end
358
+ end