symath 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|