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.
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