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