Skip to content

Sympy compat: add a functions submodule, add atan2 #118

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

Merged
merged 3 commits into from
Feb 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions symengine/lib/symengine.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ cdef extern from "<symengine/basic.h>" namespace "SymEngine":
bool is_a_ComplexMPC "SymEngine::is_a<SymEngine::ComplexMPC>"(const Basic &b) nogil
bool is_a_Log "SymEngine::is_a<SymEngine::Log>"(const Basic &b) nogil
bool is_a_PyNumber "SymEngine::is_a<SymEngine::PyNumber>"(const Basic &b) nogil
bool is_a_ATan2 "SymEngine::is_a<SymEngine::ATan2>"(const Basic &b) nogil

RCP[const Basic] expand(RCP[const Basic] &o) nogil except +

Expand Down Expand Up @@ -417,6 +418,7 @@ cdef extern from "<symengine/functions.h>" namespace "SymEngine":
cdef RCP[const Basic] function_symbol(string name, const vec_basic &arg) nogil except+
cdef RCP[const Basic] abs(RCP[const Basic] &arg) nogil except+
cdef RCP[const Basic] gamma(RCP[const Basic] &arg) nogil except+
cdef RCP[const Basic] atan2(RCP[const Basic] &num, RCP[const Basic] &den) nogil except+

cdef cppclass Function(Basic):
pass
Expand Down Expand Up @@ -512,6 +514,9 @@ cdef extern from "<symengine/functions.h>" namespace "SymEngine":
cdef cppclass Gamma(Function):
pass

cdef cppclass ATan2(Function):
pass

IF HAVE_SYMENGINE_MPFR:
cdef extern from "mpfr.h":
ctypedef struct __mpfr_struct:
Expand Down
3 changes: 3 additions & 0 deletions symengine/lib/symengine_wrapper.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ cdef class Abs(Function):
cdef class Gamma(Function):
pass

cdef class ATan2(Function):
pass

cdef class Derivative(Basic):
pass

Expand Down
9 changes: 9 additions & 0 deletions symengine/lib/symengine_wrapper.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ cdef c2py(RCP[const symengine.Basic] o):
r = ATanh.__new__(ATanh)
elif (symengine.is_a_ACoth(deref(o))):
r = ACoth.__new__(ACoth)
elif (symengine.is_a_ATan2(deref(o))):
r = ATan2.__new__(ATan2)
elif (symengine.is_a_PyNumber(deref(o))):
r = PyNumber.__new__(PyNumber)
else:
Expand Down Expand Up @@ -169,6 +171,8 @@ def sympy2symengine(a, raise_error=False):
return acsc(a.args[0])
elif isinstance(a, sympy.asec):
return asec(a.args[0])
elif isinstance(a, sympy.atan2):
return atan2(*a.args)
elif isinstance(a, sympy.functions.elementary.hyperbolic.HyperbolicFunction):
if isinstance(a, sympy.sinh):
return sinh(a.args[0])
Expand Down Expand Up @@ -2159,6 +2163,11 @@ def gamma(x):
cdef Basic X = sympify(x)
return c2py(symengine.gamma(X.thisptr))

def atan2(x, y):
cdef Basic X = sympify(x)
cdef Basic Y = sympify(y)
return c2py(symengine.atan2(X.thisptr, Y.thisptr))

def eval_double(x):
cdef Basic X = sympify(x)
return c2py(<RCP[const symengine.Basic]>(symengine.real_double(symengine.eval_double(deref(X.thisptr)))))
Expand Down
76 changes: 53 additions & 23 deletions symengine/sympy_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .lib.symengine_wrapper import (Symbol, sympify, sympify as S,
SympifyError, sqrt, I, E, pi, Matrix, Derivative, exp,
Lambdify as lambdify, symarray, diff, zeros, eye, diag, ones, zeros,
expand, FunctionSymbol as AppliedUndef)
expand, Subs, FunctionSymbol as AppliedUndef)


class BasicMeta(type):
Expand Down Expand Up @@ -64,158 +64,188 @@ def __new__(cls, name):
return symengine.UndefFunction(name)


class log(Function):
from types import ModuleType

functions = ModuleType(__name__ + ".functions")
import sys
sys.modules[functions.__name__] = functions

functions.sqrt = sqrt
functions.exp = exp


class _FunctionRegistrarMeta(BasicMeta):

def __new__(mcls, name, bases, dict):
cls = BasicMeta.__new__(mcls, name, bases, dict)
if not name.startswith("_"):
setattr(functions, name, cls)
return cls
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is name attribute the main purpose of this? If so, you can add it to, https://github.com/symengine/symengine.py/blob/master/symengine/lib/symengine_wrapper.pyx#L1092-L1100

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main purpose of this is to register all the special functions in this current file into the functions submodule so that it behaves like sympy.functions (as an alternative, I could just go ahead and make a separate file for the module). I'm not 100% sure how your suggestion fits into this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, then that's fine. Can you move setting the name attribute to symengine_wrapper.pyx? Otherwise (sin(x) + 1) - 1 wouldn't have the name attribute, since it's only added if the function is directly created.

Copy link
Contributor Author

@mattwala mattwala Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont't think I understand what you mean, could you please explain a bit more? This code is not trying to add an attribute to the expression (sin(x) + 1) - 1). It's operating on the class sympy_compat.sin when gets created as a class (metaclass registry pattern).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. It's operating on the class, not the object.



class _RegisteredFunction(with_metaclass(_FunctionRegistrarMeta, Function)):
pass


class log(_RegisteredFunction):
_classes = (symengine.Log,)

def __new__(cls, a, b = E):
return symengine.log(a, b)


class sin(Function):
class sin(_RegisteredFunction):
_classes = (symengine.Sin,)

def __new__(cls, a):
return symengine.sin(a)


class cos(Function):
class cos(_RegisteredFunction):
_classes = (symengine.Cos,)

def __new__(cls, a):
return symengine.cos(a)


class tan(Function):
class tan(_RegisteredFunction):
_classes = (symengine.Tan,)

def __new__(cls, a):
return symengine.tan(a)

class gamma(Function):
class gamma(_RegisteredFunction):
_classes = (symengine.Gamma,)

def __new__(cls, a):
return symengine.gamma(a)


class cot(Function):
class cot(_RegisteredFunction):
_classes = (symengine.Cot,)

def __new__(cls, a):
return symengine.cot(a)


class csc(Function):
class csc(_RegisteredFunction):
_classes = (symengine.Csc,)

def __new__(cls, a):
return symengine.csc(a)


class sec(Function):
class sec(_RegisteredFunction):
_classes = (symengine.Sec,)

def __new__(cls, a):
return symengine.sec(a)


class asin(Function):
class asin(_RegisteredFunction):
_classes = (symengine.ASin,)

def __new__(cls, a):
return symengine.asin(a)


class acos(Function):
class acos(_RegisteredFunction):
_classes = (symengine.ACos,)

def __new__(cls, a):
return symengine.acos(a)


class atan(Function):
class atan(_RegisteredFunction):
_classes = (symengine.ATan,)

def __new__(cls, a):
return symengine.atan(a)


class acot(Function):
class acot(_RegisteredFunction):
_classes = (symengine.ACot,)

def __new__(cls, a):
return symengine.acot(a)


class acsc(Function):
class acsc(_RegisteredFunction):
_classes = (symengine.ACsc,)

def __new__(cls, a):
return symengine.acsc(a)


class asec(Function):
class asec(_RegisteredFunction):
_classes = (symengine.ASec,)

def __new__(cls, a):
return symengine.asec(a)


class sinh(Function):
class sinh(_RegisteredFunction):
_classes = (symengine.Sinh,)

def __new__(cls, a):
return symengine.sinh(a)


class cosh(Function):
class cosh(_RegisteredFunction):
_classes = (symengine.Cosh,)

def __new__(cls, a):
return symengine.cosh(a)


class tanh(Function):
class tanh(_RegisteredFunction):
_classes = (symengine.Tanh,)

def __new__(cls, a):
return symengine.tanh(a)


class coth(Function):
class coth(_RegisteredFunction):
_classes = (symengine.Coth,)

def __new__(cls, a):
return symengine.coth(a)


class asinh(Function):
class asinh(_RegisteredFunction):
_classes = (symengine.ASinh,)

def __new__(cls, a):
return symengine.asinh(a)


class acosh(Function):
class acosh(_RegisteredFunction):
_classes = (symengine.ACosh,)

def __new__(cls, a):
return symengine.acosh(a)


class atanh(Function):
class atanh(_RegisteredFunction):
_classes = (symengine.ATanh,)

def __new__(cls, a):
return symengine.atanh(a)


class acoth(Function):
class acoth(_RegisteredFunction):
_classes = (symengine.ACoth,)

def __new__(cls, a):
return symengine.acoth(a)


class atan2(_RegisteredFunction):
_classes = (symengine.ATan2,)

def __new__(cls, a, b):
return symengine.atan2(a, b)

'''
for i in ("""Sin Cos Tan Gamma Cot Csc Sec ASin ACos ATan
ACot ACsc ASec Sinh Cosh Tanh Coth ASinh ACosh ATanh
Expand Down
12 changes: 11 additions & 1 deletion symengine/tests/test_sympy_compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from symengine.sympy_compat import (Integer, Rational, S, Basic, Add, Mul,
Pow, symbols, Symbol, log, sin, zeros)
Pow, symbols, Symbol, log, sin, zeros, atan2)

def test_Integer():
i = Integer(5)
Expand Down Expand Up @@ -50,6 +50,16 @@ def test_log():
i = log(x)
assert isinstance(i, log)

def test_ATan2():
x, y = symbols("x y")
i = atan2(x, y)
assert isinstance(i, atan2)
i = atan2(0, 1)
assert i == 0

def test_zeros():
assert zeros(3, c=2).shape == (3, 2)

def test_has_functions_module():
import symengine.sympy_compat as sp
assert sp.functions.sin(0) == 0