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,282 @@
1
+ module SyMath
2
+ class Type
3
+ # Type name
4
+ attr_reader :name
5
+ # Matrix dimensions
6
+ attr_reader :dimn, :dimm
7
+ # Tensor indexes (array of 'h' amd 'l')
8
+ attr_reader :indexes
9
+
10
+ # Type hierarchy with generic types at the top level and more specific
11
+ # types further down.
12
+ @@hierarchy = {
13
+ # Non numbers
14
+ :nonfinite => 1,
15
+ # Operators
16
+ :operator => {
17
+ # Linear operators
18
+ :linop => {
19
+ # Matrices
20
+ :matrix => {
21
+ :column => 1,
22
+ :row => 1,
23
+ },
24
+ # Vector types (one dimension param)
25
+ :tensor => {
26
+ :nvector => {
27
+ :vector => 1,
28
+ },
29
+ :nform => {
30
+ :covector => {
31
+ :dform => 1,
32
+ },
33
+ },
34
+ },
35
+ # Scalar types
36
+ :quaternion => {
37
+ :scalar => {
38
+ :complex => {
39
+ :real => {
40
+ :rational => {
41
+ :integer => {
42
+ :natural => 1
43
+ }
44
+ }
45
+ },
46
+ :imaginary => 1,
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ # Create a transitively closed subtype hash for quicker subtype lookup
55
+ @@subtype = {}
56
+
57
+ def self.fill_subtype_hash(hiera, bases = [])
58
+ hiera.keys.each do |k|
59
+ bases.each do |b|
60
+ if !@@subtype.key?(k)
61
+ @@subtype[k] = {}
62
+ end
63
+ @@subtype[k][b] = 1
64
+ end
65
+
66
+ next unless hiera[k].is_a?(Hash)
67
+
68
+ fill_subtype_hash(hiera[k], bases + [k])
69
+ end
70
+ end
71
+
72
+ fill_subtype_hash(@@hierarchy)
73
+
74
+ @@types = {}
75
+
76
+ def self.types
77
+ return @@types
78
+ end
79
+
80
+ def initialize(name, dimn: nil, dimm: nil, indexes: nil)
81
+ @name = name.to_sym
82
+ @dimn = dimn
83
+ @dimm = dimm
84
+ @indexes = indexes
85
+ end
86
+
87
+ # Hash of simple types, for faster instansiations.
88
+ @@types = {
89
+ :natural => SyMath::Type.new(:natural),
90
+ :integer => SyMath::Type.new(:integer),
91
+ :rational => SyMath::Type.new(:rational),
92
+ :real => SyMath::Type.new(:real),
93
+ :complex => SyMath::Type.new(:complex),
94
+ :imaginary => SyMath::Type.new(:imaginary),
95
+ :quaternion => SyMath::Type.new(:quaternion),
96
+ :vector => SyMath::Type.new(:vector, indexes: ['u']),
97
+ :covector => SyMath::Type.new(:covector, indexes: ['l']),
98
+ :dform => SyMath::Type.new(:dform, indexes: ['l']),
99
+ }
100
+
101
+ # Check if a type is a subtype of another
102
+ def is_subtype?(other)
103
+ # Allow input as type or as string
104
+ other = other.to_t
105
+
106
+ # Types are not compatible if they have different attributes.
107
+ # FIXME: What is the correct way to define subtypes of matrices
108
+ # with respect to dimensions?
109
+ return false if @dim1 != other.dimn
110
+ return false if @dim2 != other.dimm
111
+
112
+ # Same types,
113
+ return true if @name == other.name
114
+
115
+ # This is a subtype of other
116
+ return true if @@subtype.key?(@name) and @@subtype[@name].key?(other.name)
117
+
118
+ # Fallback to false
119
+ return false
120
+ end
121
+
122
+ def common_parent(other)
123
+ if other.is_subtype?(self)
124
+ return self
125
+ elsif is_subtype?(other)
126
+ return other
127
+ elsif is_subtype?(@@types[:complex]) and
128
+ other.is_subtype?(@@types[:complex])
129
+ return @@types[:complex]
130
+ else
131
+ raise "No common type for #{self} and #{other}"
132
+ end
133
+ end
134
+
135
+ # Determine the type of a sum
136
+ def sum(other)
137
+ if is_subtype?('quaternion') and
138
+ other.is_subtype?('quaternion')
139
+ return common_parent(other)
140
+ elsif self == other
141
+ return self
142
+ elsif self.is_subtype?('tensor') or other.is_subtype?('tensor')
143
+ # FIXME: Hack. This is probably not true.
144
+ return self
145
+ else
146
+ raise "Types #{self} and #{other} cannot be summed."
147
+ end
148
+ end
149
+
150
+ # Determine the type of a product
151
+ def product(other)
152
+ scalar = is_scalar?
153
+ oscalar = other.is_scalar?
154
+
155
+ if scalar and oscalar
156
+ return common_parent(other)
157
+ elsif scalar
158
+ return other
159
+ elsif oscalar
160
+ return self
161
+ elsif is_subtype?('matrix') and
162
+ other.is_subtype?('matrix') and
163
+ dimn == other.dimm
164
+ return 'matrix'.to_t(dimm: dimm, dimn: other.dimn)
165
+ else
166
+ raise "Types #{self} and #{other} cannot be multiplied"
167
+ end
168
+ end
169
+
170
+ # Return tensor degree (rank)
171
+ def degree()
172
+ return @indexes.length
173
+ end
174
+
175
+ # True if type is a scalar value
176
+ def is_scalar?()
177
+ return is_subtype?('scalar')
178
+ end
179
+
180
+ def is_vector?()
181
+ return is_subtype?('vector')
182
+ end
183
+
184
+ # True if type is a linear combination of blades
185
+ def is_nvector?()
186
+ return is_subtype?('nvector')
187
+ end
188
+
189
+ def is_matrix?()
190
+ return ([:matrix, :colum, :row].include?(@name))
191
+ end
192
+
193
+ # True if type is a pseudovector. We use the notion of a pseudovector
194
+ # both for N-1 dimensional nvectors and nforms (N being the dimensionality
195
+ # of the default vector space)
196
+ def is_pseudovector?()
197
+ if !is_subtype?('nvector') and !is_subtype?('nform')
198
+ return false
199
+ end
200
+
201
+ return degree == SyMath.get_variable(:basis).ncols - 1
202
+ end
203
+
204
+ # True if type is a pseudoscalar. We use the notion of a pseudoscalar
205
+ # both for N dimensional nvectors and nforms (N being the dimensionality
206
+ # of the default vector space)
207
+ def is_pseudoscalar?()
208
+ if !is_subtype?('nvector') and !is_subtype?('nform')
209
+ return false
210
+ end
211
+
212
+ return degree == SyMath.get_variable(:basis).ncols
213
+ end
214
+
215
+ # True if type is the dual of an nvector
216
+ def is_nform?()
217
+ return is_subtype?('nform')
218
+ end
219
+
220
+ # FIXME: What is the difference between a covector and a dform?
221
+ def is_covector?()
222
+ return is_subtype?('covector')
223
+ end
224
+
225
+ def is_dform?()
226
+ return is_subtype?('dform')
227
+ end
228
+
229
+ # Return index list as a string coded with upper indices as ' and lower
230
+ # indices as .
231
+ def index_str()
232
+ return @indexes.map do |i|
233
+ if i == 'u'
234
+ '\''
235
+ elsif i == 'l'
236
+ '.'
237
+ end
238
+ end.join('')
239
+ end
240
+
241
+ def ==(other)
242
+ return false if @dim1 != other.dimn
243
+ return false if @dim2 != other.dimm
244
+ return false if @indexes != other.indexes
245
+ return @name == other.name
246
+ end
247
+
248
+ def to_s()
249
+ if !@dimn.nil?
250
+ return @name.to_s + '[' + @dimm.to_s + 'x' + @dimn.to_s + ']'
251
+ elsif !@indexes.nil?
252
+ return @name.to_s + '[' + @indexes.join('') + ']'
253
+ else
254
+ return @name.to_s
255
+ end
256
+ end
257
+
258
+ def to_t(*args)
259
+ return self
260
+ end
261
+ end
262
+ end
263
+
264
+ class String
265
+ def to_t(**args)
266
+ if args.empty? and SyMath::Type.types.key?(self.to_sym)
267
+ return SyMath::Type.types[self.to_sym]
268
+ end
269
+
270
+ return SyMath::Type.new(self, **args)
271
+ end
272
+ end
273
+
274
+ class Symbol
275
+ def to_t(**args)
276
+ if args.empty? and SyMath::Type.types.key?(self)
277
+ return SyMath::Type.types[self]
278
+ end
279
+
280
+ return SyMath::Type.new(self, **args)
281
+ end
282
+ end
@@ -0,0 +1,372 @@
1
+ require 'symath/operation'
2
+ require 'symath/operation/match'
3
+ require 'symath/operation/normalization'
4
+ require 'symath/operation/distributivelaw'
5
+ require 'symath/operation/differential'
6
+ require 'symath/operation/integration'
7
+ require 'symath/operation/exterior'
8
+
9
+ module SyMath
10
+ class Value
11
+ include Operation::Match
12
+ include Operation::Normalization
13
+ include Operation::DistributiveLaw
14
+ include Operation::Differential
15
+ include Operation::Integration
16
+ include Operation::Exterior
17
+
18
+ @@class_order = [
19
+ 'SyMath::Definition::Number',
20
+ 'SyMath::Definition::Constant',
21
+ 'SyMath::Definition::Variable',
22
+ 'SyMath::Definition::Function',
23
+ 'SyMath::Definition::Operator',
24
+ 'SyMath::Definition',
25
+ 'SyMath::Minus',
26
+ 'SyMath::Power',
27
+ 'SyMath::Wedge',
28
+ 'SyMath::Fraction',
29
+ 'SyMath::Product',
30
+ 'SyMath::Sum',
31
+ 'SyMath::Operator',
32
+ ]
33
+
34
+ @@class_order_hash = {}
35
+
36
+ @@class_order.each_with_index do |e, i|
37
+ @@class_order_hash[e] = i
38
+ end
39
+
40
+ def self.create(definition, *args)
41
+ if !definition.is_a?(SyMath::Value)
42
+ definition = SyMath::Definition.get(definition)
43
+ end
44
+
45
+ if SyMath.setting(:compose_with_simplify)
46
+ return self.compose_with_simplify(definition, *args)
47
+ else
48
+ return self.new(definition, *args)
49
+ end
50
+ end
51
+
52
+ # Compose with simplify. Defaults to composition with no reductions
53
+ def self.compose_with_simplify(*args)
54
+ return self.new(*args)
55
+ end
56
+
57
+ def deep_clone()
58
+ return Marshal.load(Marshal.dump(self))
59
+ end
60
+
61
+ # Sorting/ordering operator. The ordering is used by the normalization to
62
+ # order the parts of a sum, product etc.
63
+ def <=>(other)
64
+ return @@class_order_hash[self.class.name] <=>
65
+ @@class_order_hash[other.class.name]
66
+ end
67
+
68
+ def <(other)
69
+ return (self <=> other) < 0
70
+ end
71
+
72
+ def >(other)
73
+ return (self <=> other) > 0
74
+ end
75
+
76
+ def <=(other)
77
+ return (self <=> other) <= 0
78
+ end
79
+
80
+ def >=(other)
81
+ return (self <=> other) >= 0
82
+ end
83
+
84
+ # Default properties for operators
85
+ # Note: Returning nil here means neither true or false, but unknown.
86
+ def is_nan?()
87
+ return
88
+ end
89
+
90
+ def is_finite?()
91
+ return
92
+ end
93
+
94
+ def is_positive?()
95
+ return
96
+ end
97
+
98
+ def is_negative?()
99
+ if is_nan?
100
+ return false
101
+ end
102
+
103
+ return (is_positive? == false and is_zero? == false)
104
+ end
105
+
106
+ def is_number?()
107
+ return false
108
+ end
109
+
110
+ def is_negative_number?()
111
+ return false
112
+ end
113
+
114
+ def is_zero?()
115
+ return
116
+ end
117
+
118
+ def is_divisor_factor?()
119
+ return false
120
+ end
121
+
122
+ def is_unit_quaternion?()
123
+ return false
124
+ end
125
+
126
+ # Reduce expression if possible. Defaults to no reduction
127
+ def reduce()
128
+ return self
129
+ end
130
+
131
+ # Evaluate expression. Defaults to no evaluation
132
+ def evaluate()
133
+ return self
134
+ end
135
+
136
+ ##
137
+ # Compositional math operator methods. No reductions are performed.
138
+ ##
139
+ def add(other)
140
+ return SyMath::Sum.new(self, other.to_m)
141
+ end
142
+
143
+ def sub(other)
144
+ return SyMath::Sum.new(self, SyMath::Minus.new(other.to_m))
145
+ end
146
+
147
+ def neg()
148
+ return SyMath::Minus.new(self)
149
+ end
150
+
151
+ def mul(other)
152
+ return SyMath::Product.new(self, other.to_m)
153
+ end
154
+
155
+ def div(other)
156
+ return SyMath::Fraction.new(self, other.to_m)
157
+ end
158
+
159
+ def power(other)
160
+ return SyMath::Power.new(self, other.to_m)
161
+ end
162
+
163
+ def wedge(other)
164
+ return SyMath::Wedge.new(self, other.to_m)
165
+ end
166
+
167
+ ##
168
+ # Overridden object operators.
169
+ # These operations do some simple reductions.
170
+ ##
171
+ def +(other)
172
+ return SyMath::Sum.create(self, other)
173
+ end
174
+
175
+ def -(other)
176
+ return self + (- other)
177
+ end
178
+
179
+ def -@()
180
+ return SyMath::Minus.create(self)
181
+ end
182
+
183
+ def *(other)
184
+ return SyMath::Product.create(self, other)
185
+ end
186
+
187
+ def /(other)
188
+ return SyMath::Fraction.create(self, other)
189
+ end
190
+
191
+ def inv()
192
+ return 1/self
193
+ end
194
+
195
+ def **(other)
196
+ return SyMath::Power.create(self, other)
197
+ end
198
+
199
+ def ^(other)
200
+ # Identical with *. We apply * or ^ depending on what
201
+ # the arguments are.
202
+ return self*other
203
+ end
204
+
205
+ ##
206
+ # Helper methods for the normalization operation. These are overridden by
207
+ # the subclasses. Default behaviour is defined here.
208
+ ##
209
+
210
+ # Value is a sum or unitary minus
211
+ def is_sum_exp?()
212
+ return false
213
+ end
214
+
215
+ # Value is a product, fraction or unitary minus
216
+ def is_prod_exp?()
217
+ return false
218
+ end
219
+
220
+ # Returns the terms of a sum in an array.
221
+ # Defaults to self for non-sums.
222
+ def terms()
223
+ return [self]
224
+ end
225
+
226
+ # Returns the base of a power expression.
227
+ # Defaults to self for non-powers.
228
+ def base()
229
+ return self
230
+ end
231
+
232
+ # Returns the exponent of a power expression.
233
+ # Defaults to self for non-powers.
234
+ def exponent()
235
+ return 1.to_m
236
+ end
237
+
238
+ # Return factors in enumerator
239
+ def factors()
240
+ return [self].to_enum
241
+ end
242
+
243
+ # Returns the accumulated sign of a product.
244
+ # Defaults to 1 for positive non-sum expressions.
245
+ def sign()
246
+ return 1
247
+ end
248
+
249
+ # Simple reduction rules, allows sign to change. Returns
250
+ # (reduced exp, sign, changed). Defaults to no change
251
+ def reduce_modulo_sign
252
+ return self, 1, false
253
+ end
254
+
255
+ # By default, assume an unknown expression to be scalar
256
+ def type()
257
+ return 'scalar'.to_t
258
+ end
259
+
260
+ alias eql? ==
261
+
262
+ def to_m()
263
+ return self
264
+ end
265
+
266
+ def inspect()
267
+ if SyMath.setting(:inspect_to_s)
268
+ return self.to_s
269
+ else
270
+ return super.inspect
271
+ end
272
+ end
273
+
274
+ def dump(indent = 0)
275
+ i = ' '*indent
276
+ return "#{i}#{self.class}: #{self}"
277
+ end
278
+ end
279
+ end
280
+
281
+ class Integer
282
+ alias_method :super_add, :+
283
+ alias_method :super_sub, :-
284
+ alias_method :super_mul, :*
285
+ alias_method :super_div, :/
286
+ alias_method :super_pow, :**
287
+ alias_method :super_wedge, :^
288
+
289
+ def +(other)
290
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
291
+ return self.to_m + other.to_m
292
+ else
293
+ return self.super_add(other)
294
+ end
295
+ end
296
+
297
+ def -(other)
298
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
299
+ return self.to_m - other.to_m
300
+ else
301
+ return self.super_sub(other)
302
+ end
303
+ end
304
+
305
+ def *(other)
306
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
307
+ return self.to_m*other.to_m
308
+ else
309
+ return self.super_mul(other)
310
+ end
311
+ end
312
+
313
+ def /(other)
314
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
315
+ return self.to_m/other.to_m
316
+ else
317
+ return self.super_div(other)
318
+ end
319
+ end
320
+
321
+ def **(other)
322
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
323
+ return self.to_m**other.to_m
324
+ else
325
+ return self.super_pow(other)
326
+ end
327
+ end
328
+
329
+ def ^(other)
330
+ if other.class.method_defined?(:to_m) and !other.is_a?(Integer)
331
+ return self.to_m^other.to_m
332
+ else
333
+ return self.super_wedge(other)
334
+ end
335
+ end
336
+ end
337
+
338
+ class Symbol
339
+ def +(other)
340
+ return self.to_m + other.to_m
341
+ end
342
+
343
+ def -(other)
344
+ return self.to_m - other.to_m
345
+ end
346
+
347
+ def -@()
348
+ return - self.to_m
349
+ end
350
+
351
+ def *(other)
352
+ return self.to_m*other.to_m
353
+ end
354
+
355
+ def /(other)
356
+ return self.to_m/other.to_m
357
+ end
358
+
359
+ def **(other)
360
+ return self.to_m**other.to_m
361
+ end
362
+
363
+ def ^(other)
364
+ return self.to_m^other.to_m
365
+ end
366
+ end
367
+
368
+ class String
369
+ def to_m()
370
+ return SyMath.parse(self)
371
+ end
372
+ end
@@ -0,0 +1,3 @@
1
+ module SyMath
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,48 @@
1
+ require 'symath/product'
2
+
3
+ module SyMath
4
+ class Wedge < Product
5
+ def initialize(arg1, arg2)
6
+ super(arg1, arg2)
7
+ @name = '^'
8
+ end
9
+
10
+ def type()
11
+ if factor1.type.is_subtype?('tensor') and
12
+ factor2.type.is_subtype?('tensor')
13
+ # Wedge product of two tensor-like object. Determine index signature
14
+ # and subtype.
15
+ indexes = factor1.type.indexes + factor2.type.indexes
16
+ if (indexes - ['u']).empty?
17
+ type = 'nvector'
18
+ elsif (indexes - ['l']).empty?
19
+ type = 'nform'
20
+ else
21
+ type = 'tensor'
22
+ end
23
+
24
+ return type.to_t(indexes: indexes)
25
+ else
26
+ return factor1.type.sum(factor2.type)
27
+ end
28
+ end
29
+
30
+ def to_s()
31
+ if SyMath.setting(:expl_parentheses)
32
+ return '('.to_s + factor1.to_s + '^' + factor2.to_s + ')'.to_s
33
+ else
34
+ return @args.map do |a|
35
+ if a.is_sum_exp?
36
+ '(' + a.to_s + ')'
37
+ else
38
+ a.to_s
39
+ end
40
+ end.join('^')
41
+ end
42
+ end
43
+
44
+ def to_latex()
45
+ return @args.map { |a| a.to_latex }.join('\wedge')
46
+ end
47
+ end
48
+ end