How do you create a symbolic function that takes a vector input?
29 次查看(过去 30 天)
显示 更早的评论
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))
0 个评论
采纳的回答
Karan Gill
2016-10-20
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 个评论
Karan Gill
2016-11-1
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
更多回答(2 个)
Walter Roberson
2016-10-19
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 个评论
Walter Roberson
2016-10-20
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.
Walter Roberson
2016-10-21
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.
Daniele Cuomo
2019-2-5
编辑:Daniele Cuomo
2019-2-6
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 个评论
Daniele Cuomo
2019-2-7
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.
Walter Roberson
2019-2-7
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)})
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!