How to define a symbolic function that receives a vector?

9 views (last 30 days)
Hello,
I'm searching for a method to define a symbolic function that receives a vector and outputs a matrix.
Example:
x = sym('x', [3 1])
test = sym('test', [3 1])
f = symfun([x(1) 2 -x(1); 1 x(2) 3; x(3) 2 x(3)],x)
f(test) --> error Symbolic function expected 3 inputs and received 1
the problem I'm having with this is that the defined function f(x) looks like this:
f(x1, x2, x3) =
[ x1, 2, x1]
[ 1, x2, 3]
[ x3, 2, x3]
Which means that I need to pass each element of my vector separate. If I pass a matrix multiplied by a vector as an example, it would be really inconvenient to pass each component of the resulting vector. Is there a way so that f(x) directly uses a vector as the input?

Answers (2)

Walter Roberson
Walter Roberson on 16 Nov 2017
Nicolas Schmit gave a work-around that is usable in a number of contexts.
Unfortunately, that, it is not possible to define a symbolic function has having a vector argument.
The forms
f(x) = expression
and
f = symfun(expression, x)
are equivalent in that respect. In both cases, the expression will be fully evaluated symbolically first, and then the result will be attached to the symbolic function object f with x being created as the argument list. This is not the same as
f = @(x) expression
in an important way.
When an anonymous function is created, the body is scanned and any variable that is found (other than a variable mentioned in the argument list) has a copy taken and attached to the anonymous function, but the body is otherwise completely unevaluated. The variable named in the @() part does not need to exist first. For example,
clear all
A = 0
f = (x) x/A
will have a copy of A (value 0) taken and attached to the anonymous function so that if the function is ever evaluated, then no matter what A had been set to in the meantime, the copy (value 0) would be used by the anonymous function, resulting in a run-time division by 0, but not until run-time.
But the equivalent
clear all
A = 0
f = symfun(x/A, x)
needs to evaluate x/A first, which would fail because x is not defined. If you
clear all
syms x
A = 0
f = symfun(x/A, x)
then the x/A = x/0 is evaluated at the time of symfun definition, and the resulting expression Inf*x is what is attached to symbol f. At run-time, the resulting expression is recalled and the actual parameter is subs()'d in for the parameter name x.
To see even more clearly why this is a problem, consider
syms x
f = symfun(mod(x, 2), x)
Then mod(x,2) has to be evaluated first. In symbolic form, mod(constant*variable, value) is evaluated as mod(constant,value)*variable, so mod(x,2) being mod(1*x,2) is mod(1,2)*x = 1*x = x . (mod(7*x,5) would be mod(7,5)*x = 2*x for example). This mod(x,2) -> x is then attached to f, so f = symfun(mod(x,2),x) becomes f(x) = x, no matter what the value of x will later be. This is not at all the same as would be the case for
f = @(x) mod(x,2)
which does not attempt to evaluate the body of the expression until run time.
Now, suppose you attempt
syms x
f = symfun( x(1) + 2*x(2), x)
with the idea being that you want f to take a vector of 2 elements and compute the first element plus twice the second element.
But remember, the expression needs to be evaluated first. SO x(1) + 2*x(2) needs to be evaluated. But x is a scalar symbol at that point, so x(2) does not exist. The x(1)+2*x(2) expression would fail, leading to an error before symfun even gets called.
But if you try your approach, a very natural approach to expect to have work
x = syms(x, [1 2])
f = symfun( x(1) + 2*x(2), x)
then because the body gets executed first, the x(1) and x(2) get evaluated, giving
f = symfun( x1 + 2*x2, x )
and the x would also get evaluated in the second parameter, so
f = symfun(x1 + 2*x2, [x1, x2])
But of course this is the syntax for creating a function with two parameters,
f(x1, x2) = x1 + 2*x2
and you lose :(
This all reflects a fundamental internal limitation in the symbolic engine: the very insides of the symbolic engine have no provision for describing or operating on an "array" whose contents will be filled in later. There is, for example, no way inside the symbolic engine to construct symbolic mtimes(A,B) equivalent to MATLAB's A*B, to be held as an unevaluated matrix multiply, so that if you were to later substitute in matrices for A and B that a matrix multiplication would occur. Inside, the symbolic engine can only deal with matrices that are filled with _something of definite size. Like sym('x', 2, 2) is an array that is filled with [x11 x12;x21 x22] and is then a definite 2 x 2 with known elements: that can be worked with. But no concept of "trust me, this will be a matrix later but now I just want to tell you how you will work with it when the actual matrix arrives." (This matters because it means you cannot construct symbolic expressions that do multiplication of matrices of variablesize.)
So what can you do?
Well, not much about the fact that the expression is fully evaluated -- if, for example, you need mod(x,2) preserved, then you just cannot do that symbolically.
However, there is a work-around that can be used sometimes for the vector issue:
x = sym('x', [3 1]);
test = sym('test', [3 1])
f = matlabFunction( [x(1) 2 -x(1); 1 x(2) 3; x(3) 2 x(3)], 'var', {x});
f(test)
Here, f is not a symbolic function: it has been transformed into an anonymous function with options that instruct the anonymous function to pack x1, x2, x3 into a single column vector. (When you use this code, you have to decide whether the vector is row vector or column vector.) But you can evaluate anonymous functions with symbolic inputs, giving back symbolic output.
What do you lose when you do this?
For one thing you lose precision: the anonymous function is often going to evaluate rational values as floating point. Secondly, the anonymous functions cannot typically handle piecewise(). Thirdly, symbolic integrals would be transformed into numeric integrals (or it will just refuse to process it.) Along with various other restrictions: some constructs that are symbolic only cannot be transformed into numeric calls.

Nicolas Schmit
Nicolas Schmit on 16 Nov 2017
You could use an anonymous function to call f
x = sym('x', [3 1])
test = sym('test', [3 1])
f = symfun([x(1) 2 -x(1); 1 x(2) 3; x(3) 2 x(3)],x);
g = @(x) f(x(1), x(2), x(3))
g(x)

Community Treasure Hunt

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

Start Hunting!