# Strange behaviour with distributivity of multiplication

I got the following code wich is not consistent. There should be a bug in there. The priority of the parenthesis is not respected in the first case.

 sage: version()
'SageMath version 9.0, Release Date: 2020-01-01'
sage: ....: Qpart(x) =5*( unit_step(x)
....: ....:     * sin(x )  -
....: ....:     sin(x))
sage: Qpart
x |--> 5*sin(x)*unit_step(x) - sin(x)
sage: ....:5*( unit_step(x)
....: ....:     * sin(x )  -
....: ....:     sin(x))
5*sin(x)*unit_step(x) - 5*sin(x)

edit retag close merge delete

Sort by » oldest newest most voted

Hello, @JonasA! This is not a bug; the problem is in your definition of Qpart. Since you are using multi-line code, you should indicate Python (and thus Sage) where it starts and where it ends. There are two ways of doing this (that I'm aware of):

1. You could use the line continuation character \ (which doesn't work inside strings):

Qpart(x) = 5 * (unit_step(x) \
* sin(x) \
- sin(x))


That clearly indicates that the first line continues with the second, and the second with the third, but the third does not continue with another line.

2. You could enclose the defining expression of Qpart with parenthesis. Python and Sage assume that an expression that starts with a ( hasn't ended until the corresponding ) is found. In order for this to work, the whole expression must be enclosed; it's not enough to have unclosed parenthesis. In this case, your code should look like this:

Qpart(x) = (5 * (unit_step(x)
* sin(x)
- sin(x)))


I don't recommend this for your formula, since you already have a certain number of parenthesis, and increasing this number could potentially make your code less clear.

However, I am baffled by the fact that your second piece of code does work. My guess is that

5 * (unit_step(x)
* sin(x)
- sin(x))


IS one single complete expression by itself, so Python is clever enough to recognize its start and its end. On the contrary, your first piece of code:

Qpart(x) = 5 * (unit_step(x)
* sin(x)
- sin(x))


is NOT one single expression by itself, since there is another one in the way, namely, Qpart(x) =, so Python could be confused by this, and this avoids understanding the complete formula.

That being said, you should always indicate that an expression continues, even is Python is clever enough to recognize it by itself. This isn't just considered a good programming practice, it also avoids potential errors in the code going unnoticed, which could be catastrophic. (And it also frees you from the responsibility of having to figure out when Python will understand a piece of code or not.)

more

In Python, the rule is that if a line ends with a bracket/brace/parenthesis waiting to be closed, then the newline is read as whitespace and the expression is considered to continue on the next line. This explains the behaviour in the second example.

In the first example, the preparser gets involved, which does not handle newlines in the same way. Indeed, you can get a syntax error by putting the newline in a slightly different position:

sage: Qpart(x) = 5*( unit_step(x)*
....: sin(x) -
....: sin(x))
SyntaxError: invalid syntax


You can see what happens if you look at what the preparser does:

sage: preparse("""Qpart(x) = 5*( unit_step(x)*
....: sin(x) -
....: sin(x))""")
'__tmp__=var("x"); Qpart = symbolic_expression(Integer(5)*( unit_step(x)*).function(x)\nsin(x) -\nsin(x))'


The rewriting only happens on the first line, regardless of whether that maintains proper syntax: it's a fairly basic regexp replacement. Ideally the preparser would actually PARSE, but that's not what it does.

more

I would say that ideally the preparer would actually preparse. ;)

( 2020-03-17 12:44:25 -0500 )edit