JuliaPy / SymPy.jl

Julia interface to SymPy via PyCall
http://juliapy.github.io/SymPy.jl/
MIT License
269 stars 61 forks source link

Alternative specification of assumptions #416

Closed matthieubulte closed 3 years ago

matthieubulte commented 3 years ago

Hi, thanks for these great bindings!

I'm not a huge fan of the current way assumptions are specified, in particular the fact that assumptions do not stick to a single variable, but rather propagate to the following variables. It's not a problem if you know exactly what you are doing, but I use SymPy mainly for exploration of math expressions before I settle for anything, and having to think about the order in which variables / assumptions are declared can quickly get annoying IMO.

Here is an example showcasing the different use cases of my version of the @vars macro

@vars x::(real,positive)=>"x₀" y z::complex n::integer 

I think what it does is self-explanatory. It's really just a question of taste.

Here is the code which isn't clean or handling much edge cases

```julia function parsedecl(expr) if isa(expr, Symbol) return String(expr), expr, [] elseif isa(expr, Expr) && expr.head == :(::) sym, assumptions = expr.args assumptions = isa(assumptions, Symbol) ? (assumptions,) : assumptions.args return String(sym), sym, assumptions elseif isa(expr, Expr) && expr.head == :call && expr.args[1] == :(=>) expr, strname = expr.args[2:end] _, sym, assumptions = parsedecl(expr) return strname, sym, assumptions else error("Incorrect @vars syntax. Try `@vars x::(real,positive)=>\"x₀\" y z::complex n::integer` for instance.") end end macro vars(xs...) asstokw(a) = Expr(:kw, esc(a), true) symdefs = map(xs) do expr varname, sym, assumptions = parsedecl(expr) sym, :($(esc(sym)) = $(esc(symbols))($(varname), $(map(asstokw, assumptions)...))) end syms, defs = collect(zip(symdefs...)) Expr(:block, defs..., :(tuple($(map(esc,syms)...)))) end ```

I could work on a PR, but since this is only syntax and would break the current API, I'd understand if you would rather ignore it. Either way, I'm happy to have this for my own usage :)

EDIT: clean up code

jverzani commented 3 years ago

This would be nice. A PR would be most welcome. I would like to avoid the breakage of @vars. Here are two thoughts:

matthieubulte commented 3 years ago

we have the legacy @syms macro that is the same as @vars. It could be that instead of a breaking change of @vars we break the definition for @syms for this style?

Good idea and easy to put in place. We would just have to see how to communicate this to the users.

Alternatively, this is reminiscent of @variables from Symbolics.jl. We could use the name @variables. (One other thing that @variables allows is commas or spaces separating variables. That would be nice to add.

I think the current status in team Symbolics is to use the @syms macro defined in SymbolicsUtils (https://github.com/JuliaSymbolics/SymbolicUtils.jl/blob/master/src/types.jl#L249) which also allows to specify assumptions. So using @syms would be compliant with their naming as well. Only problem is that the declaration syntax is quite different which might be confusing?

Adding commas shouldn't be too hard (if the top-level Expr is a tuple, then it uses "comma syntax" and the list of declarations is in root.args).

EDIT. Conclusion: I will prepare a PR using the @syms macro as you suggested and with support for the comma syntax.

jverzani commented 3 years ago

Great! Look forward to it. I can update the docs once we get that merged to advertise it.