symbo 0.1.0pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'symbo/expression'
4
+ require 'symbo/integer'
5
+ require 'symbo/symbol'
6
+
7
+ module Symbo
8
+ class Function < Expression
9
+ include Symbo
10
+
11
+ using Symbo
12
+
13
+ def name
14
+ @operands[0]
15
+ end
16
+
17
+ def parameters
18
+ @operands[1..-1]
19
+ end
20
+
21
+ # :section: Power Transformation Methods
22
+
23
+ # べき乗の低
24
+ #
25
+ # Function(:f, :x).base # => Function(:f, :x)
26
+ def base
27
+ dup
28
+ end
29
+
30
+ # べき指数
31
+ #
32
+ # Function(:f, :x).exponent # => 1
33
+ def exponent
34
+ 1
35
+ end
36
+
37
+ # :section: Basic Distributive Transformation Methods
38
+
39
+ # 同類項の項部分
40
+ #
41
+ # Function(:f, :x).term # => Product(Function(:f, :x))
42
+ def term
43
+ Product[self]
44
+ end
45
+
46
+ # 同類項の定数部分
47
+ #
48
+ # Function(:f, :x).const # => 1
49
+ def const
50
+ 1
51
+ end
52
+
53
+ # :section: Order Relation Methods
54
+
55
+ # 交換法則によるオペランド並べ替えに使う順序関係
56
+ #
57
+ # - 相手が関数の場合
58
+ # 関数名が異なる場合、関数名で順序を決定。
59
+ #
60
+ # Function(:f, :x).compare(Function(:g, :x)) # => true
61
+ #
62
+ # 関数名が同じの場合、関数の最左のオペランドから順に compare していき、
63
+ # 異なるものがあればそれで順序を決定する。
64
+ #
65
+ # Function(:f, :x).compare(Function(:f, :y)) # => true
66
+ #
67
+ # どちらかのオペランドがなくなれば、短いほうが左側。
68
+ #
69
+ # Function(:g, :x).compare(Function(:g, :x, :y)) # => true
70
+ #
71
+ # - シンボルの場合
72
+ # 関数名とシンボルが等しい場合 false
73
+ #
74
+ # Function(:f, :x).compare(:f) # => false
75
+ #
76
+ # 異なる場合、関数名とシンボルで比較
77
+ #
78
+ # Function(:f, :x).compare(:g) # => true
79
+ #
80
+ # - それ以外の場合
81
+ # 次のルールで比較
82
+ #
83
+ # !other.compare(self)
84
+ #
85
+ # rubocop:disable Metrics/PerceivedComplexity
86
+ # rubocop:disable Metrics/CyclomaticComplexity
87
+ # rubocop:disable Metrics/AbcSize
88
+ def compare(other)
89
+ case other
90
+ when Function
91
+ if name != other.name
92
+ name.compare other.name
93
+ else
94
+ return parameters.first.compare other.parameters.first if parameters.first != other.parameters.first
95
+
96
+ m = parameters.length
97
+ n = other.parameters.length
98
+ 0.upto([m, n].min - 2) do |j|
99
+ return parameters[j + 1].compare(other.parameters[j + 1]) if (parameters[j] == other.parameters[j]) && (parameters[j + 1] != other.parameters[j + 1])
100
+ end
101
+
102
+ m.compare(n)
103
+ end
104
+ when Symbol
105
+ if name == other
106
+ false
107
+ else
108
+ name.compare other
109
+ end
110
+ else
111
+ !other.compare(self)
112
+ end
113
+ end
114
+ # rubocop:enable Metrics/PerceivedComplexity
115
+ # rubocop:enable Metrics/CyclomaticComplexity
116
+ # rubocop:enable Metrics/AbcSize
117
+ end
118
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'symbo/algebraic_operators'
4
+ require 'symbo/constant'
5
+ require 'symbo/expression_type'
6
+
7
+ # Symbolic computation
8
+ module Symbo
9
+ # Integer refinements
10
+ refine Integer do
11
+ alias_method :mult, :*
12
+ alias_method :div, :/
13
+
14
+ include AlgebraicOperators
15
+ include ExpressionType
16
+ include Constant
17
+
18
+ def simplify
19
+ self
20
+ end
21
+
22
+ # :section: Power Transformation Methods
23
+
24
+ # べき乗の低
25
+ #
26
+ # 1.base # => UNDEFINED
27
+ def base
28
+ UNDEFINED
29
+ end
30
+
31
+ # べき指数
32
+ #
33
+ # 1.exponent # => UNDEFINED
34
+ def exponent
35
+ UNDEFINED
36
+ end
37
+
38
+ # :section: Basic Distributive Transformation Methods
39
+
40
+ # 同類項の項部分
41
+ #
42
+ # 1.term # => UNDEFINED
43
+ def term
44
+ UNDEFINED
45
+ end
46
+
47
+ # 同類項の定数部分
48
+ #
49
+ # 1.const # => UNDEFINED
50
+ def const
51
+ UNDEFINED
52
+ end
53
+
54
+ # :section: Order Relation Methods
55
+
56
+ # 交換法則によるオペランド並べ替えに使う順序関係
57
+ #
58
+ # - 相手が定数の場合
59
+ # 大小関係で順序を決定
60
+ #
61
+ # 2.compare(4) # => true
62
+ # 2.compare(5/2) # => true
63
+ #
64
+ # - それ以外の場合
65
+ # 常に true
66
+ #
67
+ # 2.compare(:x + :y) # => true
68
+ # 2.compare(:x * :y) # => true
69
+ # 2.compare(2**:x) # => true
70
+ # 2.compare(Factorial(2)) # => true
71
+ # 2.compare(Function(:f, :x)) # => true
72
+ def compare(other)
73
+ case other
74
+ when Integer
75
+ self < other
76
+ when Fraction
77
+ self < other.rational
78
+ else
79
+ true
80
+ end
81
+ end
82
+
83
+ # :section:
84
+
85
+ def numerator
86
+ self
87
+ end
88
+
89
+ def denominator
90
+ 1
91
+ end
92
+
93
+ def evaluate
94
+ self
95
+ end
96
+
97
+ def simplify_rational_number
98
+ self
99
+ end
100
+
101
+ def simplify_rne_rec
102
+ self
103
+ end
104
+ end
105
+ end
106
+
107
+ # Matrix などの中で使われる Integer#+ などをハイジャック
108
+ class Integer
109
+ include Symbo::Constant
110
+ include Symbo::ExpressionType
111
+
112
+ alias plus +
113
+ alias mult *
114
+
115
+ def +(other)
116
+ plus other
117
+ rescue TypeError
118
+ Symbo::Sum[self, other]
119
+ end
120
+
121
+ def *(other)
122
+ mult other
123
+ rescue TypeError
124
+ Symbo::Product[self, other]
125
+ end
126
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'matrix'
4
+
5
+ module Symbo
6
+ # シンボリック演算を使う行列
7
+ refine Matrix do
8
+ alias_method :mult, :*
9
+
10
+ def *(other)
11
+ if other.respond_to?(:to_matrix)
12
+ (mult other.to_matrix).simplify
13
+ else
14
+ mult other
15
+ end
16
+ end
17
+
18
+ def simplify
19
+ map(&:simplify)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Symbo
4
+ module Mergeable
5
+ private
6
+
7
+ # rubocop:disable Metrics/PerceivedComplexity
8
+ # rubocop:disable Metrics/CyclomaticComplexity
9
+ def merge(p, q) # rubocop:disable Naming/MethodParameterName
10
+ return p if q.empty?
11
+ return q if p.empty?
12
+
13
+ p1 = p[0]
14
+ q1 = q[0]
15
+ h = simplify_rec([p1, q1])
16
+ case h
17
+ in []
18
+ merge p[1..-1], q[1..-1]
19
+ in [_]
20
+ h + merge(p[1..-1], q[1..-1])
21
+ in ^p1, ^q1
22
+ [p1] + merge(p[1..-1], q)
23
+ in ^q1, ^p1
24
+ [q1] + merge(p, q[1..-1])
25
+ end
26
+ end
27
+ # rubocop:enable Metrics/PerceivedComplexity
28
+ # rubocop:enable Metrics/CyclomaticComplexity
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Symbo
4
+ PI = :π
5
+ end
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'symbo/expression'
4
+
5
+ module Symbo
6
+ class Power < Expression
7
+ using Symbo
8
+
9
+ # :section: Power Transformation Methods
10
+
11
+ # べき乗の低
12
+ #
13
+ # (:x**2).base # => :x
14
+ def base
15
+ @operands[0]
16
+ end
17
+
18
+ # べき指数
19
+ #
20
+ # (:x**2).exponent # => 2
21
+ def exponent
22
+ @operands[1]
23
+ end
24
+
25
+ # :section: Basic Distributive Transformation Methods
26
+
27
+ # 同類項の項部分
28
+ #
29
+ # (:x**2).term # => Product(:x**2)
30
+ def term
31
+ Product[self]
32
+ end
33
+
34
+ # 同類項の定数部分
35
+ #
36
+ # (:x**2).const # => 1
37
+ def const
38
+ 1
39
+ end
40
+
41
+ # :section: Order Relation Methods
42
+
43
+ # 交換法則によるオペランド並べ替えに使う順序関係
44
+ #
45
+ # - 相手がべき乗の場合
46
+ # 底が異なる場合、底同士で順序を決める。
47
+ # 底が等しい場合、べき指数同士で順序を決める。
48
+ #
49
+ # ((1 + :x)**3).compare((1 + :y)**2) # => true
50
+ # ((1 + :x)**2).compare((1 + :x)**3) # => true
51
+ #
52
+ # - 和、階乗、関数、シンボルの場合
53
+ # 相手を 1 乗のべき乗に変換して比較
54
+ #
55
+ # ((1 + :x)**3).compare(1 + :y) # => true
56
+ #
57
+ # - それ以外の場合
58
+ # 次のルールで比較
59
+ #
60
+ # !other.compare(self)
61
+ def compare(other)
62
+ case other
63
+ when Power
64
+ return base.compare(other.base) if base != other.base
65
+
66
+ exponent.compare other.exponent
67
+ when Sum, Factorial, Function, Symbol
68
+ compare Power[other, 1]
69
+ else
70
+ !other.compare(self)
71
+ end
72
+ end
73
+
74
+ # :section: Evaluation Methods
75
+
76
+ # rubocop:disable Metrics/PerceivedComplexity
77
+ # rubocop:disable Metrics/CyclomaticComplexity
78
+ # rubocop:disable Metrics/AbcSize
79
+ def evaluate
80
+ raise unless base.constant?
81
+ raise unless exponent.integer?
82
+
83
+ if base.numerator != 0
84
+ if exponent.positive?
85
+ Product[Power[base, exponent - 1].evaluate, base].evaluate
86
+ elsif exponent.zero?
87
+ 1
88
+ elsif exponent == -1
89
+ Fraction[1, base]
90
+ elsif exponent < -1
91
+ raise NotImplementedError
92
+ end
93
+ elsif base.numerator.zero?
94
+ if n >= 1
95
+ 0
96
+ else
97
+ UNDEFINED
98
+ end
99
+ end
100
+ end
101
+ # rubocop:enable Metrics/PerceivedComplexity
102
+ # rubocop:enable Metrics/CyclomaticComplexity
103
+ # rubocop:enable Metrics/AbcSize
104
+
105
+ def to_s
106
+ "#{base}^(#{exponent})"
107
+ end
108
+
109
+ protected
110
+
111
+ # :section: Simplification Methods
112
+
113
+ # rubocop:disable Metrics/PerceivedComplexity
114
+ # rubocop:disable Metrics/CyclomaticComplexity
115
+ # rubocop:disable Metrics/AbcSize
116
+ def _simplify
117
+ return UNDEFINED if base == UNDEFINED || exponent == UNDEFINED
118
+
119
+ if base.zero?
120
+ return 1 if exponent.positive? && exponent.constant?
121
+
122
+ return UNDEFINED
123
+ end
124
+
125
+ return 1 if base == 1
126
+ return simplify_integer_power if exponent.integer?
127
+ return simplify_eulers_formula if base == E
128
+
129
+ self
130
+ end
131
+ # rubocop:enable Metrics/PerceivedComplexity
132
+ # rubocop:enable Metrics/CyclomaticComplexity
133
+ # rubocop:enable Metrics/AbcSize
134
+
135
+ # rubocop:disable Metrics/CyclomaticComplexity
136
+ # rubocop:disable Metrics/AbcSize
137
+ def simplify_integer_power
138
+ return Power[base, exponent].simplify_rne if base.constant?
139
+ return 1 if exponent.zero?
140
+ return base if exponent == 1
141
+
142
+ case base
143
+ when Power
144
+ r = base.operands[0]
145
+ s = base.operands[1]
146
+ p = Product.new(s, exponent).simplify
147
+
148
+ if p.integer?
149
+ Power[r, p].simplify_integer_power
150
+ else
151
+ Power[r, p]
152
+ end
153
+ when Product
154
+ r = base.operands.map { |each| Power[each, exponent].simplify_integer_power }
155
+ Product[*r].simplify
156
+ else
157
+ self
158
+ end
159
+ end
160
+ # rubocop:enable Metrics/CyclomaticComplexity
161
+ # rubocop:enable Metrics/AbcSize
162
+
163
+ # e^ix = cos(x) + sin(x)
164
+ #
165
+ # rubocop:disable Metrics/PerceivedComplexity
166
+ # rubocop:disable Metrics/CyclomaticComplexity
167
+ # rubocop:disable Metrics/AbcSize
168
+ def simplify_eulers_formula
169
+ if exponent.product? &&
170
+ exponent.operand(0).is_a?(Complex) && exponent.operand(0).real.zero? &&
171
+ exponent.operand(1) == PI
172
+ # eg e^{ki * π}
173
+ x = Product[exponent.operand(0).imag, exponent.operand(1)]
174
+ (Cos[x] + 1i * Sin[x]).simplify
175
+ elsif exponent.product? &&
176
+ exponent.operand(0).fraction? && exponent.operand(0).operand(0).is_a?(Complex) && exponent.operand(0).operand(0).real.zero? &&
177
+ exponent.operand(1) == PI
178
+ # eg e^{(k/n)i * π}
179
+ x = Product[Fraction[exponent.operand(0).operand(0).imag, exponent.operand(0).operand(1)], exponent.operand(1)]
180
+ (Cos[x] + 1i * Sin[x]).simplify
181
+ else
182
+ self
183
+ end
184
+ end
185
+ # rubocop:enable Metrics/PerceivedComplexity
186
+ # rubocop:enable Metrics/CyclomaticComplexity
187
+ # rubocop:enable Metrics/AbcSize
188
+
189
+ def simplify_rne_rec
190
+ v = base.simplify_rne_rec
191
+ if v == UNDEFINED
192
+ UNDEFINED
193
+ else
194
+ Power[v, exponent].evaluate
195
+ end
196
+ end
197
+ end
198
+ end