symath 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +616 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/symath/definition/abs.rb +48 -0
- data/lib/symath/definition/arccos.rb +25 -0
- data/lib/symath/definition/arccot.rb +23 -0
- data/lib/symath/definition/arccsc.rb +24 -0
- data/lib/symath/definition/arcsec.rb +24 -0
- data/lib/symath/definition/arcsin.rb +25 -0
- data/lib/symath/definition/arctan.rb +23 -0
- data/lib/symath/definition/bounds.rb +39 -0
- data/lib/symath/definition/codiff.rb +31 -0
- data/lib/symath/definition/constant.rb +111 -0
- data/lib/symath/definition/cos.rb +17 -0
- data/lib/symath/definition/cot.rb +17 -0
- data/lib/symath/definition/csc.rb +17 -0
- data/lib/symath/definition/curl.rb +27 -0
- data/lib/symath/definition/d.rb +62 -0
- data/lib/symath/definition/div.rb +27 -0
- data/lib/symath/definition/exp.rb +112 -0
- data/lib/symath/definition/fact.rb +55 -0
- data/lib/symath/definition/flat.rb +31 -0
- data/lib/symath/definition/function.rb +197 -0
- data/lib/symath/definition/grad.rb +23 -0
- data/lib/symath/definition/hodge.rb +23 -0
- data/lib/symath/definition/int.rb +75 -0
- data/lib/symath/definition/laplacian.rb +23 -0
- data/lib/symath/definition/lmd.rb +97 -0
- data/lib/symath/definition/ln.rb +45 -0
- data/lib/symath/definition/number.rb +51 -0
- data/lib/symath/definition/operator.rb +228 -0
- data/lib/symath/definition/sec.rb +17 -0
- data/lib/symath/definition/sharp.rb +31 -0
- data/lib/symath/definition/sin.rb +17 -0
- data/lib/symath/definition/sqrt.rb +62 -0
- data/lib/symath/definition/tan.rb +17 -0
- data/lib/symath/definition/trig.rb +95 -0
- data/lib/symath/definition/variable.rb +284 -0
- data/lib/symath/definition/xd.rb +28 -0
- data/lib/symath/definition.rb +205 -0
- data/lib/symath/equation.rb +67 -0
- data/lib/symath/fraction.rb +177 -0
- data/lib/symath/matrix.rb +252 -0
- data/lib/symath/minus.rb +125 -0
- data/lib/symath/operation/differential.rb +167 -0
- data/lib/symath/operation/distributivelaw.rb +367 -0
- data/lib/symath/operation/exterior.rb +64 -0
- data/lib/symath/operation/integration.rb +329 -0
- data/lib/symath/operation/match.rb +166 -0
- data/lib/symath/operation/normalization.rb +458 -0
- data/lib/symath/operation.rb +36 -0
- data/lib/symath/operator.rb +163 -0
- data/lib/symath/parser.rb +473 -0
- data/lib/symath/parser.y +129 -0
- data/lib/symath/poly/dup.rb +835 -0
- data/lib/symath/poly/galois.rb +621 -0
- data/lib/symath/poly.rb +142 -0
- data/lib/symath/power.rb +224 -0
- data/lib/symath/product.rb +183 -0
- data/lib/symath/sum.rb +174 -0
- data/lib/symath/type.rb +282 -0
- data/lib/symath/value.rb +372 -0
- data/lib/symath/version.rb +3 -0
- data/lib/symath/wedge.rb +48 -0
- data/lib/symath.rb +157 -0
- data/symath.gemspec +39 -0
- metadata +160 -0
data/README.md
ADDED
@@ -0,0 +1,616 @@
|
|
1
|
+
# SyMath
|
2
|
+
|
3
|
+
Rudimentary symbolic math library for Ruby. This gem is mainly intended
|
4
|
+
as a coding excercise. The operations have not been optimized for speed.
|
5
|
+
The current state of the project is 'under construction'.
|
6
|
+
|
7
|
+
# Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```
|
12
|
+
gem 'symath'
|
13
|
+
```
|
14
|
+
|
15
|
+
Then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install symath
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Using the library:
|
26
|
+
|
27
|
+
<pre>
|
28
|
+
> require 'SyMath'
|
29
|
+
</pre>
|
30
|
+
|
31
|
+
### Simple introduction
|
32
|
+
|
33
|
+
A convenient way to explore the SyMath library is using the interactive
|
34
|
+
Ruby interpreter, irb:
|
35
|
+
|
36
|
+
<pre>
|
37
|
+
> # Load the symath library
|
38
|
+
> require 'symath'
|
39
|
+
=> false
|
40
|
+
> # Add the symbols module to your environment
|
41
|
+
> extend SyMath::Definitions
|
42
|
+
=> main
|
43
|
+
</pre>
|
44
|
+
|
45
|
+
You can now say, for example:
|
46
|
+
|
47
|
+
<pre>
|
48
|
+
> # Simplify an expression
|
49
|
+
> sin(:x) + 2*sin(:x)
|
50
|
+
=> 3*sin(:x)
|
51
|
+
> # Derivative of tan(2*y + 3)
|
52
|
+
> (d(tan(2*:y + 3))/d(:y)).evaluate
|
53
|
+
=> 2*(tan(2*y + 3)**2 + 1)
|
54
|
+
</pre>
|
55
|
+
|
56
|
+
Ruby symbols, :x and :y in the above example, are converted into
|
57
|
+
symbolic math variables and Ruby numbers are converted into symbolic
|
58
|
+
math numbers. Functions, operators and constants (e, pi, i, etc.) are
|
59
|
+
available as methods through the SyMath::Definitions module. In some cases
|
60
|
+
it is necessary to tell Ruby that your number or symbol is to be
|
61
|
+
understood as a symbolic object, and not just a Ruby number or
|
62
|
+
symbol. Use the to_m method to explicitly convert them to symbolic
|
63
|
+
bjects:
|
64
|
+
|
65
|
+
<pre>
|
66
|
+
> # Ruby integer math
|
67
|
+
> 3**4
|
68
|
+
=> 81
|
69
|
+
> # SyMath symbolic math
|
70
|
+
> 3.to_m**4
|
71
|
+
=> 3**4
|
72
|
+
> (3.to_m**4).normalize
|
73
|
+
=> 81
|
74
|
+
</pre>
|
75
|
+
|
76
|
+
An complete expression can also be converted from a string, using the
|
77
|
+
same to_m method:
|
78
|
+
|
79
|
+
<pre>
|
80
|
+
> 'ln(e) + sin(pi/2)'.to_m
|
81
|
+
=> ln(e) + sin(pi/2)
|
82
|
+
> 'ln(e) + sin(pi/2)'.to_m.normalize
|
83
|
+
=> 2
|
84
|
+
</pre>
|
85
|
+
|
86
|
+
### The SyMath::Definitions module
|
87
|
+
|
88
|
+
The module SyMath::Definitions is available to be included or extended
|
89
|
+
to your class or code block. It gives a Ruby method for each operator,
|
90
|
+
function and constant that exists, so they can be referred to by their
|
91
|
+
name, as in the code examples above. If you don't want to use the
|
92
|
+
module, functions, operators and constants must be referred to by the
|
93
|
+
fn, op and definition methods:
|
94
|
+
|
95
|
+
<pre>
|
96
|
+
> # Using the SyMath::Definitions methods
|
97
|
+
> sin(:x)
|
98
|
+
=> sin(x)
|
99
|
+
> int(:x)
|
100
|
+
=> int(x)
|
101
|
+
> e
|
102
|
+
=> e
|
103
|
+
> sin
|
104
|
+
=> sin(...)
|
105
|
+
> # Using the generic creator functions
|
106
|
+
> fn(:sin, :x)
|
107
|
+
=> sin(x)
|
108
|
+
> op(:int, :x)
|
109
|
+
=> int(x)
|
110
|
+
> definition(:e)
|
111
|
+
=> e
|
112
|
+
> definition(:sin)
|
113
|
+
=> sin(...)
|
114
|
+
</pre>
|
115
|
+
|
116
|
+
The SyMath::Definitions module is updated dynamically after the user has
|
117
|
+
defined new functions, operators and constants.
|
118
|
+
|
119
|
+
### String representaton of symbolic objects
|
120
|
+
|
121
|
+
Symbolic math objects, inheriting from SyMath::Value, all have a to_s
|
122
|
+
method which returns a string representation of the object. The string
|
123
|
+
representation is compatible with the String.to_m method which
|
124
|
+
converts a string representation into a symbolic object:
|
125
|
+
|
126
|
+
<pre>
|
127
|
+
> (ln(e) + sin(pi/2)).to_s
|
128
|
+
=> "ln(e) + sin(pi/2)"
|
129
|
+
> 'ln(e) + sin(pi/2)'.to_m
|
130
|
+
=> ln(e) + sin(pi/2)
|
131
|
+
</pre>
|
132
|
+
|
133
|
+
SyMath::Value overrides the Object.inspect method, returning the to_s
|
134
|
+
representation rather than the more verbose and less readable
|
135
|
+
Object.inspect output. This behaviour can be disabled with the setting
|
136
|
+
'inspect_to_s':
|
137
|
+
|
138
|
+
<pre>
|
139
|
+
> SyMath.setting(:inspect_to_s, false)
|
140
|
+
=> false
|
141
|
+
> ln(e) + sin(pi/2)
|
142
|
+
=> "#<SyMath::Sum:0x000055e8a1d93b38 @definition=..."
|
143
|
+
</pre>
|
144
|
+
|
145
|
+
### Simplification and normalizing
|
146
|
+
|
147
|
+
Simple reduction rules are automatically applied when composing an
|
148
|
+
expression. These can be disabled with the setting
|
149
|
+
'compose_with_simplify'. More thorough reductions are done by the use
|
150
|
+
of the normalize method.
|
151
|
+
|
152
|
+
<pre>
|
153
|
+
> e*e*e*e
|
154
|
+
=> e**4
|
155
|
+
> SyMath.setting(:compose_with_simplify, false)
|
156
|
+
=> false
|
157
|
+
> e*e*e*e
|
158
|
+
=> e*e*e*e
|
159
|
+
> sin(pi/2).normalize
|
160
|
+
=> 1
|
161
|
+
</pre>
|
162
|
+
|
163
|
+
### Functions
|
164
|
+
|
165
|
+
The library comes with a number of built-in function, which the system
|
166
|
+
knows how to derivate and integrate over. The built-in functions also
|
167
|
+
have a number of reduction rules which are applied by the reduce
|
168
|
+
method and also as part of the 'normalize' method. A list of the
|
169
|
+
defined functions is returned by the functions method. The description
|
170
|
+
method gives a small description of the function:
|
171
|
+
|
172
|
+
<pre>
|
173
|
+
> SyMath::Definition::Function.functions
|
174
|
+
=> [sqrt, sin, cos, tan, sec, csc, cot, arcsin, arccos, arctan,
|
175
|
+
arcsec, arccsc, arccot, ln, exp, abs, fact, sinh, cosh, tanh,
|
176
|
+
coth, sech, csch, arsinh, arcosh, artanh, arcoth, arsech,
|
177
|
+
arcsch]
|
178
|
+
> sin.description
|
179
|
+
=> "sin(x) - trigonometric sine"
|
180
|
+
</pre>
|
181
|
+
|
182
|
+
### Defining functions
|
183
|
+
|
184
|
+
User-defined functions can be added by the method define_fn:
|
185
|
+
|
186
|
+
<pre>
|
187
|
+
> define_fn('poly', [:x, :y], :x**3 + :y**2 + 1)
|
188
|
+
=> poly
|
189
|
+
</pre>
|
190
|
+
|
191
|
+
The user-defined function will now be available as a method in the
|
192
|
+
SyMath::Definitions module and can be used in expressions, just as the
|
193
|
+
built in functions. Functions defined by an expression can be
|
194
|
+
evaluated by the evaluate method, which returns the expression with
|
195
|
+
each free variable replaced with the input arguments to the function:
|
196
|
+
|
197
|
+
<pre>
|
198
|
+
> poly(2, 3).evaluate
|
199
|
+
=> 2**3 + 3**2 + 1
|
200
|
+
> poly(3).evaluate.normalize
|
201
|
+
=> 18
|
202
|
+
</pre>
|
203
|
+
|
204
|
+
### Lambda functions
|
205
|
+
|
206
|
+
A nameless user-defined function can be created using the lmd
|
207
|
+
method. The method returns a function object which does not have a
|
208
|
+
name, but otherwise works as a function. The lambda function has
|
209
|
+
important usages in operators. Since they eturn a function as the
|
210
|
+
result, it will typically be a lambda function. Also, the lambda
|
211
|
+
function can be used for wrapping an expression into a function before
|
212
|
+
doing an integral or derivative, in this way telling which variables
|
213
|
+
the operator should work on. The lambda function can be called using
|
214
|
+
the call method or the Ruby 'call' operator '()':
|
215
|
+
|
216
|
+
<pre>
|
217
|
+
> l = lmd(:x**3 + :y**2 + 1, :x, :y)
|
218
|
+
> l.(2, 3)
|
219
|
+
=> (x**3 + y**2 + 1).(2,3)
|
220
|
+
> l.(2, 3).evaluate
|
221
|
+
=> 2**3 + 3**2 + 1
|
222
|
+
> l.(2, 3).evaluate.normalize
|
223
|
+
=> 18
|
224
|
+
</pre>
|
225
|
+
|
226
|
+
### Operators
|
227
|
+
|
228
|
+
The library has some built-in operators, i.e. functions which take
|
229
|
+
functions as arguments and return functions. A list of the defined
|
230
|
+
operators is returned by the operators method. The description method
|
231
|
+
gives a small description of the operator:
|
232
|
+
|
233
|
+
<pre>
|
234
|
+
> SyMath::Definition::Operator.operators
|
235
|
+
=> [d(...), xd(...), int(...), [f](b,), #(), b(), hodge(...), grad(f),
|
236
|
+
curl(f), div(f), laplacian(f), codiff(f), laplace(f), fourier(f),
|
237
|
+
invfourier(f), dpart(f,t)]
|
238
|
+
> codiff.description
|
239
|
+
=> "codiff(f) - codifferential of function f"
|
240
|
+
</pre>
|
241
|
+
|
242
|
+
### Defining operators
|
243
|
+
|
244
|
+
User-defined operators can be added by the method define_op:
|
245
|
+
|
246
|
+
<pre>
|
247
|
+
> define_op('d2', [:f, :x], d(d(:f)/d(:x))/d(:x))
|
248
|
+
=> d2
|
249
|
+
> d2(:x**3 + 2, :x).evaluate
|
250
|
+
=> 6*x
|
251
|
+
</pre>
|
252
|
+
|
253
|
+
The user-defined function will now be available as a method in the
|
254
|
+
SyMath::Definitions module and can be used in expressions.
|
255
|
+
|
256
|
+
### Evaluating functions and operators
|
257
|
+
|
258
|
+
Evaluating a functions or operators which is defined by an expression
|
259
|
+
returns the expression with each free variable replaced with input
|
260
|
+
arguments. Functions which do not have an expression will typically
|
261
|
+
evaluate to itself (no reduction). Most operators which do not have an
|
262
|
+
expression has a built in evaluation, and returns a function or
|
263
|
+
expression according to the operator.
|
264
|
+
|
265
|
+
### Derivative
|
266
|
+
|
267
|
+
The d-operator returns the differential of a function or expresson. If
|
268
|
+
a function is given, the differential is made over all the free
|
269
|
+
variables of the function. If an expression is given, the operator
|
270
|
+
differentiates over the first free variable found in the
|
271
|
+
expression. Wrapping the expression into a lambda function makes it
|
272
|
+
possible to say which variables to differentiate over. Note that the
|
273
|
+
differential is an operator, so it returns the result in a a lambda
|
274
|
+
function, and not just the expression.
|
275
|
+
|
276
|
+
<pre>
|
277
|
+
> d(sin(:x)).evaluate
|
278
|
+
=> cos(x)*dx.(x)
|
279
|
+
> d(:x**2 + :y**3 + 1).evaluate.normalize
|
280
|
+
=> (2*x*dx).(x)
|
281
|
+
> d(lmd(:x**2 + :y**3 + 1, :y)).evaluate.normalize
|
282
|
+
=> (3*y**2*dy).(y)
|
283
|
+
> d(lmd(:x**2 + :y**3 + 1, :x, :y)).evaluate.normalize
|
284
|
+
=> (3*y**2*dy + 2*x*dx).(x,y)
|
285
|
+
</pre>
|
286
|
+
|
287
|
+
As a special case, the notatonal form d(f)/d(x) is recognized as the
|
288
|
+
derivative of f with regards to x. This is calculated as d(lmd(f,
|
289
|
+
x))/d(x). This evaluates to the derivative expression:
|
290
|
+
|
291
|
+
<pre>
|
292
|
+
> (d(:y**2 + :x**3 + 1)/d(:x)).evaluate
|
293
|
+
=> 3*x**2
|
294
|
+
</pre>
|
295
|
+
|
296
|
+
The partial derivative is available as well as 'syntactic sugar':
|
297
|
+
|
298
|
+
<pre>
|
299
|
+
> dpart(:x**2 + :y**3 + 1, :x).evaluate.normalize
|
300
|
+
=> 2*x
|
301
|
+
> dpart(:x**2 + :y**3 + 1, :y).evaluate.normalize
|
302
|
+
=> 3*y**2
|
303
|
+
</pre>
|
304
|
+
|
305
|
+
### Integration
|
306
|
+
|
307
|
+
Integration is available as the int-operator. With one argument, the
|
308
|
+
operator evaluates to the antiderivative of the expression:
|
309
|
+
|
310
|
+
<pre>
|
311
|
+
> int(2**:x).evaluate
|
312
|
+
=> 2**x/ln(2) + C
|
313
|
+
</pre>
|
314
|
+
|
315
|
+
The variable C is used by convention to represent the free constant
|
316
|
+
factor of the antiderivative.
|
317
|
+
|
318
|
+
With three arguments, the int-operator evaluates to the definite
|
319
|
+
integral from a to b:
|
320
|
+
|
321
|
+
<pre>
|
322
|
+
> int(2**:x, 3, 4).evaluate.normalize
|
323
|
+
=> 8/ln(2)
|
324
|
+
</pre>
|
325
|
+
|
326
|
+
### Complex numbers and quaternions
|
327
|
+
|
328
|
+
The imaginary unit, i, is available as a constant, and can be used for
|
329
|
+
composing expressions with complex numbers. Simple reduction rules are
|
330
|
+
built in which reduces i*i to -1, and so on.
|
331
|
+
|
332
|
+
The basic quaternions, i, j, k are also available as constants. The
|
333
|
+
quaternion i is identical to the complex imaginary unit. Some simple
|
334
|
+
reduction rules are available for the quaternions as well.
|
335
|
+
|
336
|
+
### Exterior algebra
|
337
|
+
|
338
|
+
Caveat: Exterior algebra and differential forms are not well
|
339
|
+
understood by the author of this code. The following has not been
|
340
|
+
reviewed by any others who understand the subject better than me, and
|
341
|
+
it may very well contain a lot of errors and misunderstandings.
|
342
|
+
|
343
|
+
D-forms can be defined in several ways. The following are equal:
|
344
|
+
|
345
|
+
<pre>
|
346
|
+
> # Using the to_d method on a scalar variable
|
347
|
+
> :x.to_m.to_d
|
348
|
+
=> dx
|
349
|
+
> # Differentiating a scalar variable
|
350
|
+
> d(:x)
|
351
|
+
=> dx
|
352
|
+
> # Creating a variable, and specifying the dform type
|
353
|
+
> :dx.to_m('dform')
|
354
|
+
=> dx
|
355
|
+
</pre>
|
356
|
+
|
357
|
+
D-forms can be wedged together, forming n-forms (note that the ^
|
358
|
+
operator has lower preceedence in Ruby than in math, so parantheses
|
359
|
+
must be used, e.g. when adding):
|
360
|
+
|
361
|
+
<pre>
|
362
|
+
> d(:x)^d(:y)^d(:z)
|
363
|
+
=> dx^dy^dz
|
364
|
+
> (d(:x)^d(:x)^d(:z)).normalize
|
365
|
+
=> 0
|
366
|
+
</pre>
|
367
|
+
|
368
|
+
A vector variable can be defined using the to_m method, and specifying
|
369
|
+
the vector type.
|
370
|
+
|
371
|
+
<pre>
|
372
|
+
> :v.to_m('vector')
|
373
|
+
=> v'
|
374
|
+
</pre>
|
375
|
+
|
376
|
+
The exterior derivative and related operators all work in a local
|
377
|
+
coordinate system defined by set of basic vectors of names :x1, :x2,
|
378
|
+
x3. The names can be changed by setting the built-in variable 'basis':
|
379
|
+
|
380
|
+
<pre>
|
381
|
+
> SyMath.get_variable('basis')
|
382
|
+
=> [x1, x2, x3]
|
383
|
+
> SyMath.assign_variable('basis', [:x, :y, :z])
|
384
|
+
=> {dx=>x', dy=>y', dz=>z'}
|
385
|
+
> SyMath.get_variable('basis')
|
386
|
+
=> [x, y, z]
|
387
|
+
</pre>
|
388
|
+
|
389
|
+
The rest of this section assumes that the following scalars, vectors
|
390
|
+
and d-forms are defined:
|
391
|
+
|
392
|
+
<pre>
|
393
|
+
> SyMath.assign_variable('basis', [:x1, :x2, :x3])
|
394
|
+
=> {dx1=>x1', dx2=>x2', dx3=>x3'}
|
395
|
+
> x1 = :x1.to_m
|
396
|
+
> x2 = :x2.to_m
|
397
|
+
> x3 = :x3.to_m
|
398
|
+
> x1v = :x1.to_m('vector')
|
399
|
+
> x2v = :x2.to_m('vector')
|
400
|
+
> x3v = :x3.to_m('vector')
|
401
|
+
> dx1 = :dx1.to_m('dform')
|
402
|
+
> dx2 = :dx2.to_m('dform')
|
403
|
+
> dx3 = :dx3.to_m('dform')
|
404
|
+
</pre>
|
405
|
+
|
406
|
+
The exterior derivative is available as the xd-operator:
|
407
|
+
|
408
|
+
<pre>
|
409
|
+
> xd(:x1 - :x1*:x2 + :x3**2).evaluate
|
410
|
+
=> dx1 - (dx1*x2 + x1*dx2) + 2*x3*dx3
|
411
|
+
</pre>
|
412
|
+
|
413
|
+
The musical isomorphisms are available as the flat and sharp operators:
|
414
|
+
|
415
|
+
<pre>
|
416
|
+
> flat(x1v^x2v).evaluate
|
417
|
+
=> dx1^dx2
|
418
|
+
> sharp(dx1^dx2).evaluate
|
419
|
+
=> x1'^x2'
|
420
|
+
</pre>
|
421
|
+
|
422
|
+
The flat and sharp operators use the metric tensor in their
|
423
|
+
calculations. This is available a the built-in 'g' variable:
|
424
|
+
|
425
|
+
<pre>
|
426
|
+
> SyMath.get_variable(:g)
|
427
|
+
=> [1, 0, 0; 0, 1, 0; 0, 0, 1]
|
428
|
+
</pre>
|
429
|
+
|
430
|
+
It can be changed using the SyMath.assign_variable(:g, [value]) method.
|
431
|
+
|
432
|
+
The hodge star operator is available as well:
|
433
|
+
|
434
|
+
<pre>
|
435
|
+
> hodge(dx1^dx2).evaluate
|
436
|
+
=> dx3
|
437
|
+
> hodge(3).evaluate
|
438
|
+
=> 3*dx1^dx2^dx3
|
439
|
+
</pre>
|
440
|
+
|
441
|
+
Gradient, curl, divergence, laplacian and co-differential are defined
|
442
|
+
from the above operators in the usual way:
|
443
|
+
|
444
|
+
<pre>
|
445
|
+
> grad(x1 - x1*x2 + x3**2).evaluate
|
446
|
+
=> 2*x3*x3' - x2*x1' - x1*x2' + x1'
|
447
|
+
> curl(-x2*x1v + x1*x2*x2v + x3*x3v).evaluate
|
448
|
+
=> x2*x3' + x3'
|
449
|
+
> div(-x2*x1v + x1*x2*x2v + x3*x3v).evaluate
|
450
|
+
=> x1 + 1
|
451
|
+
> laplacian(x1**2 + x2**2 + x3**2).evaluate
|
452
|
+
=> 6
|
453
|
+
> codiff(x1**2*(dx1^dx3) + x2**2*(dx3^dx1) + x3**2*(dx1^dx2)).evaluate
|
454
|
+
=> 2*x1*dx3
|
455
|
+
</pre>
|
456
|
+
|
457
|
+
### Vectors and matrices
|
458
|
+
|
459
|
+
Vectors can be defined in the coordinate array form by converting an
|
460
|
+
array to a math object, using the to_m method. Matrices can be created
|
461
|
+
the same way from two dimensional arrays:
|
462
|
+
|
463
|
+
<pre>
|
464
|
+
> m = [[1, 2, 3], [4, 5, 6]].to_m
|
465
|
+
> v = [-3, 4, 1].to_m
|
466
|
+
> m*v.transpose
|
467
|
+
=> [1, 2, 3; 4, 5, 6]*[- 3; 4; 1]
|
468
|
+
> (m*v.transpose).evaluate
|
469
|
+
=> [8; 14]
|
470
|
+
</pre>
|
471
|
+
|
472
|
+
The vector and matrix cells can of course contain symbolic expressions
|
473
|
+
instead of just numbers.
|
474
|
+
|
475
|
+
### Methods for manipulating expressions
|
476
|
+
|
477
|
+
The library contains a few more complex expression manipulation
|
478
|
+
methods which are available to all math expression objects inheriting
|
479
|
+
from the SyMath::Value class (the root class of the expression
|
480
|
+
components).
|
481
|
+
|
482
|
+
#### Normalization
|
483
|
+
|
484
|
+
The normalization method tries to put an expression on a normal form,
|
485
|
+
based on some heuristics.
|
486
|
+
|
487
|
+
* Expressions formed by natural numbers are calculated.
|
488
|
+
* Fractions of natural numbers are simplified as far as possible.
|
489
|
+
* Products of equal factors are collapsed to power expressions.
|
490
|
+
* Products of powers with equal base are collapsed.
|
491
|
+
* Sums of equal terms are simplified to integer products.
|
492
|
+
* Product factors are ordered if permitted by commutativity.
|
493
|
+
* Sum terms are ordered.
|
494
|
+
|
495
|
+
<pre>
|
496
|
+
> # FIXME: Find some better examples
|
497
|
+
> (:x*4*:x*3*:y*:y**10).normalize
|
498
|
+
=> 12*x**2*y**11
|
499
|
+
</pre>
|
500
|
+
|
501
|
+
#### Variable replacement
|
502
|
+
|
503
|
+
The replace method replaces takes a map of 'variable => expression' as
|
504
|
+
argument. It looks up all instances of the variables in the original
|
505
|
+
expression, and replaces them with the expressions given by the map:
|
506
|
+
|
507
|
+
<pre>
|
508
|
+
> (:x**:y).replace({:x.to_m => :a + 2, :y.to_m => :b + 3})
|
509
|
+
=> (a + 2)**(b + 3)
|
510
|
+
</pre>
|
511
|
+
|
512
|
+
#### Matching and pattern replacement
|
513
|
+
|
514
|
+
The match method can be seen as a 'reverse' operation to
|
515
|
+
replace-method covered in the last section. It compares an expression
|
516
|
+
to a template expression containing some free variables. It returns an
|
517
|
+
array of all possible maps for the free variables in the template so
|
518
|
+
that it matches the original expression:
|
519
|
+
|
520
|
+
<pre>
|
521
|
+
> (:x**2 + :y**2 + 3).match(:a + :b, [:a.to_m, :b.to_m])
|
522
|
+
=> [{a=>x**2, b=>y**2 + 3},
|
523
|
+
{a=>y**2, b=>x**2 + 3},
|
524
|
+
{a=>3, b=>x**2 + y**2},
|
525
|
+
{a=>x**2 + y**2, b=>3},
|
526
|
+
{a=>x**2 + 3, b=>y**2},
|
527
|
+
{a=>y**2 + 3, b=>x**2}]
|
528
|
+
</pre>
|
529
|
+
|
530
|
+
#### Factorization and product expansion
|
531
|
+
|
532
|
+
The factorization method has been ripped from the python
|
533
|
+
Py-library. It factorizes a polynomial of one variable:
|
534
|
+
|
535
|
+
<pre>
|
536
|
+
> (6*:x**2 + 24*:x**3 - 27*:x**4 + 18*:x**5 + 72*:x**6 - 9*:x).factorize
|
537
|
+
=> 3*x*(2*x - 1)*(4*x + 3)*(3*x**3 + 1)
|
538
|
+
</pre>
|
539
|
+
|
540
|
+
The expand method expands the polynomial:
|
541
|
+
|
542
|
+
<pre>
|
543
|
+
> (3*:x*(2*:x - 1)*(4*:x + 3)*(3*:x**3 + 1)).expand.normalize
|
544
|
+
=> 72*x**6 + 18*x**5 - 27*x**4 + 24*x**3 + 6*x**2 - 9*x
|
545
|
+
</pre>
|
546
|
+
|
547
|
+
### Settings
|
548
|
+
|
549
|
+
The library has some global settings which change the behaviour of the system:
|
550
|
+
|
551
|
+
<pre>
|
552
|
+
> # List all settings
|
553
|
+
> SyMath.settings
|
554
|
+
=> {
|
555
|
+
# Symbol used when a d-form is created
|
556
|
+
:d_symbol => "d",
|
557
|
+
# Symbol used when a vector is stringified
|
558
|
+
:vector_symbol => "'",
|
559
|
+
# Co-vector symbol used in a tensor type signature
|
560
|
+
:covector_symbol => ".",
|
561
|
+
# Show all parentheses when stringifying an expression
|
562
|
+
:expl_parentheses => false,
|
563
|
+
# Put square roots on exponent form
|
564
|
+
:sq_exponent_form => false,
|
565
|
+
# Put fractions on exponent form
|
566
|
+
:fraction_exponent_form => false,
|
567
|
+
# In latex strings, insert a product sign between the factors
|
568
|
+
:ltx_product_sign => false,
|
569
|
+
# Use simplification rules when expressions are composed
|
570
|
+
:compose_with_simplify => true,
|
571
|
+
# Use oo as complex infinity
|
572
|
+
:complex_arithmetic => true,
|
573
|
+
# Return to_s representation by the inspect method
|
574
|
+
:inspect_to_s => true,
|
575
|
+
# Maximum value of factorial which is normalized to a number
|
576
|
+
:max_calculated_factorial => 100
|
577
|
+
}
|
578
|
+
> # Show one setting
|
579
|
+
> SyMath.setting(:vector_symbol)
|
580
|
+
=> "'"
|
581
|
+
> # Change a setting
|
582
|
+
> SyMath.setting(:vector_symbol, '¤')
|
583
|
+
=> "¤"
|
584
|
+
</pre>
|
585
|
+
|
586
|
+
## Development
|
587
|
+
|
588
|
+
After checking out the repo, run `bin/setup` to install
|
589
|
+
dependencies. Then, run `rake spec` to run the tests. You can also run
|
590
|
+
`bin/console` for an interactive prompt that will allow you to
|
591
|
+
experiment.
|
592
|
+
|
593
|
+
To install this gem onto your local machine, run `bundle exec rake
|
594
|
+
install`. To release a new version, update the version number in
|
595
|
+
`version.rb`, and then run `bundle exec rake release`, which will
|
596
|
+
create a git tag for the version, push git commits and tags, and push
|
597
|
+
the `.gem` file to [rubygems.org](https://rubygems.org).
|
598
|
+
|
599
|
+
## Contributing
|
600
|
+
|
601
|
+
Bug reports and pull requests are welcome on GitHub at
|
602
|
+
https://github.com/erikoest/symath. This project is intended to be a
|
603
|
+
safe, welcoming space for collaboration, and contributors are expected
|
604
|
+
to adhere to the [Contributor
|
605
|
+
Covenant](http://contributor-covenant.org) code of conduct.
|
606
|
+
|
607
|
+
## License
|
608
|
+
|
609
|
+
The gem is available as open source under the terms of the [MIT
|
610
|
+
License](https://opensource.org/licenses/MIT).
|
611
|
+
|
612
|
+
## Code of Conduct
|
613
|
+
|
614
|
+
Everyone interacting in the SyMath project’s codebases, issue trackers,
|
615
|
+
chat rooms and mailing lists is expected to follow the [code of
|
616
|
+
conduct](https://github.com/erikoest/symath/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sy"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'symath/definition/function'
|
2
|
+
|
3
|
+
module SyMath
|
4
|
+
class Definition::Abs < Definition::Function
|
5
|
+
def initialize()
|
6
|
+
super(:abs)
|
7
|
+
end
|
8
|
+
|
9
|
+
def description()
|
10
|
+
return 'abs(x) - absolute value'
|
11
|
+
end
|
12
|
+
|
13
|
+
def reduce_call(c)
|
14
|
+
arg = c.args[0]
|
15
|
+
if arg.is_nan?
|
16
|
+
return :nan.to_m
|
17
|
+
# Corner case, -oo is positive with complex arithmetic, so we need a
|
18
|
+
# specific check for that.
|
19
|
+
elsif arg.is_negative? or arg == -:oo
|
20
|
+
return - arg
|
21
|
+
elsif arg.is_positive? or arg.is_zero?
|
22
|
+
return arg
|
23
|
+
else
|
24
|
+
return c
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s(args = nil)
|
29
|
+
if args
|
30
|
+
arg = args[0].to_s
|
31
|
+
else
|
32
|
+
arg = '...'
|
33
|
+
end
|
34
|
+
|
35
|
+
return "|#{arg}|"
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_latex(args = nil)
|
39
|
+
if args
|
40
|
+
arg = args[0].to_latex
|
41
|
+
else
|
42
|
+
arg = '...'
|
43
|
+
end
|
44
|
+
|
45
|
+
return "\\lvert#{arg}\\rvert"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|