How do you create a symbolic function that takes a vector input?

I want to create a symbolic function that takes a 1x3 vector input, and returns 3*the vector. Super simple idea, can't figure it out. I'm getting the error "Error using symfun/subsref (line 141) Symbolic function expected 3 inputs and received 1."
a = sym('a', [1 3]);
f(a) = 3*a;
A = [1 2 3];
b = double(f(A))

 Risposta accettata

You almost got it right! In fact, you just need to delete a line of code. Don't declare "a" as a 1x3 vector because "a" can be anything by default. Just remove that line and declare "f(a)" instead:
syms f(a)
f(a) = 3*a;
A = [1 2 3];
b = double(f(A))
b =
3 6 9
At least, I think this is what you wanted to do.

3 Commenti

So if I want my function to be a matrix operation, it doesn't work.
syms f(a)
A = [1 2 3].';
f(a) = A*a;
b = [1 2 3];
double(f(b))
Error using double
Conversion to double from cell is not possible.
Sorry for the late reply -- I did not get a notification for your comment. By "matrix operation", do you mean something like what expm does? That is not possible at the moment. Can you provide more information on what you are trying to do?
The workaround here is to convert the cell to sym using "cell2sym":
double(cell2sym(f(b)))
ans =
1 2 3
2 4 6
3 6 9
what if i change f(a) = A.*a ?

Accedi per commentare.

Più risposte (2)

a = sym('a', [1 3]);
that creates a as a vector of three symbolic names
f(a) = 3*a;
there you are passing in the vector of three symbolic names in the function definition, but you are treating it as if you had passed a single variable.
syms f(a)
f(a) = 3*a;
double( f(A) )

4 Commenti

Yes I understand that but how do I treat it as 3 variables?
"I want to create a symbolic function that takes a 1x3 vector input, and returns 3*the vector"
but now you want to treat it is three variables??
I am going to speculate that what you really want to know is something different than you are asking. I am going to speculate that what you want to know is what is discussed https://www.mathworks.com/help/symbolic/matlabfunction.html#bul8wgx-1 with the example given:
Now, convert an expression r to a MATLAB function whose second input argument is a vector.
syms x y z t
r = (x + y/2 + z/3)*exp(-t);
matlabFunction(r,'File','myfile','Vars',{t,[x y z]});
Notice the use of 'Vars' with a {} input with each individual member of the cell going to create one input variable for the handle and the variables in [] groups being bundled together into one variable. So for you it would look like
matlabFunction(f, 'Vars', {a})
because your a is already a vector.
Unfortunately, MuPad (the symbolic engine) does not have the facility to be able to indicate that a symbol represents an abstract matrix. MuPad considers each unresolved symbol to be scalar.
You can, in MuPAD, create a matrix of a particular size, but it has to be populated with values and symbols, has to become a particular matrix.
The MuPad way of handling this is to create a procedure to do the work: procedures do not evaluate until they are called on particular arguments (like function handles.) Unfortunately, the interface to create a nice-looking symbolic functions that postpones to the right time is not there -- an ugly version can be created but making it nice requires setting a private field in the symbol.
The work-around looks like this:
feval(symengine, '_assign', sym('A'), sym(A)); %push the value of A into the engine
f = sym('x->A*x');
And then to use,
feval(symengine, f, b)
But if you are going to do that, you might as well use
f = @(x) A*x
In current releases, the sym('x->A*x') will warn about that going away. You could warning('off') that, or you could substitute
feval(symengine, '_assign', sym('A'), sym(A)); %push the value of A into the engine
f = mupadmex('x->A*x', 11); %yes, that IS obscure
feval(symengine, f, b)
or
feval(symengine, '_assign', sym('A'), sym(A)); %push the value of A into the engine
f = evalin(symengine, 'x->A*x');
feval(symengine, f, b)
In theory you should also be able to do
feval(symengine, '_assign', sym('A'), sym(A)); %push the value of A into the engine
f = evalin(symengine, 'proc(x) begin A*x end_proc');
feval(symengine, f, b)
but in practice you will get an error message about running out of memory or the interface needing to be reset.
I will poke around and see if I can find something nicer.
My investigations suggest that
f(variable1,variable2,...) = formula
is handled as
f = symfun(formula, [variable1, variable2, ...])
Here, formula is executed and the variables are executed. The execution of each of those parts is a symbolic expression (class sym).
In turn each sym has properties, one of which is a string. For plain symbolic variables, the string is the name of the variable. For expressions and functions, the string is a special form, and it is the name of a temporary variable that exists in the MuPAD engine and has been assigned the expression.
Now, the subsref() for class sym is treated as standard indexing -- so if you have a sym f at hand, f() will always be treated as an indexing attempt. If you have a sym that has somehow gotten defined as a MuPAD proc, and you want to invoke a function it refers to, you need to use feval() to do so, like I showed above.
symfun is slightly different. The constructor for symfun stores the reference to the formula, and stores the list of variable names that are to be parameters, and returns back a symfun object. The subsref() for class symfun checks for '()' indexing style, and when it is found, grabs the formula and subs() the provided values for the remembered variables, f(values) -> subs(stored_formula(f), stored_variables(f), values) . The standard evaluation takes place after subs, so levels of hold() might get lost:
>> sym('hold(hold(hold(b)))')
hold(hold(b))
>> subs(sym('hold(hold(hold(b)))'), 'b', 9)
hold(9)
The subsref() for class symfun does not do
feval(symengine, stored_formula(f), values)
or
feval(symengine, 'fp::apply', stored_formula(f), values)
or
feval(symengine, ['x->' stored_formula(f)]', values)
I am not sure at the moment why that seems so significant to me, but it does make it difficult to use symfun() to created useful delayed evaluation.

Accedi per commentare.

Is there a clean way to do the same thing but for functions like
x = sym('x', [1 2]);
Ffun(x) = [x(1)^2 + x(2)^2 - 1; sin(pi*x(1)/2) + x(2)^3];
The problem here is that Ffun wants 2 variables and doesn't accept a vector like
x0 = [1 2];
The only way I found is the use of subs function, but it becomes impractical when you need good waiting times.
subs(Ffun, x, x0);

8 Commenti

No there is no clean way for functions. I list the possibilities above. This is fundamental to how the symbolic engine works. Resolved symbols at the MATLAB level always represent scalars.
% remove first line
Ffun=@(x)....
x0=1:2;
Ffun(x0)
When you use @ then the result is not a symbolic function . You cannot differentiate it or create differential equations with it
Completely agree sir Walter.
Daniele Cuomo comments to me,
I agree. The only way I found is the use of subs function, but it becomes impractical when you need good waiting times
Symbolic functions use subs() to transfer the input arguments into the expressions, so doing subs() yourself cannot be slower.
Anonymous functions and regular full function functions use positional parameters with stack references to associate input arguments with variable names -- the internal code more or less uses pointers into the parameter list instead of doing lookups in the workspace each time the variable is encountered.
However, symfun receive parameters as indexing information and converts them to a cell array of parameters and feval() the expression passing in {:} of the cell array. feval() gathers them back into a cell array and uses subs() on the formula passing in the celll array of symbolic parameter names and the cell array of corresponding values. The only use of built-in routines that you cannot access directly is an internal routine to find out whether the symbolic function has a scalar body or a non-scalar body (in which case a cell array of results has to be returned.)
Perhaps you had overlooked the ability to call subs with a cell array of variable names and a cell array of corresponding values, and were instead looping calling subs to bring in one variable at a time ??
Nono, I said it badly. The problem is that I want to declare a symfun to exploit its syntax power (can call the diff function for example) at first. I need a function_handle later because I need to call it a lot of times. Calling matlabFunction does not work because it returns a function_handle with multiple input arguments.
There seems to be no solution to this problem.
Call matlabFunction() with the 'vars' option. Pass the 'vars' option a cell array. Within the cell array, each variable name you mention within a vector will be bundled together into a single input argument with the other names in the same vector of variable names. If the vector of names is a row vector then MATLAB will vectorize based upon columns of the corresponding input -- this is suitable for use with the optimization functions such as fminsearch and for use with fsolve(). If the vector of names is a column vector then MATLAB will vectorize based upon rows of the corresponding put -- this is suitable for use with the ode functions for the derivatives argument. If you pass a 2D array of names, then MATLAB will not vectorize and will use linear indexing to pull out appropriate positions from the input argument.
To see how that works out, compare
x = sym('x', [3 2]);
matlabFunction(x*x', 'vars', {x})
matlabFunction(x*x', 'vars', {reshape(x,1,[])})
matlabFunction(x*x', 'vars', {reshape(x,[],1)})

Accedi per commentare.

Community Treasure Hunt

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

Start Hunting!

Translated by