Skip to content

Conversation

@mmatera
Copy link
Contributor

@mmatera mmatera commented Dec 4, 2025

This PR adds a helper function for evaluating expressions, keeping unevaluated those expressions that could have side effects, like changing the value of a variable. This could be useful to convert (compile) expressions into Python functions.
Let' s consider the following WL code:

In[1]:= F[x_,y_]:=(a=x; Do[a=a+i,{y}];a)

In[2]:= Plot3D[F[x,y],{x,0,1},{y,0,1}]

F[x,y] is a convoluted way to write (x+IntegerPart[y]), but I imagine more complicated cases where this kind of construction is needed.
If we use the vectorized version of Plot3D, F[x,y] is compiled to a vectorized Python function through the functionplot_compile.

plot_compile(evaluation,expr, ...) tryies to convert F[x,y] into a Sympy expression. To do that, it needs to evaluate first F[x,y] into an expression involving just x,y, and built-in symbols. In the current implementation, this is done by first evaluating the expression expr (expr = expr.evaluate(evaluation)), and then trying to convert it to sympy. But in the case of the example, evaluating F[x,y] results in x, and leaves assigned the symbol a with the symbol x, which is an undesired side effect.

This PR should avoid the evaluation of Do, Set, and CompoundExpression. This makes the conversion to sympy fail, forcing the evaluation to use the standard method.
In another round, we could also provide suitable conversions to SymPy for these symbols, allowing a proper vectorized implementation.

self.var = var


def evaluate_without_side_effects(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if this is the right place for this function.

for name, defin in SIDE_EFFECT_BUILTINS.items():
# Change the definition by a temporal definition setting
# just the name and the attributes.
definitions.builtin[name] = Definition(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

maybe these definitions can be stored in some place, and then each time we compile something, bring them up, instead of constructing them each time.

= 2.18888
Loops and variable assignments are supported usinv Python builtin "compile" function:
>> Compile[{{a, _Integer}, {b, _Integer}}, While[b != 0, {a, b} = {b, Mod[a, b]}]; a] (* GCD of a, b *)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test was wrong in many ways. On the one hand, this expression shows the following warning in WMA:

Compile::argset: 
   The assignment to a is illegal; it is not valid to assign a value to an
     argument.

Then, if we try to evaluate the compiled function, we get more errors, and an unfinished loop.

The expected result is also wrong in another way: it expects the wrong behaviour, where the expression is fully evaluated before compiling, to get a. The new test is something that actually works in WMA, and produces the expected behavior.

"""

attributes = A_HOLD_ALL | A_PROTECTED | A_N_HOLD_ALL | A_READ_PROTECTED
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These attributes are very important: they prevent the evaluation of the original compiled expression.

@mmatera mmatera marked this pull request as ready for review December 4, 2025 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants