Should the Symbolic Math Toolbox Enforce Assumptions on Assignment?

4 views (last 30 days)
Paul on 23 Aug 2021
Commented: Walter Roberson on 17 Sep 2021
Consider the following:
syms a real
isAlways(in(a,'real'))
ans = logical
1
So far, so good. But
a = sym(2)*1i; % accepted w/o error or warning
isAlways(in(a,'real'))
ans = logical
0
assumptions
ans = Walter Roberson on 14 Sep 2021
a = sym(2)*1i; % accepted w/o error or warning
As usual in MATLAB, if you are not using Simulink or MATLAB Coder, then the type of a variable is dynamic, and anything on the right side of an assignment completely overwrites whatever was in the left side of the assignment if it is an unindexed variable.
syms a real
That code is the same as
assignin('caller', 'a', sym('a', 'real'))
executed inside the syms function. So what is being assigned to a is the result of the sym function. MATLAB is not making a symbol-table entry marked as real: MATLAB is assigning an object to the MATLAB variable a
What properties does the object have? It turns out that the object does not store any information about assumptions. Instead, each symbolic object at the MATLAB level has amain properties: the s field which is a MATLAB character vector that is a reference to something that exists inside the Symbolic Engine.
When a + 1 is executed at the MATLAB level, the plus() operator is executed on the symbolic object (because the MATLAB level variable is a symbolic object), and the internal symbolic reference is looked up, and looks something like '_symans_[[32,0,59723]]' and that the 1 is converted to symbolic and has a similar symengine vector, and the two character vectors are passed to the symbolic engine. The symbolic engine looks them up in its internal tables and figures out how to add the two.
When you do
a = sym(2)*1i
then 2 is converted to symbolic number, creating a symbolic object with an s field like '_symans_[[32,0,59724]]' and the 1i is converted to symbolic number, getting something much the same in the s field, and the two _symans values are passed to the symbolic engine which does the appropriate work and returns a new character internal reference character vector that is stored in the s field of a symbolic object. Then, that symbolic object overwrites the MATLAB level object without ever "informing" the existing MATLAB level object, because in MATLAB assignment of an unindexed variable only involves the existing content to the extent that the existing content has its reference count reduced and might possibly be deleted.
When the sym('a','real') part was done, the name 'a' was passed to the symbolic engine to ask for its reference to symbolic engine variable named a and the assumption 'real' and the symbolic engine reference are sent to the symbolic engine, and the symbolic engine marks its internal symbolic engine variable a with the assumption -- the assumption makes no change at the MATLAB level (but might potentially return a different internal reference to be stored.) The MATLAB level has no idea what assumptions are stored against which expressions.
Remember that it is perfectly valid to do
syms a positive
a = a - 1
If you try to interpret this as saying that some kind of absolute variable named a exists at the MATLAB level, and should thereafter have all the properties from the assumptions, then you come to the conclusion that you must be asserting an identity, that a is a value such that a and a - 1 are the same value . You must, in other words, be asserting that a is one of -infinity, +infinity, or NaN ("undefined" as it is known to the symbolic engine.) But with the positivity constraint, you would have to be narrowing that down to say that a must be positive, so the combination would require that a is +infinity . This is, of course, as much incorrect at the MATLAB level as would be the case for saying that
a = 2
a = a - 1
"must" be asserting that a becomes the set of values such that a = 2 and a = 1 both hold -- that just isn't the case !
At the numeric level like that, when the a - 1 is seen in the second line, the current content of a is copied into the expression, and (content of a) - 1 is executed, and the result is stored over top of a .
Just so, symbolically, when you do
syms a positive
a = a - 1
then when you get to the a - 1 part, the current content of a is copied into the expression, with that being a reference to a symbolic engine variable, and then the - 1 part is executed in the symbolic level, and a new symbolic reference is returned, and that symbolic reference overwrites what is stored at the MATLAB level. The new symbolic engine reference returned after the a - 1 is to something that lives in the symbolic engine and has a reference to the symbolic engine variable a and a subtraction, but there is a distinction between the symbolic engine variable a and the MATLAB level variable a
Thus, symbolic variables have exactly the same semantics as numeric variables: content is copied in as needed on the right-hand side, new values are created as needed by the calculation on the right, and the new value overwrites the variable on the left side without the current content of the variable on the left side having anything to say about the matter.
... and as usual, you can force the current variable to be permitted to have a say in the matter by using
a(:) = a - 1
which would go through the subsasgn method associated with the existing a variable and allowing that method to do whatever is meaningful under the circumstances.
Allowing syms a postive to have influence on what happens with a = sym(2)*1i would require a complete change to MATLAB assignment semantics.
And you would have to figure out how to deal with syms a positive; a = a - 1 . For example syms a positive is the same as a = sym('a'); assume(a > 0) so to maintain those constraints in the face of a = a - 1 should the result be that a is still just a name, but that the assumption on a gets updated to (a > 1) and otherwise the -1 part should be discarded ????
Walter Roberson on 17 Sep 2021
The current symbolic system is arguably broken
syms w positive
test_assume(w)
general assumptions
ans = ans = is parameter always positive?
ans = logical
1
assumptions after syms w ans = Empty sym: 1-by-0 is parameter always positive now?
Warning: Unable to prove '0 < w'.
ans = logical
0
disp('assumptions after returning'); assumptions()
assumptions after returning ans = Empty sym: 1-by-0
function test_assume(x)
disp('general assumptions'); assumptions()
disp('is parameter always positive?'); isAlways(x>0)
syms w
disp('assumptions after syms w'); assumptions
disp('is parameter always positive now?'); isAlways(x>0)
end
One could argue that inside the function, w should be a local variable, and therefore changing assumptions about w in the function should not be changing assumptions outside. But assumptions live inside the symbolic engine, and "syms w" talks about the symbolic variable named w that lives in the engine.
This does mean that symbolic variable names that you use inside functions are effectively global, and so you need to be very careful about them. But hardly anyone is... I know it is something I seldom think about myself when I am writing a function.

Tanmay Das on 13 Sep 2021
Hi,
Symbolic objects and their assumptions are stored separately. When you set an assumption that a is real using
syms a real
you actually create a symbolic object a and the assumption that the object is real. The object is stored in the MATLAB workspace, and the assumption is stored in the symbolic engine. When you replace a symbolic object from the MATLAB workspace using
a = sym(2)*1i;
the assumption that a is real stays in the symbolic engine. If you declare a new symbolic variable a later using sym, it inherits the assumption that a is real instead of getting a default assumption. To clear the assumption, enter
syms a
Please refer to Use Assumptions on Symbolic Variables documention for further information.
However, in() function refers to a variable from the MATLAB workspace and hence has the latest status of its numeric type. So a possible workaround can be the following:
syms a real
isAlways(in(a,'real'))
syms a % to clear the earlier assumption
a = sym(2)*1i;
isAlways(in(a,'real'))
assumptions
Tanmay Das on 16 Sep 2021
Regarding automatically updating assumptions, MATLAB does not inform when it deletes a MATLAB variable or assigns new values to it. So the assumptions, which are stored within the Symbolic engine, cannot be removed automatically. Furthermore, such a call into Symbolic may lead to slowdown MATLAB execution in general. Also, it is complicated to decide when to raise warning and when not to. Hence, it is recommended to use 'syms' to reinitialize the symbolic variable and reset all the assumptions associated with it.

R2021a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!