symengine.py icon indicating copy to clipboard operation
symengine.py copied to clipboard

List of inconsistency with sympy

Open nkanazawa1989 opened this issue 3 years ago • 5 comments

I found several syntactic inconsistency with SymPy. Sorry I was bit lazy to write separate issues.

  • symengine 0.9.2
  • macOS Monterey 12.3.1 (Intel)

1. Missing ITE syntax

(In sympy)

from sympy import ITE

It works.

(In symengine)

from symengine import ITE

ImportError: cannot import name 'ITE' from 'symengine'

Alternative solution

Use Piecewise, e.g.

if flag1 is True:
    return 1
else:
    return 2

can be translated into

symengine.Piecewise((1, flag1 == symengine.true), (2, symengine.true))

however

sympy.ITE(flag1, 1, 2)

looks more intuitive to me.

2. Lambdify with a single function

(In sympy)

import sympy as sym
t, a, b = sym.symbols("t, a, b")
f = a * t + b
lamb_f = sym.lambdify([t, a, b], f)

It works

(In symengine)

import symengine as sym
t, a, b = sym.symbols("t, a, b")
f = a * t + b
lamb_f = sym.lambdify([t, a, b], f)

TypeError: Value after * must be an iterable, not Add

Alternative solution

Call lamdify with sym.lambdify([t, a, b], [f]), but this looks bit cumbersome (and result is always a list).

3. Evaluate lambda function with inhomogeneous-shape arguments

(In sympy)

import numpy as np
import sympy as sym
t, a, b = sym.symbols("t, a, b")
f = a * t + b
lamb_f = sym.lambdify([t, a, b], f)

t = np.linspace(0, 1, 10)
lamb_f(t, 1, 0.2)

array([0.2 , 0.31111111, 0.42222222, 0.53333333, 0.64444444, 0.75555556, 0.86666667, 0.97777778, 1.08888889, 1.2 ])

(In symengine)

import numpy as np
import symengine as sym
t, a, b = sym.symbols("t, a, b")
f = a * t + b
lamb_f = sym.lambdify([t, a, b], [f])

t = np.linspace(0, 1, 10)
lamb_f(t, 1, 0.2)

ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (3,) + inhomogeneous part.

Alternative solution

No. We need to repeatedly call lamb_f which is heavy overhead with python (probably I just don't know another solution).

4. Inconsistent logical operation

(In sympy)

import sympy as sym
a = sym.Symbol("a")

expr = (0 < a) & (a < 1)

It works (preferably we should be able to write 0 < a < 1 in sympy but this raises TypeError: cannot determine truth value of Relational, anyways this is not the issue for symengine).

(In symengine)

import symengine as sym
a = sym.Symbol("a")

expr = (0 < a) & (a < 1)
expr

TypeError: unsupported operand type(s) for &: 'StrictLessThan' and 'StrictLessThan'

Alternative solution

Interestingly, if we write

import symengine as sym
a = sym.Symbol("a")

expr = 0 < a < 1

this omits the first condition, i.e. a < 1. This returns unexpected value when a is negative value.

expr.subs(a, -1)

True

This should be False. Likely it's a bug. It returns the same expression when I write (0 < a) and (a < 1).

nkanazawa1989 avatar Apr 18 '22 16:04 nkanazawa1989

  1. Easiest option here is to define ITE as a python function that returns a piecewise. Other option is to implement in C++
  2. sym.Lambdify works instead of sym.lambdify there. @bjodah I can't remember the details. Do you remember why?
  3. That's something we can fix here.
  4. Should be easy to implement __and__ at https://github.com/symengine/symengine.py/blob/2ae44220caf26ea529463304d6364a1b741bd503/symengine/lib/symengine_wrapper.pyx#L1525

isuruf avatar Apr 18 '22 16:04 isuruf

I seem to recall ambiguities when broadcasting, but I'm not sure, perhaps it could be implemented without too much work.

bjodah avatar Apr 18 '22 17:04 bjodah

expr = (0 < a) & (a < 1)

& is a bitwise operator and and is the logical operator. However, there's no way to override and in python. Therefore sympy uses & which I think is a mistake.

Can you use sym.And(0< a, a < 1) which is unambiguous?

isuruf avatar Apr 18 '22 21:04 isuruf

Thanks @isuruf I confirmed sym.And is sympy compatible. Seems like this is correct syntax.

import sympy as sym
a = sym.Symbol("a")

expr = sym.And(a < 1, 0 < a)
expr.subs(a, -1)

False

import symengine as sym
a = sym.Symbol("a")

expr = sym.And(a < 1, 0 < a)
expr.subs(a, -1)

False

nkanazawa1989 avatar Apr 19 '22 04:04 nkanazawa1989

https://github.com/symengine/symengine.py/issues/396 was another inconsistency along the lines of item 2. I closed that one after isuruf showed the syntax that worked. I wasn't sure how faithfully symengine is supposed to mirror the sympy API.

wshanks avatar Apr 21 '22 18:04 wshanks