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