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