Skip to content

[WIP] Exception Handling #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
21 changes: 20 additions & 1 deletion ext/symengine/ruby_basic.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,26 @@ VALUE cbasic_mul(VALUE self, VALUE operand2)

VALUE cbasic_div(VALUE self, VALUE operand2)
{
return cbasic_binary_op(self, operand2, basic_div);
basic_struct *this, *cresult;
VALUE result;

basic cbasic_operand2;
basic_new_stack(cbasic_operand2);

Data_Get_Struct(self, basic_struct, this);
sympify(operand2, cbasic_operand2);

cresult = basic_new_heap();
int error_code = basic_div(cresult, this, cbasic_operand2);

if (error_code == 0) {
result = Data_Wrap_Struct(Klass_of_Basic(cresult), NULL,
cbasic_free_heap, cresult);
basic_free_stack(cbasic_operand2);
return result;
} else {
rb_raise(rb_eRuntimeError, "Runtime Error");
}
}

VALUE cbasic_pow(VALUE self, VALUE operand2)
Expand Down
31 changes: 29 additions & 2 deletions lib/symengine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,37 @@ def symbols ary_or_string, *params
end
end
def Function(n)
return SymEngine::UndefFunction.new(n)
SymEngine::UndefFunction.new(n)
end
def evalf(operand, prec=53, real=false)
return _evalf(operand, prec, real)
_evalf(operand, prec, real)
end
def lambdify(exp, *syms)
eval(SymEngine::Utils::lambdify_code(exp, syms))
end
end
module Utils
class << self
REPLACEMENTS = { sin: 'Math.sin', cos: 'Math.cos', tan: 'Math.tan',
asin: 'Math.asin', acos: 'Math.acos', atan: 'Math.atan',
sinh: 'Math.sinh', cosh: 'Math.cosh', tanh: 'Math.tanh',
asinh: 'Math.asinh', acosh: 'Math.acosh', atanh: 'Math.atanh',
pi: 'Math::PI', E: 'Math::E', I: '::Complex::I',
dirichlet_eta: 'SymEngine::Utils::evalf_dirichlet_eta',
zeta: 'SymEngine::Utils::evalf_zeta', gamma: 'Math.gamma' }.map { |from, to| [/(\b#{from}\b)/, to] }.to_h.freeze
def evalf_dirichlet_eta(exp)
SymEngine::evalf(SymEngine::dirichlet_eta(exp))
end
def evalf_zeta(exp)
SymEngine::evalf(SymEngine::zeta(exp))
end
def lambdify_code(exp, syms)
str = exp.to_s
sym_map = syms.join(",")
str.gsub!(/[\d\.]+/, 'Rational(\0,1)')
str = REPLACEMENTS.inject(str) { |res, (from, to)| res.gsub(from, to)}
"lambda { | #{sym_map} | #{str} }"
end
end
end
end
Expand Down
16 changes: 14 additions & 2 deletions lib/symengine/basic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,20 @@ def free_symbols
end

def abs
return SymEngine::abs(self)
SymEngine::abs(self)
end
def to_proc(*args)
if(args.length == 0)
if self.free_symbols.count > 1
raise ArgumentError, "Too many free symbols! Only 1 allowed. Found #{self.free_symbols.count}."
end
SymEngine::lambdify(self, self.free_symbols.map {|s| s})
else
if self.free_symbols.count > args.length
raise ArgumentError, "#{self.free_symbols.count} Free Symbols. Only #{args.length} given."
end
SymEngine::lambdify(self, args)
end
end

end
end
84 changes: 84 additions & 0 deletions spec/lambdify_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
describe 'SymEngine::lambdify_code' do

let(:x) { SymEngine::Symbol.new('x') }
let(:y) { SymEngine::Symbol.new('y') }
let(:z) { SymEngine::Symbol.new('z') }

def l(code)
SymEngine::Utils::lambdify_code(code, [x])
end

it 'creates lambda codes' do
expect(SymEngine::Utils::lambdify_code( x + y + z, [x, y, z])).to eq("lambda { | x,y,z | x + y + z }")
expect(l( x + 5 )).to eq("lambda { | x | Rational(5,1) + x }")
expect(l( SymEngine::sin(x) )).to eq("lambda { | x | Math.sin(x) }")
expect(l( SymEngine::cos(x) )).to eq("lambda { | x | Math.cos(x) }")
expect(l( SymEngine::tan(x) )).to eq("lambda { | x | Math.tan(x) }")
expect(l( SymEngine::asin(x) )).to eq("lambda { | x | Math.asin(x) }")
expect(l( SymEngine::acos(x) )).to eq("lambda { | x | Math.acos(x) }")
expect(l( SymEngine::atan(x) )).to eq("lambda { | x | Math.atan(x) }")
expect(l( SymEngine::sinh(x) )).to eq("lambda { | x | Math.sinh(x) }")
expect(l( SymEngine::cosh(x) )).to eq("lambda { | x | Math.cosh(x) }")
expect(l( SymEngine::tanh(x) )).to eq("lambda { | x | Math.tanh(x) }")
expect(l( SymEngine::asinh(x) )).to eq("lambda { | x | Math.asinh(x) }")
expect(l( SymEngine::acosh(x) )).to eq("lambda { | x | Math.acosh(x) }")
expect(l( SymEngine::atanh(x) )).to eq("lambda { | x | Math.atanh(x) }")
expect(l( SymEngine::gamma(x) )).to eq("lambda { | x | Math.gamma(x) }")
expect(l( x + SymEngine::PI )).to eq("lambda { | x | x + Math::PI }")
expect(l( x + SymEngine::E )).to eq("lambda { | x | x + Math::E }")
expect(l( x * SymEngine::I )).to eq("lambda { | x | ::Complex::I*x }")
expect(l( SymEngine::dirichlet_eta(x) )).to eq("lambda { | x | SymEngine::Utils::evalf_dirichlet_eta(x) }")
expect(l( SymEngine::zeta(x) )).to eq("lambda { | x | SymEngine::Utils::evalf_zeta(x, Rational(1,1)) }")


end
end

describe 'SymEngine::lambdify' do

let(:x) { SymEngine::Symbol.new('x') }
let(:y) { SymEngine::Symbol.new('y') }
let(:z) { SymEngine::Symbol.new('z') }

describe 'lambda for Addition' do
let(:func) { x + y + z }
let(:lamb) { SymEngine::lambdify(func, [x, y, z]) }
it 'performs addition with a lambda function' do
expect(lamb.call(1, 1, 1)).to eq(3)
expect(lamb.call(1, -1, 1)).to eq(1)
expect(lamb.call(-1, -1, -1)).to eq(-3)
end
end

describe 'lambda for Addition with FixNums' do
let(:func) { x + 5}
let(:lamb) { SymEngine::lambdify(func, [x]) }
it 'performs addition with a lambda function' do
expect(lamb.call(1)).to eq(6)
expect(lamb.call(0)).to eq(5)
expect(lamb.call(-1)).to eq(4)
expect(lamb.call(Math::PI)).to be_within(1e-15).of(8.141592653589793)
end
end

describe 'lambda for sin' do
let(:func) { SymEngine::sin(x) }
let(:lamb) { SymEngine::lambdify(func, [x]) }
it 'performs sin calculation with a lambda function' do
expect(lamb.call(0)).to be_within(1e-15).of(0.0)
expect(lamb.call(Math::PI)).to be_within(1e-15).of(0.0)
expect(lamb.call(Math::PI/2)).to be_within(1e-15).of(1.0)
end
end

describe 'to_proc' do
let(:func) { x * 10 }
let(:func2) { x + y + 10}
it 'creates procs' do
expect([1, 2, 3, 4, 5].map(&func)).to eq([10, 20, 30, 40, 50])
expect { [[1, 2], [3, 4]].map(&func2) }.to raise_error(ArgumentError)
expect([[1, 2], [3, 4]].map(&func2.to_proc(x, y))).to eq([13, 17])
end
end

end