symath 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +8 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +616 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/symath/definition/abs.rb +48 -0
  13. data/lib/symath/definition/arccos.rb +25 -0
  14. data/lib/symath/definition/arccot.rb +23 -0
  15. data/lib/symath/definition/arccsc.rb +24 -0
  16. data/lib/symath/definition/arcsec.rb +24 -0
  17. data/lib/symath/definition/arcsin.rb +25 -0
  18. data/lib/symath/definition/arctan.rb +23 -0
  19. data/lib/symath/definition/bounds.rb +39 -0
  20. data/lib/symath/definition/codiff.rb +31 -0
  21. data/lib/symath/definition/constant.rb +111 -0
  22. data/lib/symath/definition/cos.rb +17 -0
  23. data/lib/symath/definition/cot.rb +17 -0
  24. data/lib/symath/definition/csc.rb +17 -0
  25. data/lib/symath/definition/curl.rb +27 -0
  26. data/lib/symath/definition/d.rb +62 -0
  27. data/lib/symath/definition/div.rb +27 -0
  28. data/lib/symath/definition/exp.rb +112 -0
  29. data/lib/symath/definition/fact.rb +55 -0
  30. data/lib/symath/definition/flat.rb +31 -0
  31. data/lib/symath/definition/function.rb +197 -0
  32. data/lib/symath/definition/grad.rb +23 -0
  33. data/lib/symath/definition/hodge.rb +23 -0
  34. data/lib/symath/definition/int.rb +75 -0
  35. data/lib/symath/definition/laplacian.rb +23 -0
  36. data/lib/symath/definition/lmd.rb +97 -0
  37. data/lib/symath/definition/ln.rb +45 -0
  38. data/lib/symath/definition/number.rb +51 -0
  39. data/lib/symath/definition/operator.rb +228 -0
  40. data/lib/symath/definition/sec.rb +17 -0
  41. data/lib/symath/definition/sharp.rb +31 -0
  42. data/lib/symath/definition/sin.rb +17 -0
  43. data/lib/symath/definition/sqrt.rb +62 -0
  44. data/lib/symath/definition/tan.rb +17 -0
  45. data/lib/symath/definition/trig.rb +95 -0
  46. data/lib/symath/definition/variable.rb +284 -0
  47. data/lib/symath/definition/xd.rb +28 -0
  48. data/lib/symath/definition.rb +205 -0
  49. data/lib/symath/equation.rb +67 -0
  50. data/lib/symath/fraction.rb +177 -0
  51. data/lib/symath/matrix.rb +252 -0
  52. data/lib/symath/minus.rb +125 -0
  53. data/lib/symath/operation/differential.rb +167 -0
  54. data/lib/symath/operation/distributivelaw.rb +367 -0
  55. data/lib/symath/operation/exterior.rb +64 -0
  56. data/lib/symath/operation/integration.rb +329 -0
  57. data/lib/symath/operation/match.rb +166 -0
  58. data/lib/symath/operation/normalization.rb +458 -0
  59. data/lib/symath/operation.rb +36 -0
  60. data/lib/symath/operator.rb +163 -0
  61. data/lib/symath/parser.rb +473 -0
  62. data/lib/symath/parser.y +129 -0
  63. data/lib/symath/poly/dup.rb +835 -0
  64. data/lib/symath/poly/galois.rb +621 -0
  65. data/lib/symath/poly.rb +142 -0
  66. data/lib/symath/power.rb +224 -0
  67. data/lib/symath/product.rb +183 -0
  68. data/lib/symath/sum.rb +174 -0
  69. data/lib/symath/type.rb +282 -0
  70. data/lib/symath/value.rb +372 -0
  71. data/lib/symath/version.rb +3 -0
  72. data/lib/symath/wedge.rb +48 -0
  73. data/lib/symath.rb +157 -0
  74. data/symath.gemspec +39 -0
  75. metadata +160 -0
@@ -0,0 +1,367 @@
1
+ require 'symath/operation'
2
+ require 'symath/poly/dup'
3
+
4
+ module SyMath::Operation::DistributiveLaw
5
+ # The expand() method expands a product using the distributive law over
6
+ # products of sums:
7
+ # a*(b + c) -> a*b + a*c
8
+ # a*(b - c) -> a*b - a*c
9
+ # The transformation iterates until no changes occur. Thus, the expression
10
+ # (a + b)*(c + d) transforms to a*c + a*d + b*c + b*d
11
+ def expand()
12
+ return iterate('expand_single_pass')
13
+ end
14
+
15
+ def expand_single_pass
16
+ if is_a?(SyMath::Minus)
17
+ return -argument.expand_single_pass
18
+ end
19
+
20
+ if is_a?(SyMath::Power) or is_a?(SyMath::Product)
21
+ ret = 1.to_m
22
+
23
+ factors.each do |f|
24
+ if f.is_a?(SyMath::Power)
25
+ if f.exponent.is_number?
26
+ f.exponent.value.times { ret = expand_product(ret, f.base) }
27
+ else
28
+ ret = expand_product(ret, f)
29
+ end
30
+ else
31
+ ret = expand_product(ret, f)
32
+ end
33
+ end
34
+
35
+ return ret
36
+ end
37
+
38
+ if is_sum_exp?
39
+ ret = 0.to_m
40
+
41
+ terms.each do |t|
42
+ ret += t.expand_single_pass
43
+ end
44
+
45
+ return ret
46
+ end
47
+
48
+ return self
49
+ end
50
+
51
+ def expand_product(exp1, exp2)
52
+ sign = 1.to_m
53
+
54
+ if exp1.is_a?(SyMath::Minus)
55
+ exp1 = exp1.argument
56
+ sign = -sign
57
+ end
58
+
59
+ if exp2.is_a?(SyMath::Minus)
60
+ exp2 = exp2.argument
61
+ sign = -sign
62
+ end
63
+
64
+ ret = 0.to_m
65
+
66
+ exp1.terms.each do |t1|
67
+ exp2.terms.each do |t2|
68
+ ret += sign*t1*t2
69
+ end
70
+ end
71
+
72
+ return ret
73
+ end
74
+
75
+ def has_fractional_terms?()
76
+ terms.each do |t|
77
+ t.factors.each do |f|
78
+ if f.is_divisor_factor?
79
+ return true
80
+ end
81
+ end
82
+ end
83
+
84
+ return false
85
+ end
86
+
87
+ # Collect factors which occur in each term.
88
+ def factorize_simple()
89
+ return self if !self.is_sum_exp?
90
+
91
+ sfactors = {}
92
+ vfactors = {}
93
+ coeffs = []
94
+ dcoeffs = []
95
+ vectors = []
96
+
97
+ terms.each_with_index do |t, i|
98
+ c = 1
99
+ dc = 1
100
+ vf = 1.to_m
101
+
102
+ t.factors.each do |f|
103
+ # Sign
104
+ if f == -1
105
+ c *= -1
106
+ next
107
+ end
108
+
109
+ # Constant
110
+ if f.is_number?
111
+ c *= f.value
112
+ next
113
+ end
114
+
115
+ if f.is_divisor_factor?
116
+ # Divisor constant
117
+ if f.base.is_number?
118
+ dc *= f.base.value**f.exponent.argument.value
119
+ next
120
+ end
121
+
122
+ # Divisor factor
123
+ ex = f.base
124
+
125
+ if !sfactors.key?(ex)
126
+ sfactors[ex] = []
127
+ end
128
+
129
+ if sfactors[ex][i].nil?
130
+ sfactors[ex][i] = - f.exponent.argument.value
131
+ else
132
+ sfactors[ex][i] -= f.exponent.argument.value
133
+ end
134
+ next
135
+ end
136
+
137
+ # Vector factor
138
+ if f.type.is_subtype?(:tensor)
139
+ vf *= f
140
+ next
141
+ end
142
+
143
+ # Scalar factor
144
+ if f.exponent.is_number?
145
+ ex = f.base
146
+ n = f.exponent.value
147
+ else
148
+ ex = f
149
+ n = 1
150
+ end
151
+
152
+ if !sfactors.key?(ex)
153
+ sfactors[ex] = []
154
+ end
155
+
156
+ sfactors[ex][i] = n.value
157
+ end
158
+
159
+ coeffs.push(c)
160
+ dcoeffs.push(dc)
161
+ vectors.push(vf)
162
+ end
163
+
164
+ # If there is only one term, there is nothing to factorize
165
+ if coeffs.length == 1
166
+ return self
167
+ end
168
+
169
+ # Try to factorize the scalar part
170
+ spart = 1.to_m
171
+ dpart = 1.to_m
172
+ sfactors.each do |ex, pow|
173
+ # Replace nil with 0 and extend array to full length
174
+ pow.map! { |i| i || 0 }
175
+ (coeffs.length - pow.length).times { pow << 0 }
176
+
177
+ if pow.max > 0 and pow.min > 0
178
+ f = pow.min
179
+ pow.map! { |i| i - f }
180
+ spart = spart*ex**f
181
+ end
182
+
183
+ if pow.max < 0 and pow.min < 0
184
+ f = pow.max
185
+ pow.map! { |i| i - f }
186
+ dpart = dpart*ex**(-f)
187
+ end
188
+ end
189
+
190
+ # Return self if there were no common factors.
191
+ if spart == 1 and dpart == 1
192
+ return self
193
+ end
194
+
195
+ # Extract gcd from coeffs and dcoeffs
196
+ gcd_coeffs = coeffs.inject(:gcd)
197
+ gcd_dcoeffs = dcoeffs.inject(:gcd)
198
+ coeffs.map! { |i| i/gcd_coeffs }
199
+ dcoeffs.map! { |i| i/gcd_dcoeffs }
200
+
201
+ newsum = 0.to_m
202
+
203
+ (0..coeffs.length-1).each do |i|
204
+ t = coeffs[i].to_m
205
+
206
+ sfactors.each do |ex, pow|
207
+ next if pow[i].nil?
208
+ if pow[i] > 0
209
+ t *= ex**pow[i]
210
+ elsif pow[i] < 0
211
+ t /= ex**(-pow[i])
212
+ end
213
+ end
214
+
215
+ t /= dcoeffs[i]
216
+ t *= vectors[i]
217
+
218
+ newsum += t
219
+ end
220
+
221
+ return gcd_coeffs*spart*newsum/(gcd_dcoeffs*dpart)
222
+ end
223
+
224
+ # The factorize() method factorizes a univariate polynomial expression
225
+ # with integer coefficients.
226
+ def factorize()
227
+ if (has_fractional_terms?)
228
+ e = combine_fractions
229
+ if e.is_a?(SyMath::Fraction)
230
+ return e.dividend.factorize_integer_poly.div(e.divisor)
231
+ end
232
+ else
233
+ return factorize_integer_poly
234
+ end
235
+ end
236
+
237
+ def factorize_integer_poly()
238
+ dup = SyMath::Poly::DUP.new(self)
239
+ factors = dup.factor
240
+
241
+ ret = factors[1].map do |f|
242
+ if f[1] != 1
243
+ f[0].to_m.power(f[1])
244
+ else
245
+ f[0].to_m
246
+ end
247
+ end
248
+
249
+ if factors[0] != 1
250
+ ret.unshift(factors[0].to_m)
251
+ end
252
+
253
+ return ret.inject(:mul)
254
+ end
255
+
256
+
257
+ # The combine_fractions() method combines fractions by first determining
258
+ # their least common denominator, then applying the distributive law.
259
+ # Examples:
260
+ # a/c + b/c -> (a + b)/c
261
+ # 2/3 + 3/4 -> 17/12
262
+ # a/2 + 2*a/3 -> 7*a/6
263
+ # 2*a/b + 2*c/(3*b) -> (6*a + 2*c)/(3*b)
264
+ def combine_fractions()
265
+ if is_sum_exp?
266
+ return combfrac_sum
267
+ end
268
+
269
+ return recurse('combine_fractions', nil)
270
+ end
271
+
272
+ def combfrac_add_term(sum, t)
273
+ c = 1
274
+ dc = 1
275
+ fact = 1.to_m
276
+ divf = 1.to_m
277
+
278
+ t.factors.each do |f|
279
+ if f.is_number?
280
+ c *= f.value
281
+ next
282
+ end
283
+
284
+ if f == -1
285
+ fact *= -1
286
+ next
287
+ end
288
+
289
+ if f.is_divisor_factor?
290
+ if f.base.is_number?
291
+ dc *= (f.base.value**f.exponent.argument.value)
292
+ else
293
+ divf *= f.base
294
+ end
295
+ next
296
+ end
297
+
298
+ fact *= f
299
+ end
300
+
301
+ if !sum.key?(divf)
302
+ sum[divf] = {}
303
+ sum[divf][:fact] = fact
304
+ sum[divf][:c] = c
305
+ sum[divf][:dc] = dc
306
+ return
307
+ end
308
+
309
+ s = sum[divf]
310
+ lcm = dc.lcm(s[:dc])
311
+
312
+ if lcm > dc
313
+ c *= lcm/dc
314
+ dc = lcm
315
+ end
316
+
317
+ if lcm > s[:dc]
318
+ s[:c] *= lcm/s[:dc]
319
+ s[:dc] = lcm
320
+ end
321
+
322
+ if fact.nil?
323
+ fact = c.to_m
324
+ elsif c > 1
325
+ fact = fact.mul(c.to_m)
326
+ end
327
+
328
+ if s[:fact].nil?
329
+ fact = fact.add(s[:c].to_m) if s[:c] > 1
330
+ else
331
+ fact = fact.add(s[:c] > 1 ? s[:c].to_m*s[:fact] : s[:fact])
332
+ end
333
+
334
+ s[:fact] = fact
335
+ s[:c] = 1
336
+ end
337
+
338
+ def combfrac_sum
339
+ sum = {}
340
+
341
+ terms.each do |t|
342
+ combfrac_add_term(sum, t)
343
+ end
344
+
345
+ ret = 0.to_m
346
+
347
+ sum.keys.each do |divf|
348
+ s = sum[divf]
349
+ if s[:c] > 1
350
+ r = s[:c].to_m.mul(s[:fact])
351
+ else
352
+ r = s[:fact]
353
+ end
354
+
355
+ if divf.nil?
356
+ r = r.div(s[:dc]) if s[:dc] > 1
357
+ elsif s[:dc] > 1
358
+ r = r.div(s[:dc].to_m*divf)
359
+ else
360
+ r = r.div(divf)
361
+ end
362
+
363
+ ret += r
364
+ return ret
365
+ end
366
+ end
367
+ end
@@ -0,0 +1,64 @@
1
+ require 'symath/operation'
2
+
3
+ module SyMath::Operation::Exterior
4
+ # This operation module provides methods for calculating the musical
5
+ # isomorphisms and the hodge star isomorphisms of exterior algebra
6
+ # expressions.
7
+
8
+ # Lower indices, transforming vectors to differential forms
9
+ def flat()
10
+ res = recurse('flat', nil)
11
+
12
+ if res.is_a?(SyMath::Definition::Variable)
13
+ if res.type.is_subtype?('vector')
14
+ return res.lower_vector
15
+ end
16
+ end
17
+
18
+ return res
19
+ end
20
+
21
+ # Raise indices, transforming differential forms to vectors
22
+ def sharp()
23
+ res = recurse('sharp', nil)
24
+
25
+ if res.is_a?(SyMath::Definition::Variable)
26
+ if res.type.is_subtype?('dform')
27
+ return res.raise_dform
28
+ end
29
+ end
30
+
31
+ return res
32
+ end
33
+
34
+ # Calculate hodge star duality
35
+ def hodge()
36
+ # Recurse down sums and subtractions
37
+ if is_sum_exp?
38
+ return terms.map do |t|
39
+ if t.is_a?(SyMath::Minus)
40
+ - t.argument.hodge
41
+ else
42
+ t.hodge
43
+ end
44
+ end.inject(:+)
45
+ else
46
+ # FIXME: If expression is a product of sums, expand the product first
47
+ # (distributive law), then hodge op on the new sum.
48
+
49
+ # Replace nvectors and nforms with their hodge dual
50
+ s = []
51
+ v = []
52
+ factors.each do |f|
53
+ if f.type.is_vector? or f.type.is_dform?
54
+ v.push f
55
+ else
56
+ s.push f
57
+ end
58
+ end
59
+
60
+ h = SyMath::Definition::Variable.hodge_dual(v.inject(1.to_m, :*))
61
+ return s.inject(1.to_m, :*)*h
62
+ end
63
+ end
64
+ end