Skip to content

Commit acfd1f3

Browse files
committed
Merge branch 'master' of https://github.com/symengine/symengine.rb into exception
2 parents 52236c6 + bc52a2d commit acfd1f3

File tree

7 files changed

+148
-12
lines changed

7 files changed

+148
-12
lines changed

ext/symengine/ruby_basic.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ VALUE cbasic_binary_op(VALUE self, VALUE operand2,
2323
const basic_struct *))
2424
{
2525
basic_struct *this, *cresult;
26-
VALUE result;
26+
VALUE result = Qnil;
2727

2828
basic cbasic_operand2;
2929
basic_new_stack(cbasic_operand2);
@@ -38,18 +38,18 @@ VALUE cbasic_binary_op(VALUE self, VALUE operand2,
3838
result = Data_Wrap_Struct(Klass_of_Basic(cresult), NULL,
3939
cbasic_free_heap, cresult);
4040
basic_free_stack(cbasic_operand2);
41-
return result;
4241
} else {
4342
basic_free_stack(cbasic_operand2);
4443
raise_exception(error_code);
4544
}
45+
return result;
4646
}
4747

4848
VALUE cbasic_unary_op(VALUE self,
4949
int (*cwfunc_ptr)(basic_struct *, const basic_struct *))
5050
{
5151
basic_struct *this, *cresult;
52-
VALUE result;
52+
VALUE result = Qnil;
5353

5454
Data_Get_Struct(self, basic_struct, this);
5555

@@ -59,10 +59,10 @@ VALUE cbasic_unary_op(VALUE self,
5959
if (error_code == 0) {
6060
result = Data_Wrap_Struct(Klass_of_Basic(cresult), NULL,
6161
cbasic_free_heap, cresult);
62-
return result;
6362
} else {
6463
raise_exception(error_code);
6564
}
65+
return result;
6666
}
6767

6868
VALUE cbasic_add(VALUE self, VALUE operand2)

ext/symengine/ruby_utils.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ VALUE cutils_sympify(VALUE self, VALUE operand)
1717

1818
VALUE cutils_evalf(VALUE self, VALUE operand, VALUE prec, VALUE real)
1919
{
20-
VALUE result;
20+
VALUE result = Qnil;
2121

2222
basic_struct *cresult;
2323
cresult = basic_new_heap();
@@ -29,8 +29,8 @@ VALUE cutils_evalf(VALUE self, VALUE operand, VALUE prec, VALUE real)
2929
if (error_code == 0) {
3030
result = Data_Wrap_Struct(Klass_of_Basic(cresult), NULL,
3131
cbasic_free_heap, cresult);
32-
return result;
3332
} else {
3433
raise_exception(error_code);
3534
}
35+
return result;
3636
}

ext/symengine/symengine_utils.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ VALUE function_twoarg(int (*cwfunc_ptr)(basic_struct *, const basic_struct *,
256256

257257
void raise_exception(int error_code)
258258
{
259-
char *str;
259+
char *str = "";
260260
switch (error_code) {
261261
case -1:
262262
str = "Runtime Error";
@@ -265,5 +265,5 @@ void raise_exception(int error_code)
265265
str = "Division by Zero";
266266
break;
267267
}
268-
rb_raise(rb_eRuntimeError, str);
268+
rb_raise(rb_eRuntimeError, "%s", str);
269269
}

lib/symengine.rb

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'backports'
2+
13
module SymEngine
24
class << self
35
# Defines a shortcut for SymEngine::Symbol.new() allowing multiple symbols
@@ -28,10 +30,42 @@ def symbols ary_or_string, *params
2830
end
2931
end
3032
def Function(n)
31-
return SymEngine::UndefFunction.new(n)
33+
SymEngine::UndefFunction.new(n)
3234
end
3335
def evalf(operand, prec=53, real=false)
34-
return _evalf(operand, prec, real)
36+
_evalf(operand, prec, real)
37+
end
38+
def lambdify(exp, *syms)
39+
syms.flatten!
40+
if exp.free_symbols.count > syms.length
41+
raise ArgumentError, "Formula contains #{exp.free_symbols.count} free "\
42+
"symbols. You should provide at least this number "\
43+
"of arguments (only #{syms.length} given)."
44+
end
45+
eval(SymEngine::Utils::lambdify_code(exp, syms))
46+
end
47+
end
48+
module Utils
49+
class << self
50+
REPLACEMENTS = { sin: 'Math.sin', cos: 'Math.cos', tan: 'Math.tan',
51+
asin: 'Math.asin', acos: 'Math.acos', atan: 'Math.atan',
52+
sinh: 'Math.sinh', cosh: 'Math.cosh', tanh: 'Math.tanh',
53+
asinh: 'Math.asinh', acosh: 'Math.acosh', atanh: 'Math.atanh',
54+
pi: 'Math::PI', E: 'Math::E', I: '::Complex::I',
55+
dirichlet_eta: 'SymEngine::Utils::evalf_dirichlet_eta',
56+
zeta: 'SymEngine::Utils::evalf_zeta', gamma: 'Math.gamma' }.map { |from, to| [/(\b#{from}\b)/, to] }.to_h.freeze
57+
def evalf_dirichlet_eta(exp)
58+
SymEngine::evalf(SymEngine::dirichlet_eta(exp))
59+
end
60+
def evalf_zeta(exp)
61+
SymEngine::evalf(SymEngine::zeta(exp))
62+
end
63+
def lambdify_code(exp, syms)
64+
body = exp.to_s.gsub(/[\d\.]+/, 'Rational(\0,1)')
65+
sym_map = syms.join(",")
66+
rubified_body = REPLACEMENTS.inject(body) { |res, (from, to)| res.gsub(from, to)}
67+
"proc { | #{sym_map} | #{rubified_body} }"
68+
end
3569
end
3670
end
3771
end

lib/symengine/basic.rb

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,25 @@ def free_symbols
1111
end
1212

1313
def abs
14-
return SymEngine::abs(self)
14+
SymEngine::abs(self)
15+
end
16+
def to_proc(*args)
17+
if args.empty?
18+
if free_symbols.count > 1
19+
raise ArgumentError, "You should provide symbols order for #to_proc"\
20+
". Only formulae with 1 free symbol can deduce"\
21+
" its name automatically (#{free_symbols.count}"\
22+
" found in #{self})."
23+
end
24+
SymEngine::lambdify(self, free_symbols.map {|s| s})
25+
else
26+
if free_symbols.count > args.length
27+
raise ArgumentError, "Formula contains #{free_symbols.count} free "\
28+
"symbols. You should provide at least this number "\
29+
"of arguments (only #{args.length} given)."
30+
end
31+
SymEngine::lambdify(self, args)
32+
end
1533
end
16-
1734
end
1835
end

spec/lambdify_spec.rb

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
describe 'SymEngine::lambdify_code' do
2+
3+
let(:x) { SymEngine::Symbol.new('x') }
4+
let(:y) { SymEngine::Symbol.new('y') }
5+
let(:z) { SymEngine::Symbol.new('z') }
6+
7+
def l(code)
8+
SymEngine::Utils::lambdify_code(code, [x])
9+
end
10+
11+
it 'creates lambda codes' do
12+
expect(SymEngine::Utils::lambdify_code( x + y + z, [x, y, z])).to eq("proc { | x,y,z | x + y + z }")
13+
expect(l( x + 5 )).to eq("proc { | x | Rational(5,1) + x }")
14+
expect(l( SymEngine::sin(x) )).to eq("proc { | x | Math.sin(x) }")
15+
expect(l( SymEngine::cos(x) )).to eq("proc { | x | Math.cos(x) }")
16+
expect(l( SymEngine::tan(x) )).to eq("proc { | x | Math.tan(x) }")
17+
expect(l( SymEngine::asin(x) )).to eq("proc { | x | Math.asin(x) }")
18+
expect(l( SymEngine::acos(x) )).to eq("proc { | x | Math.acos(x) }")
19+
expect(l( SymEngine::atan(x) )).to eq("proc { | x | Math.atan(x) }")
20+
expect(l( SymEngine::sinh(x) )).to eq("proc { | x | Math.sinh(x) }")
21+
expect(l( SymEngine::cosh(x) )).to eq("proc { | x | Math.cosh(x) }")
22+
expect(l( SymEngine::tanh(x) )).to eq("proc { | x | Math.tanh(x) }")
23+
expect(l( SymEngine::asinh(x) )).to eq("proc { | x | Math.asinh(x) }")
24+
expect(l( SymEngine::acosh(x) )).to eq("proc { | x | Math.acosh(x) }")
25+
expect(l( SymEngine::atanh(x) )).to eq("proc { | x | Math.atanh(x) }")
26+
expect(l( SymEngine::gamma(x) )).to eq("proc { | x | Math.gamma(x) }")
27+
expect(l( x + SymEngine::PI )).to eq("proc { | x | x + Math::PI }")
28+
expect(l( x + SymEngine::E )).to eq("proc { | x | x + Math::E }")
29+
expect(l( x * SymEngine::I )).to eq("proc { | x | ::Complex::I*x }")
30+
expect(l( SymEngine::dirichlet_eta(x) )).to eq("proc { | x | SymEngine::Utils::evalf_dirichlet_eta(x) }")
31+
expect(l( SymEngine::zeta(x) )).to eq("proc { | x | SymEngine::Utils::evalf_zeta(x, Rational(1,1)) }")
32+
33+
34+
end
35+
end
36+
37+
describe 'SymEngine::lambdify' do
38+
39+
let(:x) { SymEngine::Symbol.new('x') }
40+
let(:y) { SymEngine::Symbol.new('y') }
41+
let(:z) { SymEngine::Symbol.new('z') }
42+
43+
describe 'lambda for Addition' do
44+
let(:func) { x + y + z }
45+
let(:lamb) { SymEngine::lambdify(func, x, y, z) }
46+
it 'performs addition with a lambda function' do
47+
expect(lamb.call(1, 1, 1)).to eq(3)
48+
expect(lamb.call(1, -1, 1)).to eq(1)
49+
expect(lamb.call(-1, -1, -1)).to eq(-3)
50+
end
51+
end
52+
53+
describe 'lambda for Addition with FixNums' do
54+
let(:func) { x + 5}
55+
let(:lamb) { SymEngine::lambdify(func, [x]) }
56+
it 'performs addition with a lambda function' do
57+
expect(lamb.call(1)).to eq(6)
58+
expect(lamb.call(0)).to eq(5)
59+
expect(lamb.call(-1)).to eq(4)
60+
expect(lamb.call(Math::PI)).to be_within(1e-15).of(8.141592653589793)
61+
end
62+
end
63+
64+
describe 'lambda for sin' do
65+
let(:func) { SymEngine::sin(x) }
66+
let(:lamb) { SymEngine::lambdify(func, [x]) }
67+
it 'performs sin calculation with a lambda function' do
68+
expect(lamb.call(0)).to be_within(1e-15).of(0.0)
69+
expect(lamb.call(Math::PI)).to be_within(1e-15).of(0.0)
70+
expect(lamb.call(Math::PI/2)).to be_within(1e-15).of(1.0)
71+
end
72+
end
73+
74+
describe 'to_proc' do
75+
let(:func) { x * 10 }
76+
let(:func2) { x + y + 10}
77+
it 'creates procs' do
78+
expect([1, 2, 3, 4, 5].map(&func)).to eq([10, 20, 30, 40, 50])
79+
expect { [[1, 2], [3, 4]].map(&func2) }.to raise_error(ArgumentError)
80+
expect([[1, 2], [3, 4]].map(&func2.to_proc(x, y))).to eq([13, 17])
81+
end
82+
end
83+
84+
end

symengine.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ Gem::Specification.new do |gem|
1616
gem.add_development_dependency 'rspec', '~> 3.0'
1717
gem.add_development_dependency 'rspec-its'
1818
gem.add_development_dependency 'rdoc', '~> 4.0'
19+
gem.add_runtime_dependency 'backports'
1920
end

0 commit comments

Comments
 (0)