Mr.CAS 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +2 -0
- data/lib/Mr.CAS.rb +73 -0
- data/lib/Mr.CAS/auto-diff.rb +129 -0
- data/lib/Mr.CAS/c-opt.rb +225 -0
- data/lib/Mr.CAS/c.rb +126 -0
- data/lib/Mr.CAS/graphviz.rb +132 -0
- data/lib/Mr.CAS/latex.rb +68 -0
- data/lib/Mr.CAS/matlab.rb +81 -0
- data/lib/functions/fnc-base.rb +515 -0
- data/lib/functions/fnc-box-conditions.rb +319 -0
- data/lib/functions/fnc-conditions.rb +365 -0
- data/lib/functions/fnc-piecewise.rb +186 -0
- data/lib/functions/fnc-prod.rb +102 -0
- data/lib/functions/fnc-sum.rb +151 -0
- data/lib/functions/fnc-trig.rb +489 -0
- data/lib/functions/fnc-trsc.rb +192 -0
- data/lib/numbers/constants.rb +350 -0
- data/lib/numbers/functions.rb +194 -0
- data/lib/numbers/variables.rb +202 -0
- data/lib/operators/bary-op.rb +186 -0
- data/lib/operators/nary-op.rb +232 -0
- data/lib/operators/op.rb +285 -0
- data/lib/overloading/fixnum.rb +61 -0
- data/lib/overloading/float.rb +61 -0
- data/lib/version.rb +12 -0
- metadata +88 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# ___ _ _
|
5
|
+
# | _ (_)___ __ _____ __ _(_)___ ___
|
6
|
+
# | _/ / -_) _/ -_) V V / (_-</ -_)
|
7
|
+
# |_| |_\___\__\___|\_/\_/|_/__/\___|
|
8
|
+
|
9
|
+
##
|
10
|
+
# Piecewise function. The function returns when called a result
|
11
|
+
# that dependes upon the evaluation of a condition. In practice:
|
12
|
+
#
|
13
|
+
# ```
|
14
|
+
# /
|
15
|
+
# | f(x) if condition(x) is True
|
16
|
+
# <
|
17
|
+
# | g(x) otherwise
|
18
|
+
# \
|
19
|
+
# ```
|
20
|
+
#
|
21
|
+
# From this class other classes will inherit like `CAS::Max` and `CAS::Min` classes
|
22
|
+
class Piecewise < CAS::BinaryOp
|
23
|
+
attr_reader :condition
|
24
|
+
|
25
|
+
# Initialize a new piecewise function. It requires first the function
|
26
|
+
# that returns when condition is true, than the function when condition is
|
27
|
+
# false, and finally the condition that must be of class `CAS::Condition`
|
28
|
+
#
|
29
|
+
# * **argument**: `CAS::Op` first function
|
30
|
+
# * **argument**: `CAS::Op` second function
|
31
|
+
# * **argument**: `CAS::Condition` evaluated condition
|
32
|
+
# * **returns**: `CAS::Piecewise` new instance
|
33
|
+
def initialize(x, y, condition)
|
34
|
+
CAS::Help.assert(condition, CAS::Condition)
|
35
|
+
|
36
|
+
super(x, y)
|
37
|
+
@condition = condition
|
38
|
+
end
|
39
|
+
|
40
|
+
# Derivative of a function is performed as derivative of the two internal functions
|
41
|
+
# while condition is unchanged
|
42
|
+
#
|
43
|
+
# warning:: Piecewise functions are in general not differentiable. Thus differentiability
|
44
|
+
# is left to the user
|
45
|
+
#
|
46
|
+
# ```
|
47
|
+
# / /
|
48
|
+
# d | f(x) if condition(x) is True | f'(x) if condition(x) is True
|
49
|
+
# -- < = <
|
50
|
+
# dx | g(x) otherwise | g'(x) otherwise
|
51
|
+
# \ \
|
52
|
+
# ```
|
53
|
+
#
|
54
|
+
# * **argument**: `CAS::Op` argument of derivative
|
55
|
+
# * **returns**: `CAS::Piecewise` with derivated functions and unchanged condition
|
56
|
+
def diff(v)
|
57
|
+
CAS::Help.assert(v, CAS::Op)
|
58
|
+
return CAS::Piecewise.new(@x.diff(v).simplify, @y.diff(v).simplify, @condition)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Executes the condition. If it is `true` it returns the first function,
|
62
|
+
# else it returns the value of the second function.
|
63
|
+
#
|
64
|
+
# * **argument**: `Hash` with value tables
|
65
|
+
# * **returns**: `Numeric` the result of the call
|
66
|
+
def call(fd)
|
67
|
+
CAS::Help.assert(fd, Hash)
|
68
|
+
(@condition.call(fd) ? @x.call(fd) : @y.call(fd))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if two `CAS::Piecewise` are equal. Checks equality on all functions+
|
72
|
+
# and conditions
|
73
|
+
#
|
74
|
+
# * **argument**: `CAS::Op` to be checked against
|
75
|
+
# * **returns**: `TrueClass` or `FalseClass`
|
76
|
+
def ==(op)
|
77
|
+
CAS::Help.assert(op, CAS::Op)
|
78
|
+
if self.class != op.class
|
79
|
+
return false
|
80
|
+
else
|
81
|
+
return ((@x == op.x) and (@y == op.y) and (@condition == op.condition))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Convert the piecewise funtion to a String of Ruby code
|
86
|
+
#
|
87
|
+
# * **returns**: `String` of code
|
88
|
+
def to_code
|
89
|
+
"(#{@condition.to_code} ? (#{@x.to_code}) : (#{@y.to_code}))"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Convert the piecewise function into a String
|
93
|
+
#
|
94
|
+
# * **returns**: `String`
|
95
|
+
def to_s
|
96
|
+
"(#{@condition} ? #{@x} : #{@y})"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Convert piecewise function into LaTeX representation
|
100
|
+
#
|
101
|
+
# * **returns**: `String` of LaTeX code
|
102
|
+
def to_latex
|
103
|
+
"\\left\\{ \\begin{array}{lr} #{@x.to_latex} & #{@condition.to_latex} \\\\ #{@y.to_latex} \\end{array} \\right."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# __ __ _ __ __
|
108
|
+
# | \/ (_)_ _ | \/ |__ ___ __
|
109
|
+
# | |\/| | | ' \| |\/| / _` \ \ /
|
110
|
+
# |_| |_|_|_||_|_| |_\__,_/_\_\
|
111
|
+
|
112
|
+
##
|
113
|
+
# Class MinMax is an intermediate class for Min and Max functions. It contains shared code
|
114
|
+
# and methods
|
115
|
+
class MinMax < CAS::Piecewise
|
116
|
+
# Convert MinMax function into LaTeX representation
|
117
|
+
#
|
118
|
+
# * **returns**: `String` of LaTeX code
|
119
|
+
def to_latex
|
120
|
+
"\\mathrm{#{@type}}\\left( \\begin{array}{c} #{@x.to_latex} \\\\ #{@y.to_latex} \\end{array} \\right)"
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns a string representation for the current operation
|
124
|
+
#
|
125
|
+
# * **returns**: `String`
|
126
|
+
def to_s
|
127
|
+
"#{@type}(#{@x}, #{@y})"
|
128
|
+
end
|
129
|
+
end # MinMax
|
130
|
+
|
131
|
+
# __ __
|
132
|
+
# | \/ |__ ___ __
|
133
|
+
# | |\/| / _` \ \ /
|
134
|
+
# |_| |_\__,_/_\_\
|
135
|
+
|
136
|
+
# Max class represent a piecewise in which the condition is `f(x) ≥ g(x)`. Derivate a `CAS::Max`
|
137
|
+
# will return a `CAS::Piecewise` (since condition will not depend anymore on object functions)
|
138
|
+
class Max < CAS::Piecewise
|
139
|
+
# To initialize `CAS::Max` only the two functions are necessary. The condition is automatically
|
140
|
+
# generated
|
141
|
+
#
|
142
|
+
# * **argument**: `CAS::Op` first function
|
143
|
+
# * **argument**: `CAS::Op` second function
|
144
|
+
def initialize(x, y)
|
145
|
+
super(x, y, CAS::greater_equal(x, y))
|
146
|
+
@type = "max"
|
147
|
+
end
|
148
|
+
end # Max
|
149
|
+
|
150
|
+
# __ __ _
|
151
|
+
# | \/ (_)_ _
|
152
|
+
# | |\/| | | ' \
|
153
|
+
# |_| |_|_|_||_|
|
154
|
+
|
155
|
+
# Min class represent a piecewise in which the condition is `f(x) ≤ g(x)`. Derivate a `CAS::Min`
|
156
|
+
# will return a `CAS::Piecewise` (since condition will not depend anymore on object functions)
|
157
|
+
class Min < CAS::Piecewise
|
158
|
+
# To initialize `CAS::Min` only the two functions are necessary. The condition is automatically
|
159
|
+
# generated
|
160
|
+
#
|
161
|
+
# * **argument**: `CAS::Op` first function
|
162
|
+
# * **argument**: `CAS::Op` second function
|
163
|
+
def initialize(x, y)
|
164
|
+
super(x, y, CAS::smaller_equal(x, y))
|
165
|
+
@type = "min"
|
166
|
+
end
|
167
|
+
end # Min
|
168
|
+
|
169
|
+
# Shortcut for `CAS::Max` initializer
|
170
|
+
#
|
171
|
+
# * **argument**: `CAS::Op` left function
|
172
|
+
# * **argument**: `CAS::Op` right function
|
173
|
+
# * **returns**: `CAS::Max` new instance
|
174
|
+
def self.max(x, y)
|
175
|
+
CAS::Max.new(x, y)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Shortcut for `CAS::Min` initializer
|
179
|
+
#
|
180
|
+
# * **argument**: `CAS::Op` left function
|
181
|
+
# * **argument**: `CAS::Op` right function
|
182
|
+
# * **returns**: `CAS::Min` new instance
|
183
|
+
def self.min(x, y)
|
184
|
+
CAS::Min.new(x, y, CAS::smaller_equal(x, y))
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
|
5
|
+
# ___ _
|
6
|
+
# | _ \_ _ ___ __| |
|
7
|
+
# | _/ '_/ _ \/ _` |
|
8
|
+
# |_| |_| \___/\__,_|
|
9
|
+
|
10
|
+
##
|
11
|
+
# Product class. Performs the product between two elements.
|
12
|
+
# This class will be soon modified as an n-ary operator.
|
13
|
+
class Prod < CAS::NaryOp
|
14
|
+
# The new element of a sum accumulates inside the
|
15
|
+
# vector that holds the elements
|
16
|
+
def *(op)
|
17
|
+
CAS::Help.assert(op, CAS::Op)
|
18
|
+
@x << op
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
# Performs the product between two `CAS::Op`
|
23
|
+
#
|
24
|
+
# ```
|
25
|
+
# d
|
26
|
+
# ---- (f(x) * g(x) * h(x)) = f'(x) * g(x) * h(x) +
|
27
|
+
# dx
|
28
|
+
# + f(x) * g'(x) * h(x) +
|
29
|
+
#
|
30
|
+
# + f(x) * g(x) * h'(x)
|
31
|
+
# ```
|
32
|
+
#
|
33
|
+
# * **argument**: `CAS::Op` argument of derivative
|
34
|
+
# * **returns**: `CAS::Op` derivative
|
35
|
+
def diff(v)
|
36
|
+
xdiff = @x.map { |y| y.diff(v) }
|
37
|
+
|
38
|
+
xdiff.each_with_index { |y, i|
|
39
|
+
xdiff[i] = y * CAS::Prod.new(@x[0...i] + @x[(i + 1)..-1])
|
40
|
+
}
|
41
|
+
|
42
|
+
return CAS::Sum.new(xdiff)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
46
|
+
# or `Float` (depends upon promotions).
|
47
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
48
|
+
# as keys, and a `Numeric` as a value. In this case it will call
|
49
|
+
# the `Fixnum#overloaded_plus`, that is the old plus function.
|
50
|
+
#
|
51
|
+
# * **argument**: `Hash` with feed dictionary
|
52
|
+
# * **returns**: `Numeric`
|
53
|
+
def call(f)
|
54
|
+
CAS::Help.assert(f, Hash)
|
55
|
+
|
56
|
+
return @x.inject { |p, y| p = p.overloaded_mul(y.call(f)) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convert expression to string
|
60
|
+
#
|
61
|
+
# * **returns**: `String` to print on screen
|
62
|
+
def to_s
|
63
|
+
"(#{@x.map(&:to_s).join(" * ")})"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Same as `CAS::Op`
|
67
|
+
#
|
68
|
+
# Simplifcation engine supports:
|
69
|
+
#
|
70
|
+
# * x * 0 = x * y = 0
|
71
|
+
# * 1 * y = y
|
72
|
+
# * x * 1 = x
|
73
|
+
# * x * x = x²
|
74
|
+
# * a * b = c (constants reduction)
|
75
|
+
#
|
76
|
+
# * **returns**: `CAS::Op` simplified version
|
77
|
+
def simplify
|
78
|
+
super
|
79
|
+
return CAS::Zero if @x.include? CAS::Zero
|
80
|
+
@x = @x - [CAS::One]
|
81
|
+
return CAS::One if @x.size == 0
|
82
|
+
return @x[0] if @x.size == 1
|
83
|
+
|
84
|
+
@x = self.__reduce_constants(@x) do |cs, xs|
|
85
|
+
[cs.inject { |t, c| t *= c.call({}) }] + xs
|
86
|
+
end
|
87
|
+
|
88
|
+
@x = self.__reduce_multeplicity(@x) do |op, count|
|
89
|
+
count > 1 ? (op ** count) : op
|
90
|
+
end
|
91
|
+
return self
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
95
|
+
#
|
96
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
97
|
+
def to_code
|
98
|
+
"(#{@x.map(&:to_code).join(" * ")})"
|
99
|
+
end
|
100
|
+
end # Prod
|
101
|
+
CAS::Prod.init_simplify_dict
|
102
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module CAS
|
2
|
+
# ___
|
3
|
+
# / __|_ _ _ __
|
4
|
+
# \__ \ || | ' \
|
5
|
+
# |___/\_,_|_|_|_|
|
6
|
+
|
7
|
+
##
|
8
|
+
# **Sum basic operation**. As for now it is implemented as a simple
|
9
|
+
# binary operation. It will be implemented as n-ary op.
|
10
|
+
class Sum < CAS::NaryOp
|
11
|
+
# Performs the sum between arbitrary number of `CAS::Op`
|
12
|
+
#
|
13
|
+
# ```
|
14
|
+
# d
|
15
|
+
# ---- (f(x) + g(x) + h(x)) = f'(x) + g'(x) + h'(x)
|
16
|
+
# dx
|
17
|
+
# ```
|
18
|
+
#
|
19
|
+
# * **argument**: `CAS::Op` argument of derivative
|
20
|
+
# * **returns**: `CAS::Op` derivative
|
21
|
+
def diff(v)
|
22
|
+
@x.map { |x| x.diff(v) }.inject { |sum_x, dx| sum_x += dx }
|
23
|
+
end
|
24
|
+
|
25
|
+
# The added element of a sum accumulates inside the
|
26
|
+
# vector that holds the elements
|
27
|
+
def +(op)
|
28
|
+
CAS::Help.assert(op, CAS::Op)
|
29
|
+
@x << op
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
34
|
+
# or `Float` (depends upon promotions).
|
35
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
36
|
+
# as keys, and a `Numeric` as a value. In this case it will call
|
37
|
+
# the `Fixnum#overloaded_plus`, that is the old plus function.
|
38
|
+
#
|
39
|
+
# * **argument**: `Hash` with feed dictionary
|
40
|
+
# * **returns**: `Numeric`
|
41
|
+
def call(f)
|
42
|
+
CAS::Help.assert(f, Hash)
|
43
|
+
return @x.inject { |val, x_i| val += x_i.call(f) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Convert expression to string
|
47
|
+
#
|
48
|
+
# * **returns**: `String` to print on screen
|
49
|
+
def to_s
|
50
|
+
"(#{@x.map(&:to_s).join(" + ")})"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Same as `CAS::Op`
|
54
|
+
#
|
55
|
+
# Simplifcation engine supports:
|
56
|
+
#
|
57
|
+
# * x + 0 = x
|
58
|
+
# * 0 + y = y
|
59
|
+
# * x + x = 2 x
|
60
|
+
# * x + (-x) = 0
|
61
|
+
# * x + (-y) = x - y
|
62
|
+
# * 1 + 2 = 3 (constants reduction)
|
63
|
+
#
|
64
|
+
# * **returns**: `CAS::Op` simplified version
|
65
|
+
def simplify
|
66
|
+
super
|
67
|
+
return @x[0] if @x.size == 1
|
68
|
+
|
69
|
+
# return CAS::Zero if @x == -@y or -@x == @y
|
70
|
+
# return (@x - @y.x) if @y.is_a? CAS::Invert
|
71
|
+
# return CAS.const(self.call({})) if (@x.is_a? CAS::Constant and @y.is_a? CAS::Constant)
|
72
|
+
# Removing Zeros
|
73
|
+
@x = @x - [CAS::Zero]
|
74
|
+
return CAS::Zero if @x.size == 0
|
75
|
+
# Reduce constants
|
76
|
+
@x = self.__reduce_constants(@x) do |cs, xs|
|
77
|
+
xs + [cs.inject { |t, c| t += c.call({}) }]
|
78
|
+
end
|
79
|
+
# Multeplicity and associativity executed
|
80
|
+
return self.reduce_associativity
|
81
|
+
end
|
82
|
+
|
83
|
+
# Reduces from an associative point of view, by a segregation
|
84
|
+
# of "negative" and positive elements. Negatives comes from
|
85
|
+
# Diff operations and Invert operations. All the others are considered
|
86
|
+
# positive. This function implements an internal heuristic. Should
|
87
|
+
# not be used outside
|
88
|
+
#
|
89
|
+
# * **returns**: A `CAS::Diff` or a `CAS::Sum`
|
90
|
+
def reduce_associativity
|
91
|
+
pos, neg = [], []
|
92
|
+
|
93
|
+
@x.each do |x_el|
|
94
|
+
case x_el
|
95
|
+
when CAS::Invert
|
96
|
+
neg << x_el.x
|
97
|
+
when CAS::Diff
|
98
|
+
pos << x_el.x
|
99
|
+
neg << x_el.y
|
100
|
+
else
|
101
|
+
pos << x_el
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
pos, neg = self.reduce_associativity_array pos, neg
|
106
|
+
pos = self.__reduce_multeplicity(pos)
|
107
|
+
neg = self.__reduce_multeplicity neg
|
108
|
+
|
109
|
+
# TODO : Add rules for simplifications
|
110
|
+
left, right = nil, nil
|
111
|
+
left = CAS::Sum.new(pos) if pos.size > 1
|
112
|
+
left = pos[0] if pos.size == 1
|
113
|
+
right = CAS::Sum.new(neg) if neg.size > 1
|
114
|
+
right = neg[0] if neg.size == 1
|
115
|
+
|
116
|
+
return CAS::Zero unless left || right
|
117
|
+
return left unless right
|
118
|
+
return -right unless left
|
119
|
+
return left - right
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reduce the positive and the negative associative part of
|
123
|
+
# the sum to perform the symbolic difference. Does not take into account
|
124
|
+
# multeplicity
|
125
|
+
#
|
126
|
+
# * **requires**: positive `Array`
|
127
|
+
# * **requires**: negative `Array`
|
128
|
+
# * **returns**: positive, reduced `Array` and negative `Array`
|
129
|
+
def reduce_associativity_array(p_old, n_old)
|
130
|
+
p_del, n_del = [], []
|
131
|
+
p_old.each do |p|
|
132
|
+
n_old.each do |n|
|
133
|
+
if p == n
|
134
|
+
p_del << p
|
135
|
+
n_del << n
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
return (p_old - p_del), (n_old - n_del)
|
141
|
+
end
|
142
|
+
|
143
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
144
|
+
#
|
145
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
146
|
+
def to_code
|
147
|
+
"(#{@x.map(&:to_code).join(" + ")})"
|
148
|
+
end
|
149
|
+
end # Sum
|
150
|
+
CAS::Sum.init_simplify_dict
|
151
|
+
end
|
@@ -0,0 +1,489 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
# ___ _
|
5
|
+
# / __(_)_ _
|
6
|
+
# \__ \ | ' \
|
7
|
+
# |___/_|_||_|
|
8
|
+
|
9
|
+
##
|
10
|
+
# Representation for the `sin(x)` function. It is implemented
|
11
|
+
# as a `CAS::Op`
|
12
|
+
class Sin < CAS::Op
|
13
|
+
# Return the derivative of the `sin(x)` function using the chain
|
14
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
15
|
+
# with respect to functions.
|
16
|
+
#
|
17
|
+
# ```
|
18
|
+
# d
|
19
|
+
# -- sin(f(x)) = f'(x) cos(fx)
|
20
|
+
# dx
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# * **argument**: `CAS::Op` object of the derivative
|
24
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
25
|
+
def diff(v)
|
26
|
+
if @x.depend? v
|
27
|
+
return @x.diff(v) * CAS.cos(@x)
|
28
|
+
else
|
29
|
+
return CAS::Zero
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
34
|
+
# or `Float` (depends upon promotions).
|
35
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
36
|
+
# as keys, and a `Numeric` as a value
|
37
|
+
#
|
38
|
+
# * **argument**: `Hash` with feed dictionary
|
39
|
+
# * **returns**: `Numeric`
|
40
|
+
def call(f)
|
41
|
+
CAS::Help.assert(f, Hash)
|
42
|
+
Math::sin(@x.call(f))
|
43
|
+
end
|
44
|
+
|
45
|
+
# Convert expression to string
|
46
|
+
#
|
47
|
+
# * **returns**: `String` to print on screen
|
48
|
+
def to_s
|
49
|
+
"sin(#{@x})"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Simplification callback. It simplify the subgraph of each node
|
53
|
+
# until all possible simplification are performed (thus the execution
|
54
|
+
# time is not deterministic).
|
55
|
+
#
|
56
|
+
# * **returns**: `CAS::Op` simplified version
|
57
|
+
def simplify
|
58
|
+
super
|
59
|
+
return @x.x if @x.is_a? CAS::Asin
|
60
|
+
return self.simplify_dictionary
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.init_simplify_dict
|
64
|
+
@simplify_dict = {
|
65
|
+
CAS::Zero => CAS::Zero,
|
66
|
+
CAS::Pi => CAS::Zero,
|
67
|
+
CAS::Pi/2 => CAS::One
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
72
|
+
#
|
73
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
74
|
+
def to_code
|
75
|
+
"Math::sin(#{@x.to_code})"
|
76
|
+
end
|
77
|
+
end # Sin
|
78
|
+
CAS::Sin.init_simplify_dict
|
79
|
+
|
80
|
+
# Shortcut for `CAS::Sin#new`
|
81
|
+
#
|
82
|
+
# * **argument**: `CAS::Op` argument of the function
|
83
|
+
# * **returns**: `CAS::Sin` operation
|
84
|
+
def self.sin(x)
|
85
|
+
CAS::Sin.new x
|
86
|
+
end
|
87
|
+
|
88
|
+
# _ _
|
89
|
+
# /_\ __(_)_ _
|
90
|
+
# / _ \ (_-< | ' \
|
91
|
+
# /_/ \_\/__/_|_||_|
|
92
|
+
|
93
|
+
##
|
94
|
+
# Representation for the `arcsin(x)` function. It is implemented
|
95
|
+
# as a `CAS::Op`. It is the inverse of the `sin(x)` function
|
96
|
+
class Asin < CAS::Op
|
97
|
+
# Return the derivative of the `arcsin(x)` function using the chain
|
98
|
+
# rule. The input is a `CAS::Op`
|
99
|
+
#
|
100
|
+
# * **argument**: `CAS::Op` object of the derivative
|
101
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
102
|
+
def diff(v)
|
103
|
+
if @x.depend? v
|
104
|
+
return @x.diff(v) / CAS.sqrt(CAS::One - CAS.pow(@x, CAS::Two))
|
105
|
+
else
|
106
|
+
return CAS::Zero
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
111
|
+
# or `Float` (depends upon promotions).
|
112
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
113
|
+
# as keys, and a `Numeric` as a value
|
114
|
+
#
|
115
|
+
# * **argument**: `Hash` with feed dictionary
|
116
|
+
# * **returns**: `Numeric`
|
117
|
+
def call(f)
|
118
|
+
CAS::Help.assert(f, Hash)
|
119
|
+
Math::acos(@x.call(f))
|
120
|
+
end
|
121
|
+
|
122
|
+
# Convert expression to string
|
123
|
+
#
|
124
|
+
# * **returns**: `String` to print on screen
|
125
|
+
def to_s
|
126
|
+
"asin(#{@x})"
|
127
|
+
end
|
128
|
+
|
129
|
+
# Simplification callback. It simplify the subgraph of each node
|
130
|
+
# until all possible simplification are performed (thus the execution
|
131
|
+
# time is not deterministic).
|
132
|
+
#
|
133
|
+
# * **returns**: `CAS::Op` simplified version
|
134
|
+
def simplify
|
135
|
+
super
|
136
|
+
return @x.x if @x.is_a? CAS::Sin
|
137
|
+
return self.simplify_dictionary
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.init_simplify_dict
|
141
|
+
@simplify_dict = {
|
142
|
+
CAS::Zero => CAS::Zero,
|
143
|
+
CAS::One => (CAS::Pi / 2)
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
148
|
+
#
|
149
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
150
|
+
def to_code
|
151
|
+
"Math::asin(#{@x.to_code})"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
CAS::Asin.init_simplify_dict
|
155
|
+
|
156
|
+
class << self
|
157
|
+
# Shortcuts for `CAS::Asin#new`
|
158
|
+
#
|
159
|
+
# * **argument**: `CAS::Op` argument of the function
|
160
|
+
# * **returns**: `CAS::Asin` operation
|
161
|
+
def asin(x)
|
162
|
+
CAS::Asin.new x
|
163
|
+
end
|
164
|
+
alias :arcsin :asin
|
165
|
+
end
|
166
|
+
|
167
|
+
# ___
|
168
|
+
# / __|___ ___
|
169
|
+
# | (__/ _ (_-<
|
170
|
+
# \___\___/__/
|
171
|
+
|
172
|
+
##
|
173
|
+
# Representation for the `cos(x)` function. It is implemented
|
174
|
+
# as a `CAS::Op`.
|
175
|
+
class Cos < CAS::Op
|
176
|
+
# Return the derivative of the `cos(x)` function using the chain
|
177
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
178
|
+
# with respect to functions.
|
179
|
+
#
|
180
|
+
# ```
|
181
|
+
# d
|
182
|
+
# -- cos(f(x)) = -f'(x) sin(fx)
|
183
|
+
# dx
|
184
|
+
# ```
|
185
|
+
#
|
186
|
+
# * **argument**: `CAS::Op` object of the derivative
|
187
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
188
|
+
def diff(v)
|
189
|
+
if @x.depend? v
|
190
|
+
return CAS.invert(@x.diff(v) * CAS.sin(@x))
|
191
|
+
else
|
192
|
+
return CAS::Zero
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
197
|
+
# or `Float` (depends upon promotions).
|
198
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
199
|
+
# as keys, and a `Numeric` as a value
|
200
|
+
#
|
201
|
+
# * **argument**: `Hash` with feed dictionary
|
202
|
+
# * **returns**: `Numeric`
|
203
|
+
def call(f)
|
204
|
+
CAS::Help.assert(f, Hash)
|
205
|
+
Math::cos(@x.call(f))
|
206
|
+
end
|
207
|
+
|
208
|
+
# Convert expression to string
|
209
|
+
#
|
210
|
+
# * **returns**: `String` to print on screen
|
211
|
+
def to_s
|
212
|
+
"cos(#{@x})"
|
213
|
+
end
|
214
|
+
|
215
|
+
# Simplification callback. It simplify the subgraph of each node
|
216
|
+
# until all possible simplification are performed (thus the execution
|
217
|
+
# time is not deterministic).
|
218
|
+
#
|
219
|
+
# * **returns**: `CAS::Op` simplified version
|
220
|
+
def simplify
|
221
|
+
super
|
222
|
+
return @x.x if @x.is_a? CAS::Acos
|
223
|
+
return self.simplify_dictionary
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.init_simplify_dict
|
227
|
+
@simplify_dict = {
|
228
|
+
CAS::Zero => CAS::One,
|
229
|
+
CAS::Pi => CAS::One,
|
230
|
+
CAS::Pi/2 => CAS::Zero
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
235
|
+
#
|
236
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
237
|
+
def to_code
|
238
|
+
"Math::cos(#{@x.to_code})"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
CAS::Cos.init_simplify_dict
|
242
|
+
|
243
|
+
# Shortcut for `CAS::Cos#new`
|
244
|
+
#
|
245
|
+
# * **argument**: `CAS::Op` argument of the function
|
246
|
+
# * **returns**: `CAS::Cos` operation
|
247
|
+
def self.cos(x)
|
248
|
+
CAS::Cos.new x
|
249
|
+
end
|
250
|
+
|
251
|
+
# _
|
252
|
+
# /_\ __ ___ ___
|
253
|
+
# / _ \/ _/ _ (_-<
|
254
|
+
# /_/ \_\__\___/__/
|
255
|
+
|
256
|
+
##
|
257
|
+
# Representation for the `arccos(x)` function. It is implemented
|
258
|
+
# as a `CAS::Op`. It is the inverse of the `cos(x)` function
|
259
|
+
class Acos < CAS::Op
|
260
|
+
def diff(v)
|
261
|
+
if @x.depend? v
|
262
|
+
return CAS.invert(@x.diff(v)/CAS.sqrt(CAS::One - CAS.pow(@x, CAS::Two)))
|
263
|
+
else
|
264
|
+
return CAS::Zero
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
269
|
+
# or `Float` (depends upon promotions).
|
270
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
271
|
+
# as keys, and a `Numeric` as a value
|
272
|
+
#
|
273
|
+
# * **argument**: `Hash` with feed dictionary
|
274
|
+
# * **returns**: `Numeric`
|
275
|
+
def call(f)
|
276
|
+
CAS::Help.assert(f, Hash)
|
277
|
+
return Math::acos(@x.call(f))
|
278
|
+
end
|
279
|
+
|
280
|
+
# Convert expression to string
|
281
|
+
#
|
282
|
+
# * **returns**: `String` to print on screen
|
283
|
+
def to_s
|
284
|
+
"acos(#{@x})"
|
285
|
+
end
|
286
|
+
|
287
|
+
# Simplification callback. It simplify the subgraph of each node
|
288
|
+
# until all possible simplification are performed (thus the execution
|
289
|
+
# time is not deterministic).
|
290
|
+
#
|
291
|
+
# * **returns**: `CAS::Op` simplified version
|
292
|
+
def simplify
|
293
|
+
super
|
294
|
+
return @x.x if @x.is_a? CAS::Cos
|
295
|
+
return self.simplify_dictionary
|
296
|
+
end
|
297
|
+
|
298
|
+
def self.init_simplify_dict
|
299
|
+
@simplify_dict = {
|
300
|
+
CAS::Zero => (CAS::Pi / 2),
|
301
|
+
CAS::One => CAS::Zero
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
306
|
+
#
|
307
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
308
|
+
def to_code
|
309
|
+
"Math::acos(#{@x.to_code})"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
CAS::Acos.init_simplify_dict
|
313
|
+
|
314
|
+
class << self
|
315
|
+
# Shortcut for `CAS::Acos#new`
|
316
|
+
#
|
317
|
+
# * **argument**: `CAS::Op` argument of the function
|
318
|
+
# * **returns**: `CAS::Acos` operation
|
319
|
+
def acos(x)
|
320
|
+
CAS::Acos.new x
|
321
|
+
end
|
322
|
+
alias :arccos :acos
|
323
|
+
end
|
324
|
+
|
325
|
+
# _____
|
326
|
+
# |_ _|_ _ _ _
|
327
|
+
# | |/ _` | ' \
|
328
|
+
# |_|\__,_|_||_|
|
329
|
+
|
330
|
+
##
|
331
|
+
# Representation for the `tan(x)` function. It is implemented
|
332
|
+
# as a `CAS::Op`.
|
333
|
+
class Tan < CAS::Op
|
334
|
+
# Return the derivative of the `tan(x)` function using the chain
|
335
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
336
|
+
# with respect to functions. E.g.:
|
337
|
+
#
|
338
|
+
# ```
|
339
|
+
# d f'(x)
|
340
|
+
# -- sin(f(x)) = -------
|
341
|
+
# dx cos²(x)
|
342
|
+
# ```
|
343
|
+
#
|
344
|
+
# * **argument**: `CAS::Op` object of the derivative
|
345
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
346
|
+
def diff(v)
|
347
|
+
if @x.depend? v
|
348
|
+
return @x.diff(v) * CAS.pow(CAS::One/CAS.cos(@x), CAS::Two)
|
349
|
+
else
|
350
|
+
return CAS::Zero
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
355
|
+
# or `Float` (depends upon promotions).
|
356
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
357
|
+
# as keys, and a `Numeric` as a value
|
358
|
+
#
|
359
|
+
# * **argument**: `Hash` with feed dictionary
|
360
|
+
# * **returns**: `Numeric`
|
361
|
+
def call(f)
|
362
|
+
CAS::Help.assert(f, Hash)
|
363
|
+
Math::tan(@x.call(f))
|
364
|
+
end
|
365
|
+
|
366
|
+
# Convert expression to string
|
367
|
+
#
|
368
|
+
# * **returns**: `String` to print on screen
|
369
|
+
def to_s
|
370
|
+
"tan(#{@x})"
|
371
|
+
end
|
372
|
+
|
373
|
+
# Simplification callback. It simplify the subgraph of each node
|
374
|
+
# until all possible simplification are performed (thus the execution
|
375
|
+
# time is not deterministic).
|
376
|
+
#
|
377
|
+
# * **returns**: `CAS::Op` simplified version
|
378
|
+
def simplify
|
379
|
+
super
|
380
|
+
return @x.x if @x.is_a? CAS::Atan
|
381
|
+
return self.simplify_dictionary
|
382
|
+
end
|
383
|
+
|
384
|
+
def self.init_simplify_dict
|
385
|
+
@simplify_dict = {
|
386
|
+
CAS::Zero => CAS::Zero,
|
387
|
+
CAS::Pi => CAS::Zero,
|
388
|
+
CAS::Pi/2 => CAS::Infinity
|
389
|
+
}
|
390
|
+
end
|
391
|
+
|
392
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
393
|
+
#
|
394
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
395
|
+
def to_code
|
396
|
+
"Math::tan(#{@x.to_code})"
|
397
|
+
end
|
398
|
+
end
|
399
|
+
CAS::Tan.init_simplify_dict
|
400
|
+
|
401
|
+
# Shortcut for `CAS::Tan#new`
|
402
|
+
#
|
403
|
+
# * **argument**: `CAS::Op` argument of the function
|
404
|
+
# * **returns**: `CAS::Tan` operation
|
405
|
+
def self.tan(x)
|
406
|
+
CAS::Tan.new x
|
407
|
+
end
|
408
|
+
|
409
|
+
# _ _
|
410
|
+
# /_\| |_ __ _ _ _
|
411
|
+
# / _ \ _/ _` | ' \
|
412
|
+
# /_/ \_\__\__,_|_||_|
|
413
|
+
|
414
|
+
##
|
415
|
+
# Representation for the `arctan(x)` function. It is implemented
|
416
|
+
# as a `CAS::Op`. It is the inverse of the `tan(x)` function
|
417
|
+
class Atan < CAS::Op
|
418
|
+
# Return the derivative of the `arctan(x)` function using the chain
|
419
|
+
# rule. The input is a `CAS::Op` because it can handle derivatives
|
420
|
+
# with respect to functions.
|
421
|
+
#
|
422
|
+
# * **argument**: `CAS::Op` object of the derivative
|
423
|
+
# * **returns**: `CAS::Op` a derivated object, or `CAS::Zero` for constants
|
424
|
+
def diff(v)
|
425
|
+
if @x.depend? v
|
426
|
+
return (@x.diff(v) / (CAS.pow(@x, CAS::Two) + CAS::One))
|
427
|
+
else
|
428
|
+
return CAS::Zero
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Call resolves the operation tree in a `Numeric` (if `Fixnum`)
|
433
|
+
# or `Float` (depends upon promotions).
|
434
|
+
# As input, it requires an hash with `CAS::Variable` or `CAS::Variable#name`
|
435
|
+
# as keys, and a `Numeric` as a value
|
436
|
+
#
|
437
|
+
# * **argument**: `Hash` with feed dictionary
|
438
|
+
# * **returns**: `Numeric`
|
439
|
+
def call(f)
|
440
|
+
CAS::Help.assert(f, Hash)
|
441
|
+
Math::atan(@x.call(f))
|
442
|
+
end
|
443
|
+
|
444
|
+
# Convert expression to string
|
445
|
+
#
|
446
|
+
# * **returns**: `String` to print on screen
|
447
|
+
def to_s
|
448
|
+
"atan(#{@x})"
|
449
|
+
end
|
450
|
+
|
451
|
+
# Simplification callback. It simplify the subgraph of each node
|
452
|
+
# until all possible simplification are performed (thus the execution
|
453
|
+
# time is not deterministic).
|
454
|
+
#
|
455
|
+
# * **returns**: `CAS::Op` simplified version
|
456
|
+
def simplify
|
457
|
+
super
|
458
|
+
return @x.x if @x.is_a? CAS::Tan
|
459
|
+
return self.simplify_dictionary
|
460
|
+
end
|
461
|
+
|
462
|
+
def self.init_simplify_dict
|
463
|
+
@simplify_dict = {
|
464
|
+
CAS::Zero => CAS::Zero,
|
465
|
+
CAS::One => (CAS::Pi/4),
|
466
|
+
CAS::Infinity => (CAS::Pi/2)
|
467
|
+
}
|
468
|
+
end
|
469
|
+
|
470
|
+
# Convert expression to code (internal, for `CAS::Op#to_proc` method)
|
471
|
+
#
|
472
|
+
# * **returns**: `String` that represent Ruby code to be parsed in `CAS::Op#to_proc`
|
473
|
+
def to_code
|
474
|
+
"Math::atan(#{@x.to_code})"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
CAS::Atan.init_simplify_dict
|
478
|
+
|
479
|
+
class << self
|
480
|
+
# Shortcut for `CAS::Atan#new`
|
481
|
+
#
|
482
|
+
# * **argument**: `CAS::Op` argument of the function
|
483
|
+
# * **returns**: `CAS::Atan` operation
|
484
|
+
def atan(x)
|
485
|
+
CAS::Atan.new x
|
486
|
+
end
|
487
|
+
alias :arctan :atan
|
488
|
+
end
|
489
|
+
end
|