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,284 @@
1
+ require 'symath/value'
2
+ require 'symath/type'
3
+
4
+ module SyMath
5
+ class Definition::Variable < Definition
6
+
7
+ # Return parity of permutation. Even number of permutations give
8
+ # 1 and odd number gives -1
9
+ def self.permutation_parity(perm)
10
+ # perm is an array of indexes representing the permutation
11
+ # Put permutation list into disjoint cycles form
12
+ cycles = {}
13
+ (0..perm.length-1).each do |i|
14
+ cycles[perm[i]] = i
15
+ end
16
+
17
+ sign = 0
18
+
19
+ # Count the number even cycles.
20
+ (0..perm.length-1).each do |i|
21
+ next if !cycles.key?(i)
22
+
23
+ count = 0
24
+ while cycles.key?(i)
25
+ count += 1
26
+ j = cycles[i]
27
+ cycles.delete(i)
28
+ i = j
29
+ end
30
+
31
+ if (count % 2) == 0
32
+ sign += 1
33
+ end
34
+ end
35
+
36
+ # Even => 1, Odd => -1
37
+ sign = (1 - (sign % 2)*2).to_m
38
+ end
39
+
40
+ # Re-calculate various auxiliary data structured based on the given basis
41
+ # This does not scale for higher dimensions, but that will most probably
42
+ # be out of scope for this library anyway.
43
+ def self.recalc_basis_vectors()
44
+ b = SyMath.get_variable(:basis)
45
+ g = SyMath.get_variable(:g)
46
+
47
+ brow = b.row(0)
48
+ dim = brow.length
49
+
50
+ dmap = brow.map do |bb|
51
+ "d#{bb.name}".to_sym.to_m('dform')
52
+ end
53
+
54
+ vmap = brow.map do |bb|
55
+ bb.name.to_sym.to_m('vector')
56
+ end
57
+
58
+ # Hash up the order of the basis vectors
59
+ @@basis_order = {}
60
+
61
+ (0..dim - 1).each do |i|
62
+ @@basis_order[brow[i].name.to_sym] = i
63
+ @@basis_order["d#{brow[i].name}".to_sym] = i
64
+ end
65
+
66
+ # Calculate all possible permutations of all possible combinations of
67
+ # the basis vectors (including no vectors).
68
+ @@norm_map = {}
69
+ @@hodge_map = {}
70
+ (0..dim).each do |d|
71
+ (0..dim - 1).to_a.permutation(d).each do |p|
72
+ if p.length == 0
73
+ @@norm_map[1.to_m] = 1.to_m
74
+ @@hodge_map[1.to_m] = dmap.inject(:^)
75
+ next
76
+ end
77
+
78
+ # Hash them to the normalized expression (including the sign).
79
+ # Do this both for vectors and dforms.
80
+ norm = p.sort
81
+ sign = permutation_parity(p)
82
+
83
+ dform = p.map { |i| dmap[i] }.inject(:^)
84
+ vect = p.map { |i| vmap[i] }.inject(:^)
85
+
86
+ dnorm = sign*(norm.map { |i| dmap[i] }.inject(:^))
87
+ vnorm = sign*(norm.map { |i| vmap[i] }.inject(:^))
88
+
89
+ @@norm_map[dform] = dnorm
90
+ @@norm_map[vect] = vnorm
91
+
92
+ # Hash them to their hodge dual
93
+ dual = (0..dim - 1).to_a - norm
94
+ dsign = permutation_parity(p + dual)
95
+
96
+ if dual.length == 0
97
+ hdd = sign
98
+ hdv = sign
99
+ else
100
+ hdd = sign*dsign*(dual.map { |i| dmap[i] }.inject(:^))
101
+ hdv = sign*dsign*(dual.map { |i| vmap[i] }.inject(:^))
102
+ end
103
+
104
+ @@hodge_map[dform] = hdd
105
+ @@hodge_map[vect] = hdv
106
+ end
107
+ end
108
+
109
+ # Calculate the musical isomorphisms. Hash up the mappings both ways.
110
+ flat = (g*SyMath::Matrix.new(dmap).transpose).evaluate.normalize.col(0)
111
+ sharp = (g.inverse*SyMath::Matrix.new(vmap).transpose).evaluate.
112
+ normalize.col(0)
113
+
114
+ @@flat_map = (0..dim - 1).map { |i| [vmap[i], flat[i]] }.to_h
115
+ @@sharp_map = (0..dim - 1).map { |i| [dmap[i], sharp[i]] }.to_h
116
+ end
117
+
118
+ # Return the hodge dual of an expression consisting only of basis vectors
119
+ # or basis dforms
120
+ def self.hodge_dual(exp)
121
+ if !@@hodge_map.key?(exp)
122
+ raise 'No hodge dual for ' + exp.to_s
123
+ end
124
+
125
+ return @@hodge_map[exp]
126
+ end
127
+
128
+ attr_reader :name
129
+ attr_reader :type
130
+
131
+ def initialize(name, t = 'real')
132
+ @type = t.to_t
133
+ super(name, define_symbol: false)
134
+ end
135
+
136
+ def description()
137
+ return "#{name} - free variable"
138
+ end
139
+
140
+ def call(*args)
141
+ return SyMath::Operator.create(self, args.map { |a| a.nil? ? a : a.to_m })
142
+ end
143
+
144
+ def ==(other)
145
+ return false if self.class.name != other.class.name
146
+ return false if @type != other.type
147
+ return @name == other.name
148
+ end
149
+
150
+ def <=>(other)
151
+ if self.class.name != other.class.name
152
+ return super(other)
153
+ end
154
+
155
+ if type.name != other.type.name
156
+ return type.name <=> other.type.name
157
+ end
158
+ # Order basis vectors and basis dforms by basis order
159
+ if type.is_subtype?('vector') or type.is_subtype?('dform')
160
+ bv1 = @@basis_order.key?(@name)
161
+ bv2 = @@basis_order.key?(other.name)
162
+
163
+ if !bv1 and bv2
164
+ # Order basis vectors higher than other vectors
165
+ return 1
166
+ elsif bv1 and !bv2
167
+ # Order basis vectors higher than other vectors
168
+ return -1
169
+ elsif bv1 and bv2
170
+ return @@basis_order[@name] <=> @@basis_order[other.name]
171
+ end
172
+ end
173
+
174
+ return @name.to_s <=> other.name.to_s
175
+ end
176
+
177
+ def is_constant?(vars = nil)
178
+ return false if vars.nil?
179
+ return !(vars.member?(self))
180
+ end
181
+
182
+ # Returns true if variable is a differential form
183
+ def is_d?()
184
+ return @type.is_dform?
185
+ end
186
+
187
+ # Returns variable which differential is based on
188
+ def undiff()
189
+ n = "#{@name}"
190
+ if n[0] == 'd'
191
+ n = n[1..-1]
192
+ end
193
+
194
+ n.to_sym.to_m(:real)
195
+ end
196
+
197
+ def to_d()
198
+ return "d#{@name}".to_sym.to_m(:dform)
199
+ end
200
+
201
+ # Return the vector dual of the dform
202
+ def raise_dform()
203
+ if !@@sharp_map.key?(self)
204
+ raise 'No vector dual for ' + to_s
205
+ end
206
+
207
+ return @@sharp_map[self]
208
+ end
209
+
210
+ # Return the dform dual of the vector
211
+ def lower_vector()
212
+ if !@@flat_map.key?(self)
213
+ raise 'No dform dual for ' + to_s
214
+ end
215
+
216
+ return @@flat_map[self]
217
+ end
218
+
219
+ def variables()
220
+ return [@name]
221
+ end
222
+
223
+ def replace(map)
224
+ if is_d?
225
+ u = undiff
226
+ if map.key?(u)
227
+ return op(:d, map[u].deep_clone)
228
+ else
229
+ return self
230
+ end
231
+ end
232
+
233
+ if map.key?(self)
234
+ return map[self].deep_clone
235
+ else
236
+ return self
237
+ end
238
+ end
239
+
240
+ def to_s()
241
+ if @type.is_dform?
242
+ return SyMath.setting(:d_symbol) + undiff.to_s
243
+ elsif @type.is_vector?
244
+ return @name.to_s + SyMath.setting(:vector_symbol)
245
+ elsif @type.is_covector?
246
+ return @name.to_s + SyMath.setting(:covector_symbol)
247
+ elsif @type.is_subtype?('tensor')
248
+ return @name.to_s + '['.to_s + @type.index_str + ']'.to_s
249
+ else
250
+ return @name.to_s
251
+ end
252
+ end
253
+
254
+ def to_latex()
255
+ if type.is_dform?
256
+ return '\mathrm{d}' + undiff.to_latex
257
+ elsif @type.is_vector?
258
+ return '\vec{'.to_s + @name.to_s + '}'.to_s
259
+ elsif @type.is_covector?
260
+ # What is the best way to denote a covector without using indexes?
261
+ return '\vec{'.to_s + @name.to_s + '}'.to_s
262
+ elsif @type.is_subtype?('tensor')
263
+ return @name.to_s + '['.to_s + @type.index_str + ']'.to_s
264
+ else
265
+ return @name.to_s
266
+ end
267
+ end
268
+
269
+ alias eql? ==
270
+ end
271
+ end
272
+
273
+ class Symbol
274
+ def to_m(type = 'real')
275
+ begin
276
+ # Look up the already defined symbol
277
+ # (we might want to check that it is a constant or variable)
278
+ return SyMath::Definition.get(self)
279
+ rescue
280
+ # Not defined. Define it now.
281
+ return SyMath::Definition::Variable.new(self, type)
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,28 @@
1
+ require 'symath/value'
2
+ require 'symath/definition/operator'
3
+
4
+ module SyMath
5
+ class Definition::Xd < Definition::Operator
6
+ def initialize()
7
+ super(:xd)
8
+ end
9
+
10
+ def description()
11
+ return 'd(f) - exterior derivative of f'
12
+ end
13
+
14
+ def evaluate_call(c)
15
+ vars = SyMath.get_variable(:basis.to_m).row(0)
16
+
17
+ return c.args[0].evaluate.d(vars)
18
+ end
19
+
20
+ def to_latex(args)
21
+ if !args
22
+ args = @args
23
+ end
24
+
25
+ return "\\mathrm{d}(#{args[0].to_latex})"
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,205 @@
1
+ # This class represents a definition of a constant, variable, operator or
2
+ # number. A function or operator can be used in an expression with and without
3
+ # arguments. In the latter case, they behave as a constant of type function or
4
+ # operator.
5
+
6
+ # A special case of a definition is the lambda function, which is a function
7
+ # with no name but a number of arguments, and an expression.
8
+
9
+ module SyMath
10
+ # Empty submodule in which to define methods for function, operator and constant
11
+ # definitions. The submodule can be extended/included into the code of the user
12
+ # code in order to make the math expressions simpler.
13
+ module Definitions
14
+ end
15
+
16
+ class Definition < Value
17
+ attr_reader :name
18
+ attr_reader :description
19
+
20
+ @@definitions = {}
21
+
22
+ @@skip_method_def = {
23
+ :+ => true,
24
+ :- => true,
25
+ :* => true,
26
+ :/ => true,
27
+ :** => true,
28
+ :'=' => true,
29
+ :op => true,
30
+ :fn => true,
31
+ }
32
+
33
+ def self.init_builtin()
34
+ # Create the builtin algebraic functions. The constructor will
35
+ # define the functions so they can be used in expressions.
36
+ SyMath::Definition::Function.new(:+)
37
+ SyMath::Definition::Function.new(:-)
38
+ SyMath::Definition::Function.new(:*)
39
+ SyMath::Definition::Function.new(:/)
40
+ SyMath::Definition::Function.new(:**)
41
+ SyMath::Definition::Function.new(:^)
42
+ SyMath::Definition::Function.new(:'=')
43
+
44
+ SyMath::Definition::Constant.init_builtin
45
+ SyMath::Definition::Function.init_builtin
46
+ SyMath::Definition::Operator.init_builtin
47
+ end
48
+
49
+ def self.get(name)
50
+ if !@@definitions.has_key?(name.to_sym)
51
+ raise "#{name} is not defined."
52
+ end
53
+
54
+ return @@definitions[name.to_sym]
55
+ end
56
+
57
+ def self.define(name, s)
58
+ if @@definitions.has_key?(name.to_sym)
59
+ raise "#{name} is already defined."
60
+ end
61
+
62
+ @@definitions[name.to_sym] = s
63
+
64
+ # Create a method for the definition. Without arguments, the method
65
+ # returns the definition object itself. With arguments, it returns
66
+ # the operator/function applied to a list of arguments.
67
+ if !SyMath::Definitions.private_method_defined?(name) and
68
+ !SyMath::Definitions.method_defined?(name) and
69
+ !@@skip_method_def[name.to_sym]
70
+
71
+ SyMath::Definitions.define_method :"#{name}" do |*args|
72
+ sym = s
73
+ if args.length > 0
74
+ return sym.call(*args)
75
+ else
76
+ return sym
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.undefine(name)
83
+ if !@@definitions.has_key?(name.to_sym)
84
+ raise "#{name} is not undefined."
85
+ end
86
+
87
+ @@definitions.delete(name.to_sym)
88
+ end
89
+
90
+ def self.defined?(name)
91
+ return @@definitions.has_key?(name.to_sym)
92
+ end
93
+
94
+ def self.definitions()
95
+ return @@definitions.values
96
+ end
97
+
98
+ def initialize(name, define_symbol: true, description: nil)
99
+ @name = name.to_sym
100
+
101
+ # Create a method for the definition if it's not a number or lambda
102
+ if define_symbol
103
+ self.class.define(name, self)
104
+ end
105
+
106
+ if description.nil?
107
+ @description = self.to_s
108
+ else
109
+ @description = description
110
+ end
111
+ end
112
+
113
+ def reduce_call(c)
114
+ return c
115
+ end
116
+
117
+ def variables()
118
+ return []
119
+ end
120
+
121
+ def replace(map)
122
+ return self
123
+ end
124
+
125
+ def is_function?()
126
+ return false
127
+ end
128
+
129
+ def is_operator?()
130
+ return false
131
+ end
132
+
133
+ def arity()
134
+ return 0
135
+ end
136
+
137
+ def hash()
138
+ return @name.hash
139
+ end
140
+
141
+ def ==(other)
142
+ o = other.to_m
143
+ return false if self.class.name != o.class.name
144
+ return false if @name.to_s != o.name.to_s
145
+ return true
146
+ end
147
+
148
+ # FIXME: Do we need to redefine it in all subclasses?
149
+ alias eql? ==
150
+
151
+ # FIXME: Identical to operator comparison
152
+ def <=>(other)
153
+ if self.class.name != other.class.name
154
+ return super(other)
155
+ end
156
+
157
+ if name != other.name
158
+ return name.to_s <=> other.name.to_s
159
+ end
160
+
161
+ return 0
162
+ end
163
+
164
+ # is_self_adjoint
165
+ # is_additive
166
+ # is_homogenous
167
+ # is_conjugate_homogenous
168
+ # is_linear (additive + homogenous)
169
+ # is_antilinear (additive + conjugate_homogenous)
170
+
171
+ def is_constant?(vars = nil)
172
+ return true
173
+ end
174
+
175
+ def to_s()
176
+ return @name.to_s
177
+ end
178
+
179
+ def to_latex()
180
+ return "#{name}"
181
+ end
182
+
183
+ def inspect()
184
+ if SyMath.setting(:inspect_to_s)
185
+ return to_s
186
+ else
187
+ return super.inspect
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def definition(name)
194
+ return SyMath::Definition.get(name)
195
+ end
196
+
197
+ def definitions()
198
+ return SyMath::Definition.definitions
199
+ end
200
+
201
+ require 'symath/definition/variable'
202
+ require 'symath/definition/constant'
203
+ require 'symath/definition/number'
204
+ require 'symath/definition/operator'
205
+ require 'symath/definition/function'
@@ -0,0 +1,67 @@
1
+ require 'symath/value'
2
+ require 'symath/operator'
3
+
4
+ module SyMath
5
+ class Equation < Operator
6
+ def initialize(arg1, arg2)
7
+ super('=', [arg1, arg2])
8
+ end
9
+
10
+ def +(other)
11
+ if other.is_a?(SyMath::Equation)
12
+ return eq(self.args[0] + other.args[0], self.args[1] + other.args[1])
13
+ else
14
+ return eq(self.args[0] + other, self.args[1] + other)
15
+ end
16
+ end
17
+
18
+ def -(other)
19
+ if other.is_a?(SyMath::Equation)
20
+ return eq(self.args[0] - other.args[0], self.args[1] - other.args[1])
21
+ else
22
+ return eq(self.args[0] - other, self.args[1] - other)
23
+ end
24
+ end
25
+
26
+ def -@()
27
+ return eq(-self.args[0], -self.args[1])
28
+ end
29
+
30
+ def *(other)
31
+ if other.is_a?(SyMath::Equation)
32
+ raise 'Cannot multiply two equations'
33
+ else
34
+ return eq(self.args[0] * other, self.args[1] * other)
35
+ end
36
+ end
37
+
38
+ def /(other)
39
+ if other.is_a?(SyMath::Equation)
40
+ raise 'Cannot divide by equation'
41
+ else
42
+ return eq(self.args[0] / other, self.args[1] / other)
43
+ end
44
+ end
45
+
46
+ def **(other)
47
+ if other.is_a?(SyMath::Equation)
48
+ raise 'Cannot use equation as exponent'
49
+ else
50
+ return eq(self.args[0]**other, self.args[1]**other)
51
+ end
52
+ end
53
+
54
+ def to_s()
55
+ return "#{@args[0]} = #{@args[1]}"
56
+ end
57
+
58
+ def to_latex()
59
+ return @args[0].to_latex + ' = ' + @args[1].to_latex
60
+ end
61
+ end
62
+ end
63
+
64
+ # Convenience method
65
+ def eq(a, b)
66
+ return SyMath::Equation.new(a, b)
67
+ end