My question is if there is a good way to use MuPAD functions in a Matlab script. The background is that I have a problem where I need to find all solutions to a set of non-linear equations. The previous solution was to use solve in Matlab, which works for some of my simulations (i.e., some of the sets of input T) but not always. So instead I'm using MuPAD in the following way:
function ut1 = testMupadSolver(T)
% # Input T should be a vector of 15 elements
mupadCommand = ['numeric::polysysroots({' eq1(T) ' = 0,' ...
eq2(T) '= 0},[u, v])'];
allSolutions = evalin(symengine, mupadCommand);
ut1 = allSolutions;
end
function strEq = eq1(T)
sT = #(x) ['(' num2str(T(x)) ')'];
strEq = [ '-' sT(13) '*u^4 + (4*' sT(15) '-2*' sT(10) '-' sT(11) '*v)*u^3 + (3*' ...
sT(13) '-3*' sT(6) '+v*(3*' sT(14) '-2*' sT(7) ')-' sT(8) '*v^2)*u^2 + (2*' ...
sT(10) '-4*' sT(1) '+v*(2*' sT(11) '-3*' sT(2) ')+v^2*(2*' sT(12) ' - 2*' ...
sT(3) ')-' sT(4) '*v^3)*u + v*(' sT(7) '+' sT(8) '*v+' sT(9) '*v^2)+' sT(6)];
end
function strEq = eq2(T)
sT = #(x) ['(' num2str(T(x)) ')'];
strEq = ['(' sT(14) '-' sT(13) '*v)*u^3 + u^2*' '(' sT(11) '+(2*' sT(12) '-2*' sT(10) ...
')*v-' sT(11) '*v^2) + u*(' sT(7) '+v*(2*' sT(8) '-3*' sT(6) ')+v^2*(3*' sT(9) ...
'-2*' sT(7) ') - ' sT(8) '*v^3) + v*(2*' sT(3) '-4*' sT(1) '+v*(3*' sT(4) ...
'-3*' sT(2) ')+v^2*(4*' sT(5) ' - 2*' sT(3) ')-' sT(4) '*v^3)+' sT(2)];
end
I have two queries:
1) In order to use MuPAD I need to rewrite my two equations for the equation-system as strings, as you can see above. Is there a better way to do this, preferably without the string step?
2) And regarding the format output; when
T = [0 0 0 0 0 0 0 0 0 0 1 0 1 0 1];
the output is:
testMupadSolver(T)
ans =
matrix([[u], [v]]) in {matrix([[4.4780323328249527319374854327354], [0.21316518769990291263811232040432]]), matrix([[- 0.31088044854742790561428736573347 - 0.67937835289645431373983117422178*i], [1.1103383836576028262792542770062 + 0.39498445715599777249947213893789*i]]), matrix([[- 0.31088044854742790561428736573347 + 0.67937835289645431373983117422178*i], [1.1103383836576028262792542770062 - 0.39498445715599777249947213893789*i]]), matrix([[0.47897094942962218512261248590261], [-1.26776233072168360314707025141]]), matrix([[-0.83524238515971910583152318717102], [-0.66607962429342496204955062300669]])} union solvelib::VectorImageSet(matrix([[0], [z]]), z, C_)
Can MuPAD give the solutions as a set of vectors or similarly? In order to use the answer above I need to sort out the solutions from that string-set of solutions. Is there a clever way to do this? My solution so far is to find the signs I know will be present in the solution, such as '([[' and pick the numbers following, which is really ugly, and if the solution for some reason looks a little bit different than the cases I've covered it doesn't work.
EDIT
When I'm using the solution suggested in the answer below by #horchler, I get the same solution as with my previous implementation. But for some cases (not all) it takes much longer time. Eg. for the T below the solution suggested below takes more than a minute whilst using evalin (my previous implementation) takes one second.
T = [2.4336 1.4309 0.5471 0.0934 9.5838 -0.1013 -0.2573 2.4830 ...
36.5464 0.4898 -0.5383 61.5723 1.7637 36.0816 11.8262]
The new function:
function ut1 = testMupadSolver(T)
% # Input T should be a vector of 15 elements
allSolutions = feval(symengine,'numeric::polysysroots', ...
[eq1(T),eq2(T)],'[u,v]');
end
function eq = eq1(T)
syms u v
eq = -T(13)*u^4 + (4*T(15) - 2*T(10) - T(11)*v)*u^3 + (3*T(13) - 3*T(6) ...
+ v*(3*T(14) -2*T(7)) - T(8)*v^2)*u^2 + (2*T(10) - 4*T(1) + v*(2*T(11) ...
- 3*T(2)) + v^2*(2*T(12) - 2*T(3)) - T(4)*v^3)*u + v*(T(7) + T(8)*v ...
+ T(9)*v^2) + T(6);
end
function eq = eq2(T)
syms u v
eq = (T(14) - T(13)*v)*u^3 + u^2*(T(11) + (2*T(12) - 2*T(10))*v ...
- T(11)*v^2) + u*(T(7) + v*(2*T(8) - 3*T(6) ) + v^2*(3*T(9) - 2*T(7)) ...
- T(8)*v^3) + v*(2*T(3) - 4*T(1) + v*(3*T(4) - 3*T(2)) + v^2*(4*T(5) ...
- 2*T(3)) - T(4)*v^3) + T(2);
end
Is there a good reason to why it takes so much longer time?
Firstly, Matlab communicates with MuPAD via string commands so ultimately there is no way of getting around the use of strings. And because it's the native format, if you're passing large amounts of data into MuPAD, the best approach will be to convert everything to strings fast and efficiently (sprintf is usually best). However, in your case, I think that you can use feval instead of evalin which allows you to pass in regular Matlab datatypes (under the hood sym/feval does the string conversion and calls evalin). This method is discussed in this MathWorks article. The following code could be used:
T = [0 0 0 0 0 0 0 0 0 0 1 0 1 0 1];
syms u v;
eq1 = -T(13)*u^4 + (4*T(15) - 2*T(10) - T(11)*v)*u^3 + (3*T(13) - 3*T(6) ...
+ v*(3*T(14) -2*T(7)) - T(8)*v^2)*u^2 + (2*T(10) - 4*T(1) + v*(2*T(11) ...
- 3*T(2)) + v^2*(2*T(12) - 2*T(3)) - T(4)*v^3)*u + v*(T(7) + T(8)*v ...
+ T(9)*v^2) + T(6);
eq2 = (T(14) - T(13)*v)*u^3 + u^2*(T(11) + (2*T(12) - 2*T(10))*v ...
- T(11)*v^2) + u*(T(7) + v*(2*T(8) - 3*T(6) ) + v^2*(3*T(9) - 2*T(7)) ...
- T(8)*v^3) + v*(2*T(3) - 4*T(1) + v*(3*T(4) - 3*T(2)) + v^2*(4*T(5) ...
- 2*T(3)) - T(4)*v^3) + T(2);
allSolutions = feval(symengine, 'numeric::polysysroots',[eq1,eq2],'[u,v]');
The last argument still needed to be a string (or omitted) and adding ==0 to the equations also doesn't work, but the zero is implicit anyways.
For the second question, the result returned by numeric::polysysroots is very inconvenient and not easy to work with. It's a set (DOM_SET) of matrices. I tried using coerce to convert the result to something else to no avail. I think you best bet it to convert the output to a string (using char) and parse the result. I do this for simpler output formats. I'm not sure if it will be helpful, but feel free to look at my sym2float which just handles symbolic matrices (the 'matrix([[ ... ]])' part go your output) using a few optimizations.
A last thing. Is there a reason your helper function includes superfluous parentheses? This seems sufficient
sT = #(x)num2str(T(x),17);
or
sT = #(x)sprintf('%.17g',T(x));
Note that num2str only converts to four decimal places by default. int2str (or %d should be used if T(x) is always an integer).
Related
I'm getting these error:
Conversion to function_handle from double is not possible.
Already searched about it and tried to change my code but without sucess. Could u help? Thanks
A=[99.23;100.05;91;107.71;104.1];
B=[3441 3441 301720.5;68750 1068750 0;170040 13085020 0;229350 229350 5729350;34194000 0 0];
N=[300000;1000000;13000000;5500000;32800000];
E=[-0.00302;-0.00261;-0.00208];
[c3,r3]=size(A);
[c4,r4]=size(B);
x=sym ('x',[1 c3]);
x=transpose(x);
for i=1:c3
Valor(i,1)=0;
for j=1:r4
Valor(i,1)=#(x){(Valor(i,1)/((1+E(j,1)+x(j,1))^j))+(B(i,j)/((1+E(j,1)+x(j,1))^j))};
end
end
What I want is to find the vector x given that I already have a vector Valorantigo that I will use to apply solve.
Valorantigo(1:c3,1)=A(1:c3,1).* N(1:c3,1) ./100;
eqn=Valor(1:c3,1)==Valorantigo(1:c3,1);
[solx, param, cond] = solve(eqn, x, 'ReturnConditions', true);
Basically x would be the solution of
Valorantigo(1,1)=3441/(1-0.00302+x1) + 3441/(1-0.00261+x1)^2 + 301720.5/(1-0.00208+x1)^3
Valorantigo(2,1)=68750/(1-0.00302+x2) + 1068750/(1-0.00261+x2)^2 + 0/(1-0.00208+x2)^3
Valorantigo(3,1)=170040/(1-0.00302+x3) + 13085020/(1-0.00261+x3)^2 + 0/(1-0.00208+x3)^3
the same fot the other lines...
Just the relevant Part:
Valor(1,1)=symfun(0,x);
for i=1:c3
Valor(i,1)=symfun(0,x);
for j=1:r4
Valor(i,1)=symfun( Valor(i,1)/(1+E(j,1)+x(j,1))^j+(B(i,j)/((1+E(j,1)+x(j,1))^j)),x);
end
end
Valor needs to be predefinded as symbolic, with Valor(i,1)=0; you made it to a double.
Oh almost forgot, your solve needs to look like this:
[solx1,solx2,solx3,solx4, solx5, param, cond] = solve(eqn, x, 'ReturnConditions', true);
let's say I have s=g(1,2,0)+g(1,3,0)+u(1,3)+g(1,1,0) where g, u are functions; I want to replace all 3rd arguments of g to something I choose without going through my script and doing it manually.
x = ... % assign some value beforehand
s = g(1,2,x) + g(1,3,x) + u(1,3) + g(1,1,x)
What follows is an ugly hack and I don't recommend using it:
g = #(a,b,c) g(a,b,0)
This redefines g function in a way that executing after that:
s = g(1,2,5) + g(1,3,3) + u(1,3) + g(1,1,2)
actually executes:
s = g(1,2,0) + g(1,3,0) + u(1,3) + g(1,1,0)
EDIT:
I think what I need is to apply fmincon, but I don't know how. I want to do this:
Use fmincon to solve:
Minimize a, subject to the equality constraint
f(q,z) = 0
by varying the unknowns (q,z).
How do I use fmincon to implement this? Thanks!
Here is the entire function:
f(q,z) = 60*q^9*z^4 + 120*q^7*z^3 + 80*q^(17/2)*z^3 - 60*q^8*z^3 - 60*q^6*z^3 + 40*q^(15/2)*z^3 + 20*q^(11/2)*z^3 - 20*q^9*z^3 + 175*q^(13/2)*z^2 - 90*q^(15/2)*z^2 + 75*q^5*z^2 - 60*q^6*z^2 - 45*q^(11/2)*z^2 + 40*q^2*z^2 + 30*q^8*z^2 - 30*q^(3/2)*z^2 - 10*q^(17/2)*z^2 + 15*q^(9/2)*z^2 - 20*q^4*z^2 - 10*q^(7/2)*z^2 - 90*q^(11/2)*z + 86*q^(9/2)*z + 75*q^6*z - 45*q^(7/2)*z + 40*q^(3/2)*z - 30*q^7*z - 20*q^(13/2)*z - 24*q^(1/2)*z + 15*q^4*z + 15*q^3*z - 9*q^(5/2)*z - 9*q^5*z + 45*q^4 - 30*q^5 + 30*q^(1/2) - 29*q^3 + 21*q^(5/2) - 10*q^(9/2) - 9*q^2 - 24.
----- original post -------
I have a function of 2 variables q and z like
f(q,z) = 60*q^9*z^4 + 120*q^7*z^3 + 80*q^(17/2)*z^3 - 60*q^8*z^3 - 60*q^6*z^3 + ...
I need to find
f(q,z) = 0
with respect to z, but this is not possible analytically (resulting in RootOf). So instead of finding all points, I want to numerically find the smallest number z such that the value pair (q,z) fulfills f(q,z) = 0. Any ideas how to do this?
I'm afraid there is no minimum z, as f(q,z) = 0: q↓0 ⇒ z↓-∞.
Your original equation can be written as:
Writing all the terms following like powers of z as A, B, C, D and E, you basically have
This is a quartic equation in z, which can definitely be solved analytically. Granted, it's a bit awkward, but possible nonetheless.
Note that the fact that A = f(q), B = g(q), C = h(q), D = k(q) and E = m(q) does not matter, as your goal is to express z = F(q).
Here's one way to find the minimum z for which f(q,z)=0 holds:
function find_minimum_z
solution = fminsearch(#F, 3)
end
function R = F(q)
R = roots([
60.*q.^9
120.*q.^7 + 80.*q.^(17/2) - 60.*q.^8 - 60.*q.^6 + 40.*q.^(15/2) + 20.*q.^(11/2) - 20.*q.^9
175.*q.^(13/2) - 90.*q.^(15/2) + 75.*q.^5 - 60.*q.^6 - 45.*q.^(11/2) + 40.*q.^2 + 30.*q.^8 - 30.*q.^(3/2) - 10.*q.^(17/2) + 15.*q.^(9/2) - 20.*q.^4 - 10.*q.^(7/2)
-90.*q.^(11/2) + 86.*q.^(9/2) + 75.*q.^6 - 45.*q.^(7/2) + 40.*q.^(3/2) - 30.*q.^7 - 20.*q.^(13/2) - 24.*q.^(1/2) + 15.*q.^4 + 15.*q.^3 - 9.*q.^(5/2) - 9.*q.^5
45.*q.^4 - 30.*q.^5 + 30.*q.^(1/2) - 29.*q.^3 + 21.*q.^(5/2) - 10.*q.^(9/2) - 9.*q.^2 - 24
]);
R = min(R(imag(R)==0));
if isempty(R)
R = NaN; end
end
My trials indicate there is an asymptote at q=0 for which the minimum, real z that solves the quartic tends to -∞.
Indeed this is what you can see when you make a plot of F(q):
If all terms after the first term have a z³, then it's really quite simple:
f(q,z) = 60q⁹z⁴ + 120q⁷z³ + 80q¹⁷/²z³ - 60q⁸z³ - 60q⁶z³ + ...
= z⁴·(60q⁹) + z³·(120q⁷ + 80q¹⁷/² - 60q⁸ - 60q⁶ + ... )
f(q,z) = 0
⇒ z⁴ · (60q⁹) = -z³ · (120q⁷ + 80q¹⁷/² - 60q⁸ - 60q⁶ + ...)
⇒ -z · (60q⁹) = 120q⁷ + 80q¹⁷/² - 60q⁸ - 60q⁶ + ...
⇒ z = -(120q⁷ + 80q¹⁷/² - 60q⁸ - 60q⁶ + ...) / 60q⁹
= -(2q⁷ + 1⅓q¹⁷/² - q⁸ - q⁶ + ...) / q⁹
I have a matrix with a bunch of unknown constants such as the one below:
a*b -c -d 0
-c e -a -b-d
-d -a d -e
0 -b-d -e a
As you may realize it is symmetric about the diagonal and therefore, the diagonal values are all positive. All constants are greater than 0.
I would like to solve this for the eigenvalues in matlab. How would I go about doing this? I do not know the values a,b,c,d, and e. I would like to do something like this:
d = eig(#getMatrix)
but the eig function does not accept function handles.
No problem in MATLAB.
>> syms a b c d e
>> M = [a*b -c -d 0
-c e -a -b-d
-d -a d -e
0 -b-d -e a];
>> eig(M)
ans =
a/4 + d/4 + e/4 + (a*b)/4 - ((51*a*d^3)/16 - (117*a^4*b)/16 + (27*a^3*d)/16 + (27*a*e^3)/16 + (57*b*d^3)/2 + (27*a^3*e)/16 + (27*d*e^3)/16 + (51*d^3*e)/16 + 6*((4*(2*b*d - (a*e)/4 - (a*d)/4 - (d*e)/4 - (a^2*b)/4 + (11*a^2)/8 + b^2 + c^2 + (19*d^2)/8 + (11*e^2)/8 + (3*a^2*b^2)/8 - (a*b*d)/4 - (a*b*e)/4)*((17*a*d^3)/64 - (39*a^4*b)/64 + (9*a^3*d)/64 + (9*a*e^3)/64 + (19*b*d^3)/8 + (9*a^3*e)/64 + (9*d*e^3)/64 + (17*d^3*e)/64 + (45*a^4)/256 + (285*d^4)/256 + (45*e^4)/256 - (a^2*b^2)/16 + (a^2*b^3)/8 + (3*a^2*b^4)/16 + (31*a^4*b^2)/128 + (a^4*b^3)/64 - (3*a^4*b^4)/256 + (3*a^2*c^2)/16 + (15*a^2*d^2)/128 - (9*a^2*e^2)/128 + (19*b^2*d^2)/16 - (b^2*e^2)/16 + (3*c^2*d^2)/16 + (15*c^2*e^2)/16 +
...
(a*b*c^2*e)/8 + (3*a*b*d*e^2)/64 + (11*a*b*d^2*e)/64 + (a*b^2*d*e)/4 - (33*a^2*b*d*e)/32 - (5*a^2*b^2*d*e)/64 + (a*b*d*e)/4 + (a*c*d*e)/2 - 2*b*c*d*e) - 256*((17*a*d^3)/64 - (39*a^4*b)/64 + (9*a^3*d)/64 + (9*a*e^3)/64 + (19*b*d^3)/8 + (9*a^3*e)/64 + (9*d*e^3)/64 + (17*d^3*e)/64 + (45*a^4)/256 + (285*d^4)/256 + (45*e^4)/256 - (a^2*b^2)/16 + (a^2*b^3)/8 + (3*a^2*b^4)/16 + (31*a^4*b^2)/128 + (a^4*b^3)/64 - (3*a^4*b^4)/256 + (3*a^2*c^2)/16 + (15*a^2*d^2)/128 - (9*a^2*e^2)/128 + (19*b^2*d^2)/16 - (b^2*e^2)/16 + (3*c^2*d^2)/16 + (15*c^2*e^2)/16 + (15*d^2*e^2)/1...
Output truncated. Text exceeds maximum line length of 25,000 characters for Command Window display.
I deleted a lot there. Admittedly, its rather messy and lengthy, but can you really expect better?
Edit: I should comment that such a long extended formula may be dangerous in terms of computational accuracy. I've seen people blindly use such a mess of an expression, evaluating it in Fortran or MATLAB. They think that because it is "symbolic" that it is also exact. This is a total fallacy when numerical computations are done.
There may well be immense subtractive cancellation in those terms, with huge positive and negative terms almost canceling each other out, leaving a tiny result that is essentially worthless because of the limited dynamic range of floating point computations. BEWARE. At the very least, compare single and double precision computations done with the same expression. If they differ by any significant amount, try an extended precision version to verify there is not a problem for the doubles. If you have not tested such an expression and verified it extensively, don't trust it.
for Temp = 1000:10:6000
cp_CO2 = ((2e-18)*Temp.^5) - ((4e-14)*Temp.^4) + ((3e-10)*Temp.^3) - ((8e-07)*Temp.^2) + (0.0013*Temp) + 0.5126;
cp_CO = ((5e-12)*Temp.^3) - ((7e-08)*Temp.^2) + (0.0003*Temp) + 0.9657;
cp_H2O = ((7e-12)*Temp.^3) - ((1e-07)*Temp.^2) + (0.0008*Temp) + 1.6083;
cp_N2 = ((-1e-18)*Temp.^5) + ((2e-14)*Temp.^4) - ((8e-11)*Temp.^3) + ((1e-07)*Temp.^2) + (0.0001*Temp) + 0.9985;
D_H = (y(1)*cp_CO2*44*(25-Temp)) + (y(2)*cp_CO*28*(25-Temp)) + (y(3)*cp_H2O*18*(25-Temp)) + (percent_air*x_final(2)*3.76*28*(25-Temp));
DELTA_H = round(D_H);
if DELTA_H == delta_h
break
end
end
The 'for' loop in my code is above, the variables delta_h, y and percent_air have been defined and calculated/input earlier. If I work on the loop as a cell and manually increase Temp then the values of D_H etc. all change. But for some reason when I try and execute the loop the 'if' statement doesn't seem to come into effect and the final values where Temp = 6000 are displayed in the workspace instead of the value of Temp where it produces a DELTA_H equal to that of delta_h. It's the first time I've used MATLAB for about 2 years (I'm a 3rd Year Mech Eng student) so please forgive me if it's a simple error to fix.
If either of the variables are floating-point, doing an exact compare like that is problematic. A <= or >= comparison might work better.