Mr.CAS 0.2.6
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.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 62a33629bd1fe8154be4964e99dccd87cd7c060c
|
4
|
+
data.tar.gz: 6842feded0a74f326b81a6d5513527df10ab3d96
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3d0c46c099e1bb90d2d5a1087b286d079712777e92ed1d5d9c7a1ab7947fac0f98a82414128eadd92d76b1ead31b3fe704b8f0048ed1686e3ef08220a5614cf
|
7
|
+
data.tar.gz: 07ccfad775f154afff75a097b9c0e0b9233ac023452176dbbbc429417da3fb810c02796ab52d601366d672e7025c9cf8ed4fa95ce2527c4cb878e0816887c39b
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
�
|
data.tar.gz.sig
ADDED
data/lib/Mr.CAS.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
##
|
4
|
+
# Mr.CAS
|
5
|
+
# A minmalistic CAS engine with encapsuled graph
|
6
|
+
# representation. This will make impossible to
|
7
|
+
# perform complex high level simplifications, but
|
8
|
+
# it is powerful enough to define simple algorithm
|
9
|
+
# in a symbolic way.
|
10
|
+
#
|
11
|
+
# Mathematically, this is an implementation of the
|
12
|
+
# forward chain rule for automatic differentiaition.
|
13
|
+
# Each function is a container of function and the
|
14
|
+
# derivation is in the form:
|
15
|
+
#
|
16
|
+
# ```
|
17
|
+
# d(f(g(x))
|
18
|
+
# --------- = g'(x) * f'(g(x))
|
19
|
+
# dx
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
# Author:: Matteo Ragni (mailto:info@ragni.me)
|
23
|
+
# Copyright:: Copyright (c) 2016 Matteo Ragni
|
24
|
+
# License:: Distributed under MIT license terms
|
25
|
+
module CAS
|
26
|
+
|
27
|
+
# Support functions are in this separate Helper class
|
28
|
+
module Help
|
29
|
+
# Check input `obj.class` against a `type` class
|
30
|
+
# raises an `ArgumentError` if check fails
|
31
|
+
#
|
32
|
+
# * **argument**: object to be cecked
|
33
|
+
# * **argument**: type to be checked against
|
34
|
+
# * **returns**: `TrueClass`, or raises an `ArgumentError`
|
35
|
+
def self.assert(obj, type)
|
36
|
+
raise ArgumentError, "required #{type}, received #{obj.class}" unless obj.is_a? type
|
37
|
+
return true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Check if input object is feasible to be a name of a `CAS::Variable` or a `CAS::Function`
|
41
|
+
# raise an `ArgumentError` if the check fails. To be feasible the object must be a `String`
|
42
|
+
# instance or `Symbol` instance
|
43
|
+
#
|
44
|
+
# * **argument**: object to be checked
|
45
|
+
# * **returns**: `TrueClass` or raises `ArgumentError`
|
46
|
+
def self.assert_name(obj)
|
47
|
+
raise ArgumentError, "Input name must be a String/Symbol" unless [Symbol, String].include? obj.class
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# ___ _
|
54
|
+
# | _ \___ __ _ _ _(_)_ _ ___ ___
|
55
|
+
# | / -_) _` | || | | '_/ -_|_-<
|
56
|
+
# |_|_\___\__, |\_,_|_|_| \___/__/
|
57
|
+
# |_|
|
58
|
+
|
59
|
+
%w|operators/op.rb operators/bary-op.rb operators/nary-op.rb
|
60
|
+
numbers/constants.rb numbers/variables.rb numbers/functions.rb
|
61
|
+
functions/fnc-sum.rb functions/fnc-prod.rb
|
62
|
+
functions/fnc-base.rb functions/fnc-trig.rb functions/fnc-trsc.rb
|
63
|
+
functions/fnc-conditions.rb functions/fnc-box-conditions.rb functions/fnc-piecewise.rb
|
64
|
+
overloading/fixnum.rb overloading/float.rb
|
65
|
+
version.rb|.each do |r|
|
66
|
+
require File.expand_path(r, File.dirname(__FILE__))
|
67
|
+
end
|
68
|
+
|
69
|
+
module CAS
|
70
|
+
CAS::NumericToConst[-Math::PI] = (-CAS::Pi)
|
71
|
+
CAS::NumericToConst[-Math::E] = (-CAS::E)
|
72
|
+
CAS::NumericToConst[(-1.0/0.0)] = (CAS::NegInfinity)
|
73
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module CAS
|
4
|
+
module AutoDiff
|
5
|
+
class DualNumber
|
6
|
+
include Math
|
7
|
+
attr_reader :x, :y
|
8
|
+
|
9
|
+
def initialize(x, y)
|
10
|
+
@x, @y = x, y
|
11
|
+
end
|
12
|
+
|
13
|
+
def +(v)
|
14
|
+
DualNumber.new @x + v.x, @y + v.y
|
15
|
+
end
|
16
|
+
|
17
|
+
def -(v)
|
18
|
+
DualNumber.new @x - v.x, @y - v.y
|
19
|
+
end
|
20
|
+
|
21
|
+
def *(v)
|
22
|
+
DualNumber.new @x * v.x, @y * v.x + @x * v.y
|
23
|
+
end
|
24
|
+
|
25
|
+
def /(v)
|
26
|
+
DualNumber.new @x / v.x, (@y * v.x - @x * v.y) / (v.x ** 2)
|
27
|
+
end
|
28
|
+
|
29
|
+
def -@
|
30
|
+
DualNumber.new -@x, -@y
|
31
|
+
end
|
32
|
+
|
33
|
+
def **(v)
|
34
|
+
t = (v.y == 0 ? 0 : @x * log(@x) * v.y)
|
35
|
+
DualNumber.new @x ** v.x, (@x ** (v.x - 1)) * (v.x * @y + t)
|
36
|
+
end
|
37
|
+
|
38
|
+
def abs
|
39
|
+
return DualNumber.new(0, 0) if @x == 0
|
40
|
+
DualNumber.new @x.abs, @y * (@x <=> 0)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s; self.inspect; end
|
44
|
+
def inspect; "<#{@x},#{@y}>"; end
|
45
|
+
def real; @x; end
|
46
|
+
def diff; @y; end
|
47
|
+
end # DualNumbers
|
48
|
+
|
49
|
+
def self.const(x)
|
50
|
+
DualNumber.new x, 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.vars(x)
|
54
|
+
DualNumber.new x, 1
|
55
|
+
end
|
56
|
+
|
57
|
+
Zero = self.const 0
|
58
|
+
One = self.const 1
|
59
|
+
Two = self.const 2
|
60
|
+
E = self.const Math::E
|
61
|
+
Pi = self.const Math::PI
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
{
|
66
|
+
# Terminal nodes
|
67
|
+
CAS::Variable => Proc.new { |fd| CAS::AutoDiff.vars (fd[CAS::Variable[@name]] || fd[@name]) },
|
68
|
+
CAS::Constant => Proc.new { |_fd| CAS::AutoDiff.const @x },
|
69
|
+
CAS::Function => Proc.new { |_fd| raise RuntimeError, "Impossible for implicit functions" },
|
70
|
+
|
71
|
+
# Base functions
|
72
|
+
CAS::Sum => Proc.new { |fd| @x.auto_diff(fd) + @y.auto_diff(fd) },
|
73
|
+
CAS::Diff => Proc.new { |fd| @x.auto_diff(fd) - @y.auto_diff(fd) },
|
74
|
+
CAS::Prod => Proc.new { |fd| @x.auto_diff(fd) * @y.auto_diff(fd) },
|
75
|
+
CAS::Pow => Proc.new { |fd| @x.auto_diff(fd) ** @y.auto_diff(fd) },
|
76
|
+
CAS::Div => Proc.new { |fd| @x.auto_diff(fd) / @y.auto_diff(fd) },
|
77
|
+
CAS::Sqrt => Proc.new { |fd| @x.auto_diff(fd) ** (CAS::AutoDiff::One / CAS::AutoDiff::Two) },
|
78
|
+
CAS::Invert => Proc.new { |fd| -@x.auto_diff(fd) },
|
79
|
+
CAS::Abs => Proc.new { |fd| @x.auto_diff(fd).abs },
|
80
|
+
|
81
|
+
# Trigonometric functions
|
82
|
+
CAS::Sin => Proc.new { |fd|
|
83
|
+
u = @x.auto_diff(fd)
|
84
|
+
CAS::AutoDiff::DualNumber.new Math.sin(u.x), Math.cos(u.x) * u.y
|
85
|
+
},
|
86
|
+
CAS::Asin => Proc.new { |fd|
|
87
|
+
u = @x.auto_diff(fd)
|
88
|
+
CAS::AutoDiff::DualNumber.new Math.asin(u.x), -(Math.sin(u.x) * u.y)
|
89
|
+
},
|
90
|
+
CAS::Cos => Proc.new { |fd|
|
91
|
+
u = @x.auto_diff(fd)
|
92
|
+
CAS::AutoDiff::DualNumber.new Math.cos(u.x), -(Math.sin(u.x) * u.y)
|
93
|
+
},
|
94
|
+
CAS::Acos => Proc.new { |fd|
|
95
|
+
u = @x.auto_diff(fd)
|
96
|
+
CAS::AutoDiff::DualNumber.new Math.acos(u.x), -u.y / Math.sqrt(1 + u.x ** 2)
|
97
|
+
},
|
98
|
+
CAS::Tan => Proc.new { |fd|
|
99
|
+
u = @x.auto_diff(fd)
|
100
|
+
CAS::AutoDiff::DualNumber.new Math.tan(u.x), u.y / (Math.cos(u.x) ** 2)
|
101
|
+
},
|
102
|
+
CAS::Atan => Proc.new { |fd|
|
103
|
+
u = @x.auto_diff(fd)
|
104
|
+
CAS::AutoDiff::DualNumber.new Math.atan(u.x), u.y / (1 + u.x ** 2)
|
105
|
+
},
|
106
|
+
|
107
|
+
# Trascendent functions
|
108
|
+
CAS::Exp => Proc.new { |fd| CAS::AutoDiff::E ** @x.auto_diff(fd) },
|
109
|
+
CAS::Ln => Proc.new { |fd|
|
110
|
+
u = @x.auto_diff(fd)
|
111
|
+
CAS::AutoDiff::DualNumber.new Math.log(u.x), u.y / u.x
|
112
|
+
},
|
113
|
+
|
114
|
+
# Piecewise
|
115
|
+
CAS::Piecewise => Proc.new { |_fd| raise RuntimeError, "Not implemented auto_diff for Piecewise" },
|
116
|
+
CAS::Max => Proc.new { |fd|
|
117
|
+
a = @x.auto_diff(fd)
|
118
|
+
b = @y.auto_diff(fd)
|
119
|
+
(a.x >= b.x ? a : b)
|
120
|
+
},
|
121
|
+
CAS::Min => Proc.new { |fd|
|
122
|
+
a = @x.auto_diff(fd)
|
123
|
+
b = @y.auto_diff(fd)
|
124
|
+
(a.x >= b.x ? a : b)
|
125
|
+
}
|
126
|
+
}.each do |cls, blk|
|
127
|
+
cls.send(:define_method, "auto_diff", &blk)
|
128
|
+
end
|
129
|
+
end
|
data/lib/Mr.CAS/c-opt.rb
ADDED
@@ -0,0 +1,225 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ___ ___ _ _
|
4
|
+
# / __| | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | (__ | _/ | || / _` | | ' \
|
6
|
+
# \___| |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |___/
|
8
|
+
|
9
|
+
require 'pry'
|
10
|
+
|
11
|
+
module CAS
|
12
|
+
class CLib < Hash
|
13
|
+
def self.create(name, &blk)
|
14
|
+
a = CLib.new(name)
|
15
|
+
a.instance_eval(&blk)
|
16
|
+
return a
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(name)
|
20
|
+
raise ArgumentError, "Name for the library undefined" unless name.is_a? String
|
21
|
+
@name = name
|
22
|
+
@define = {}
|
23
|
+
@include = {}
|
24
|
+
@include[:std] = []
|
25
|
+
@include[:local] = []
|
26
|
+
@type = "double"
|
27
|
+
|
28
|
+
# Default definitions
|
29
|
+
# self.define "M_PI", Math::PI
|
30
|
+
# self.define "M_INFINITY","HUGE_VAL"
|
31
|
+
# self.define "M_E", Math::E
|
32
|
+
# self.define "M_EPSILON", 1E-16
|
33
|
+
|
34
|
+
# Default inclusions
|
35
|
+
self.include "math.h"
|
36
|
+
end
|
37
|
+
|
38
|
+
def as_double; @type = "double"; end
|
39
|
+
def as_float; @type = "float"; end
|
40
|
+
def as_int; @type = "int"; end
|
41
|
+
def as_long_int; @type = "long int"; end
|
42
|
+
|
43
|
+
def define(k, v)
|
44
|
+
raise ArgumentError, "k must be a String, received a #{k.class}" unless k.is_a? String
|
45
|
+
@define[k] = v.to_s
|
46
|
+
@define
|
47
|
+
end
|
48
|
+
|
49
|
+
def undefine(k)
|
50
|
+
@define.delete k
|
51
|
+
return @define
|
52
|
+
end
|
53
|
+
|
54
|
+
def include_type(type, lib)
|
55
|
+
raise ArgumentError, "type must be a Symbol (:std, :local), received #{type}" unless [:std, :local].include? type
|
56
|
+
raise ArgumentError, "lib must be a String, received a #{lib.class}" unless lib.is_a? String
|
57
|
+
@include[type] << lib unless @include[type].include? lib
|
58
|
+
end
|
59
|
+
def include(lib); self.include_type(:std, lib); end
|
60
|
+
def include_local(lib); self.include_type(:local, lib); end
|
61
|
+
|
62
|
+
def implements_as(name, op); self[name] = op; end
|
63
|
+
|
64
|
+
def header
|
65
|
+
<<-TO_HEADER
|
66
|
+
// Header file for library: #{@name}.c
|
67
|
+
|
68
|
+
#ifndef #{@name}_H
|
69
|
+
#define #{@name}_H
|
70
|
+
|
71
|
+
// Standard Libraries
|
72
|
+
#{ @include[:std].map { |e| "#include <#{e}>" }.join("\n") }
|
73
|
+
|
74
|
+
// Local Libraries
|
75
|
+
#{ @include[:local].map { |e| "#include \"#{e}\"" }.join("\n") }
|
76
|
+
|
77
|
+
// Definitions
|
78
|
+
#{ @define.map { |k, v| "#define #{k} #{v}" }.join("\n") }
|
79
|
+
|
80
|
+
// Functions
|
81
|
+
#{
|
82
|
+
self.keys.map do |fname|
|
83
|
+
"#{@type} #{fname}(#{ self[fname].args.map { |x| "#{@type} #{x.name}" }.join(", ")});"
|
84
|
+
end.join("\n")
|
85
|
+
}
|
86
|
+
|
87
|
+
#endif // #{@name}_H
|
88
|
+
TO_HEADER
|
89
|
+
end
|
90
|
+
|
91
|
+
def source
|
92
|
+
functions = []
|
93
|
+
|
94
|
+
self.each do |fname, op|
|
95
|
+
c_op = op.to_c.sort_by { |_k, c| c[:id] }
|
96
|
+
nf = <<-NEWFUNCTION
|
97
|
+
#{@type} #{fname}(#{ op.args.map { |x| "#{@type} #{x.name}" }.join(", ")}) {
|
98
|
+
#{c_op.map { |e| " double #{e[1][:var]} = #{e[1][:def]};"}.join("\n")}
|
99
|
+
|
100
|
+
return #{c_op[-1][1][:var]};
|
101
|
+
}
|
102
|
+
NEWFUNCTION
|
103
|
+
functions << nf
|
104
|
+
end
|
105
|
+
|
106
|
+
<<-TO_SOURCE
|
107
|
+
// Source file for library: #{@name}.c
|
108
|
+
|
109
|
+
#include "#{@name}.h"
|
110
|
+
|
111
|
+
#{functions.join("\n")}
|
112
|
+
// end of #{@name}.c
|
113
|
+
TO_SOURCE
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
{
|
118
|
+
# Terminal nodes
|
119
|
+
CAS::Constant => Proc.new { |_v| "#{@x}" },
|
120
|
+
CAS::Variable => Proc.new { |_v| "#{@name}" },
|
121
|
+
CAS::PI_CONSTANT => Proc.new { |_v| "M_PI" },
|
122
|
+
CAS::INFINITY_CONSTANT => Proc.new { |_v| "M_INFINITY" },
|
123
|
+
CAS::NEG_INFINITY_CONSTANT => Proc.new { |_v| "(-M_INFINITY)" },
|
124
|
+
CAS::E_CONSTANT => Proc.new { |_v| "M_E" }
|
125
|
+
}.each do |cls, blk|
|
126
|
+
cls.send(:define_method, "__to_c", &blk)
|
127
|
+
end.each do |cls, _blk|
|
128
|
+
cls.send(:define_method, "to_c", &Proc.new do
|
129
|
+
v = {}; self.__to_c(v); v
|
130
|
+
end)
|
131
|
+
end
|
132
|
+
|
133
|
+
{
|
134
|
+
# Base functions
|
135
|
+
CAS::Sum => Proc.new { |v| "(#{@x.map { |e| e.__to_c(v) }.join(" + ")})" },
|
136
|
+
CAS::Diff => Proc.new { |v| "(#{@x.__to_c(v)} - #{@y.__to_c(v)})" },
|
137
|
+
CAS::Prod => Proc.new { |v| "(#{@x.map { |e| e.__to_c(v) }.join(" + ")})" },
|
138
|
+
CAS::Pow => Proc.new { |v| "pow(#{@x.__to_c(v)}, #{@y.__to_c(v)})" },
|
139
|
+
CAS::Div => Proc.new { |v| "(#{@x.__to_c(v)}) / (#{@y.__to_c(v)} + M_EPSILON)" },
|
140
|
+
CAS::Sqrt => Proc.new { |v| "sqrt(#{@x.__to_c(v)})" },
|
141
|
+
CAS::Invert => Proc.new { |v| "(-#{@x.__to_c(v)})" },
|
142
|
+
CAS::Abs => Proc.new { |v| "fabs(#{@x.__to_c(v)})" },
|
143
|
+
|
144
|
+
# Trigonometric functions
|
145
|
+
CAS::Sin => Proc.new { |v| "sin(#{@x.__to_c(v)})" },
|
146
|
+
CAS::Asin => Proc.new { |v| "asin(#{@x.__to_c(v)})" },
|
147
|
+
CAS::Cos => Proc.new { |v| "cos(#{@x.__to_c(v)})" },
|
148
|
+
CAS::Acos => Proc.new { |v| "acos(#{@x.__to_c(v)})" },
|
149
|
+
CAS::Tan => Proc.new { |v| "tan(#{@x.__to_c(v)})" },
|
150
|
+
CAS::Atan => Proc.new { |v| "atan(#{@x.__to_c(v)})" },
|
151
|
+
|
152
|
+
# Trascendent functions
|
153
|
+
CAS::Exp => Proc.new { |v| "exp(#{@x.__to_c(v)})" },
|
154
|
+
CAS::Ln => Proc.new { |v| "log(#{@x.__to_c(v)})" },
|
155
|
+
|
156
|
+
# # Box Conditions
|
157
|
+
# CAS::BoxConditionOpen => Proc.new {
|
158
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
159
|
+
# "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
160
|
+
# },
|
161
|
+
# CAS::BoxConditionUpperClosed => Proc.new {
|
162
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
163
|
+
# "(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
164
|
+
# },
|
165
|
+
# CAS::BoxConditionLowerClosed => Proc.new {
|
166
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
167
|
+
# "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
168
|
+
# },
|
169
|
+
# CAS::BoxConditionClosed => Proc.new {
|
170
|
+
# ["double __t_#{x.object_id} = #{x.__to_c(v)};",
|
171
|
+
# "(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
172
|
+
# },
|
173
|
+
|
174
|
+
# Conditions
|
175
|
+
# CAS::Equal => Proc.new { "(#{x.__to_c(v)} == #{y.__to_c(v)})" },
|
176
|
+
# CAS::Smaller => Proc.new { "(#{x.__to_c(v)} < #{y.__to_c(v)})" },
|
177
|
+
# CAS::Greater => Proc.new { "(#{x.__to_c(v)} > #{y.__to_c(v)})" },
|
178
|
+
# CAS::SmallerEqual => Proc.new { "(#{x.__to_c(v)} <= #{y.__to_c(v)})" },
|
179
|
+
# CAS::GreaterEqual => Proc.new { "(#{x.__to_c(v)} >= #{y.__to_c(v)})" },
|
180
|
+
# TODO there is no sense in exporting conditions in C...
|
181
|
+
|
182
|
+
# Piecewise
|
183
|
+
# CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
|
184
|
+
# CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
|
185
|
+
# CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
|
186
|
+
CAS::Function => Proc.new { |v| "#{@c_name}(#{@x.map {|e| e.__to_c(v)}.join(", ")})" }
|
187
|
+
}.each do |cls, blk|
|
188
|
+
cls.send(:define_method, "__to_c_impl", &blk)
|
189
|
+
end.each do |cls, _blk|
|
190
|
+
cls.send(:define_method, "__to_c", &Proc.new do |v|
|
191
|
+
sym = self.to_s.to_sym
|
192
|
+
v[sym] = {
|
193
|
+
:def => "#{self.__to_c_impl(v)}",
|
194
|
+
:var => "__t_#{v.keys.size}",
|
195
|
+
:id => v.keys.size
|
196
|
+
} unless v[sym] # Pleas note that the order here is foundamental!
|
197
|
+
v[sym][:var]
|
198
|
+
end)
|
199
|
+
end.each do |cls, _blk|
|
200
|
+
cls.send(:define_method, "to_c", &Proc.new do
|
201
|
+
v = {}; self.__to_c(v); v
|
202
|
+
end)
|
203
|
+
end
|
204
|
+
|
205
|
+
class Op
|
206
|
+
def to_c_lib(name)
|
207
|
+
CAS::Help.assert(name, String)
|
208
|
+
[CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
class Function
|
213
|
+
def c_name=(s)
|
214
|
+
CAS::Help.assert_name s
|
215
|
+
@c_name = s
|
216
|
+
end
|
217
|
+
|
218
|
+
def Function.new(name, *xs)
|
219
|
+
a = super
|
220
|
+
a.c_name = name
|
221
|
+
return a
|
222
|
+
end
|
223
|
+
end
|
224
|
+
Function.list.each do |k, v| v.c_name = k; end
|
225
|
+
end
|
data/lib/Mr.CAS/c.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ___ ___ _ _
|
4
|
+
# / __| | _ \ |_ _ __ _(_)_ _
|
5
|
+
# | (__ | _/ | || / _` | | ' \
|
6
|
+
# \___| |_| |_|\_,_\__, |_|_||_|
|
7
|
+
# |___/
|
8
|
+
|
9
|
+
module CAS
|
10
|
+
module C_PLUGIN
|
11
|
+
C_STD_LIBRARIES = [
|
12
|
+
"math.h"
|
13
|
+
]
|
14
|
+
|
15
|
+
C_LOCAL_LIBRARIES = [ ]
|
16
|
+
|
17
|
+
C_DEFINES = {
|
18
|
+
"M_PI" => Math::PI.to_s,
|
19
|
+
"M_INFINITY" => "HUGE_VAL",
|
20
|
+
"M_E" => Math::E.to_s,
|
21
|
+
"M_EPSILON" => (1E-16).to_s
|
22
|
+
}
|
23
|
+
|
24
|
+
def self.write_header(op, name)
|
25
|
+
<<-TO_HEADER
|
26
|
+
#ifndef #{name}_HEADER
|
27
|
+
#define #{name}_HEADER
|
28
|
+
|
29
|
+
// Standard Libraries
|
30
|
+
#{ CAS::C_PLUGIN::C_STD_LIBRARIES.map { |e| "#include <#{e}>" }.join("\n") }
|
31
|
+
|
32
|
+
// Local Libraries
|
33
|
+
#{ CAS::C_PLUGIN::C_LOCAL_LIBRARIES.map { |e| "#include <#{e}>" }.join("\n") }
|
34
|
+
|
35
|
+
// Definitions
|
36
|
+
#{ CAS::C_PLUGIN::C_DEFINES.map { |k, v| "#define #{k} #{v}" }.join("\n") }
|
37
|
+
|
38
|
+
// Function
|
39
|
+
double #{name}(#{ op.args.map { |x| "double #{x.name}"}.join(", ") });
|
40
|
+
|
41
|
+
#endif // #{name}_HEADER
|
42
|
+
TO_HEADER
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.write_source(op, name)
|
46
|
+
<<-TO_SOURCE
|
47
|
+
#include "#{name}.h"
|
48
|
+
|
49
|
+
double #{name}(#{ op.args.map { |x| "double #{x.name}"}.join(", ") }) {
|
50
|
+
return #{op.to_c};
|
51
|
+
}
|
52
|
+
TO_SOURCE
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
{
|
57
|
+
# Terminal nodes
|
58
|
+
CAS::Constant => Proc.new { "#{x}" },
|
59
|
+
CAS::Variable => Proc.new { "#{name}" },
|
60
|
+
CAS::PI_CONSTANT => Proc.new { "M_PI" },
|
61
|
+
CAS::INFINITY_CONSTANT => Proc.new { "M_INFINITY" },
|
62
|
+
CAS::NEG_INFINITY_CONSTANT => Proc.new { "(-M_INFINITY)" },
|
63
|
+
CAS::E_CONSTANT => Proc.new { "M_E" },
|
64
|
+
# Base functions
|
65
|
+
CAS::Sum => Proc.new { "(#{x.to_c} + #{y.to_c})" },
|
66
|
+
CAS::Diff => Proc.new { "(#{x.to_c} - #{y.to_c})" },
|
67
|
+
CAS::Prod => Proc.new { "(#{x.to_c} * #{y.to_c})" },
|
68
|
+
CAS::Pow => Proc.new { "pow(#{x.to_c}, #{y.to_c})" },
|
69
|
+
CAS::Div => Proc.new { "(#{x.to_c}) / (#{y.to_c} + )" },
|
70
|
+
CAS::Sqrt => Proc.new { "sqrt(#{x.to_c})" },
|
71
|
+
CAS::Invert => Proc.new { "(-#{x.to_c})" },
|
72
|
+
CAS::Abs => Proc.new { "fabs(#{x.to_c})" },
|
73
|
+
|
74
|
+
# Trigonometric functions
|
75
|
+
CAS::Sin => Proc.new { "sin(#{x.to_c})" },
|
76
|
+
CAS::Asin => Proc.new { "asin(#{x.to_c})" },
|
77
|
+
CAS::Cos => Proc.new { "cos(#{x.to_c})" },
|
78
|
+
CAS::Acos => Proc.new { "acos(#{x.to_c})" },
|
79
|
+
CAS::Tan => Proc.new { "tan(#{x.to_c})" },
|
80
|
+
CAS::Atan => Proc.new { "atan(#{x.to_c})" },
|
81
|
+
|
82
|
+
# Trascendent functions
|
83
|
+
CAS::Exp => Proc.new { "exp(#{x.to_c})" },
|
84
|
+
CAS::Ln => Proc.new { "log(#{x.to_c})" },
|
85
|
+
|
86
|
+
# Box Conditions
|
87
|
+
CAS::BoxConditionOpen => Proc.new {
|
88
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
89
|
+
"(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
90
|
+
},
|
91
|
+
CAS::BoxConditionUpperClosed => Proc.new {
|
92
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
93
|
+
"(__t_#{x.object_id} > #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
94
|
+
},
|
95
|
+
CAS::BoxConditionLowerClosed => Proc.new {
|
96
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
97
|
+
"(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} < #{upper.latex})"]
|
98
|
+
},
|
99
|
+
CAS::BoxConditionClosed => Proc.new {
|
100
|
+
["double __t_#{x.object_id} = #{x.to_c};",
|
101
|
+
"(__t_#{x.object_id} >= #{lower.latex} && __t_#{x.object_id} <= #{upper.latex})"]
|
102
|
+
},
|
103
|
+
|
104
|
+
# Conditions
|
105
|
+
CAS::Equal => Proc.new { "(#{x.to_c} == #{y.to_c})" },
|
106
|
+
CAS::Smaller => Proc.new { "(#{x.to_c} < #{y.to_c})" },
|
107
|
+
CAS::Greater => Proc.new { "(#{x.to_c} > #{y.to_c})" },
|
108
|
+
CAS::SmallerEqual => Proc.new { "(#{x.to_c} <= #{y.to_c})" },
|
109
|
+
CAS::GreaterEqual => Proc.new { "(#{x.to_c} >= #{y.to_c})" },
|
110
|
+
|
111
|
+
# Piecewise
|
112
|
+
CAS::Piecewise => Proc.new { raise CASError, "Not implemented yet" },
|
113
|
+
CAS::Max => Proc.new { raise CASError, "Not implemented yet" },
|
114
|
+
CAS::Min => Proc.new { raise CASError, "Not implemented yet" }
|
115
|
+
}.each do |cls, blk|
|
116
|
+
cls.send(:define_method, "to_c", &blk)
|
117
|
+
end
|
118
|
+
|
119
|
+
class Op
|
120
|
+
def to_c_lib(name)
|
121
|
+
CAS::Help.assert(name, String)
|
122
|
+
[CAS::C_PLUGIN.write_header(self, name), CAS::C_PLUGIN.write_source(self, name)]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|