-
Notifications
You must be signed in to change notification settings - Fork 216
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
Have a clear interface to and from sympy, deal with function calls in a language-independent way #51
Comments
I don't think we should require use of a Incidentally, I'm beginning to have doubts about the use of |
Well, I don't see a strong need for it, but probably you are right.
Um, I don't think sympy has support for all that, actually. AFAICT you can only use
So I think there are three ways to deal with this issue:
The second option is considerably more work than the current approach (and potentially ties our implementation strongly to sympy, e.g. stuff might break when sympy updates) but would allow us to directly translate functions and operators without any The third approach is probably the most work (if we want to get it right), but maybe feasible building on the existing python AST parsing. Creating C or Python code from a parse tree representation would then be quite straightforward, even when it involves replacing |
I'm beginning to think that it might not be so difficult to implement our own mini language actually. Here is a sample implementation that converts import re
def get_identifiers(expr):
return set(re.findall(r'\b[A-Za-z_][A-Za-z0-9_]*\b', expr))
class Var(object):
def __init__(self, name):
self.name = name
self.items = []
def __mul__(self, other):
return MulVar(self, other)
def __add__(self, other):
return AddVar(self, other)
def __pow__(self, other):
return PowVar(self, other)
def __call__(self, *args):
return FuncVar(self, *args)
def __str__(self):
return self.name
class OperatorVar(Var):
def __init__(self, left, right):
self.items = [left, right]
left = property(fget=lambda self: self.items[0])
right = property(fget=lambda self: self.items[1])
def __str__(self):
return '(%s)%s(%s)'%(str(self.left), self.op, str(self.right))
class AddVar(OperatorVar):
op = '+'
class MulVar(OperatorVar):
op = '*'
class PowVar(OperatorVar):
op = '**'
class FuncVar(Var):
def __init__(self, func, *args):
self.items = [func]+list(args)
func = property(fget=lambda self: self.items[0])
args = property(fget=lambda self: self.items[1:])
def __str__(self):
argslist = ', '.join(str(arg) for arg in self.args)
return '%s(%s)'%(str(self.func), argslist)
def parse(expr):
varnames = get_identifiers(expr)
ns = dict((varname, Var(varname)) for varname in varnames)
return eval(expr, ns)
def replace_pow(var):
newitems = []
if isinstance(var, PowVar):
var = FuncVar(Var('pow'), var.left, var.right)
else:
var.items = [replace_pow(item) for item in var.items]
return var
x = parse('a+b*c+d(e)+f**g')
print x
print replace_pow(x) The result is:
This is based on how sympy works internally, but as you say if we have our own system we're not dependent on sympy version changes and we have more control to do what we want. What do you think? |
I'm tending towards this approach as well. I'm a bit hesitant regarding the The good thing is that we would be very explicit about what operations we support (instead of saying something like: "everything that sympy understands + rand() and randn()") and this would also be tied to the function mechanism in code generation, so we can do the function name translations for C without using |
Um, actually |
It's a shame we can't use I also like that it's really explicit and lets us control it. Another benefit is that we can do Java output which isn't supported by sympy I think. I could expand the snippet above into a little module, probably to be included in codegen somewhere. I'll start an issue for it and we can write a list of requirements for the system before I go ahead. I'll write more there. |
Actually, preventing users from using |
Agreed! |
Ok, I added a
Step 1 + 2 are handled by
The parsing currently treats all numbers in equations as floats, I think that is the safest option for now. |
Nice! Agreed on numbers in equations being floats. |
We need to make clear which strings are interpreted by sympy and which are not. Most of the time using sympy and executing the string in Python/C gives the same result, but there are important differences, e.g. integer fractions (see #38 ).
In principle we want to use sympy only for "mathematics" (differential equations, state updater descriptions), not for "programming" (reset statements, pre/post codes). On the other hand, using a sympy expression allows for using the CCode printer, which replaces some function calls and
**
bypow
. But we also already have a mechanism in place that deals with functions directly.Proposed procedure:
parse_to_sympy
andsympy_to_str
inbrian2.codegen.parsing
.Function
system in code generation (e.g. in C we would rather use#define abs fabs
instead of using sympy to replaceabs
calls byfabs
)The only thing I'm not clear about is the translation about
**
-- maybe we simply require the use of apower
function? Note that this is not as bad as it sounds, it would not affect equations (there we could even support^
) but only abstract code where this will not be used often, anyway.The text was updated successfully, but these errors were encountered: