symbo 0.1.0pre

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.
@@ -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