How to loop through indexed symbolics Matlab - matlab

so what im trying to do is to refer to different symbolics by indexing as you would with a vector
syms k_1 k_2 k_3
for i= 1:3
'expression to get k_i'
end
Desired answer would be k1 k2 k3.
I do not intend to do this with a vector created by the sym function unless there's a way to assign a matrix to a vector position without getting this error
.

You can add them all to a struct, then use dynamic referencing to the ith field name:
% Create symbolic vars
syms k_1 k_2 k_3
% Assign to struct
K.k_1 = k_1;
K.k_2 = k_2;
K.k_3 = k_3;
% Loop through
for ii = 1:3
kstr = sprintf('k_%d', ii); % = 'k_1', 'k_2', ...
ki = K.(kstr); % equivalent to ki = K.k_1 etc
end
Equivalently, once you have the struct you can just use the field names, although it's a bit less explicit which field you're accessing each iteration.
f = fieldnames(K);
for ii = 1:numel(f)
ki = K.(f{ii});
end

Similarly to #Wolfie's answer, you can use a cell array:
syms k_1 k_2 k_3
k = {k_1, k_2, k_3};
for ii = 1:3
ki = k{ii};
end
This is somewhat simpler than the struct solution, but doesn't preserve the variable names. In this case, k{1} is similar enough to k_1, so maybe this doesn't matter.

Related

parfor with nested for: variable classification depends on loop endVal

I want to compute a fractal image in two nested loops over the pixel indices (ix,iy). The sample code just assigns a random number to RGB values instead of the real calculations.
x = 0:.2:4;
y = 0:.2:3;
nX = length(x);
nY = length(y);
RenderRed = zeros(nX,nY); RenderGreen = zeros(nX,nY); RenderBlue = zeros(nX,nY);
parfor ix = 1:nX
% for iy = 1:length(y) % error
for iy = 1:nY
% "compute" pixel (ix,iy)
RenderRed(ix, iy) = rand; RenderGreen(ix, iy) = rand; RenderBlue(ix, iy) = rand;
end
end
Pctr = [];
Pctr(:,:,1)=RenderRed; Pctr(:,:,2)=RenderGreen; Pctr(:,:,3)=RenderBlue;
handle = image(Pctr);
shg
The code works as shown but if the end value of the iy loop is changed from nY to length(y) - see the commented line - an error is issued:
Error: The variable RenderRed in a parfor cannot be classified.
See Parallel for Loops in MATLAB, "Overview".
Why? In my understanding of sliced variables no version should work: one has to use an auxiliary variable collecting the results of the inner loop, and assign that to a slice of the matrix.
But length(y) instead of nY should have no effect at all on the classification of variables because y is never assigned in the loops.
mlint finds no error, in neither version. Same behaviour with MATLAB versions 2016b, 2017b.
According to the Matlab documentation, this actually can work! When you want to use nested for loops inside parfor loops:
For proper variable classification, you must define the range of a for-loop nested in a parfor-loop by constant numbers or variables.

For loop in using fmincon function for different initial points

I know this question is really too basic, but I am stuck since I am new to MATLAB. The problem is I do not know how to use for-loop to show three output of V in one run for different initial points.
I would like to have an output like the following:
V1 (first initial point)-----V2(second initial point)-----V3(third initial point)
This is my code:
%variables
ENG1=0.52;
ENG2 = [0.00139;0.00149;0.00122;0.00130;0.000866;0.000731;0.001002;0.001285];
ENG3 = zeros(8,1);
%6.2.2) minimization term
fun = #(V) abs(ENG1 - V'*ENG2);
%6.2.3) constrains:
Aeq = [ENG3';ones(1,8)];
beq = [0;1];
lb = zeros(8,1); %lower bound
ub = ones(8,1); %upper bound
V0 = rand(8,1);V0 = V0/sum(V0); %initial guess
options = optimset('Display', 'off'); %supressing extra unnecessary outputs.
for i=1:3
V(i) = fmincon(fun,V0,[],[],Aeq,beq,lb,ub,[],options)
end
If you store your different initial conditions as columns of a matrix you can do it in the following way:
V0 = rand(8,3); %3 columns of initial guesses
V0 = bsxfun(#rdivide, V0, sum(V0));
% V0 = V0./sum(V0); % similar to previous line, but only supported from R2016b
V = zeros(size(V0)); % preallocate memory for efficiency
for i=1:3
V(:, i) = fmincon(fun,V0(:, i),[],[],Aeq,beq,lb,ub,[],options);
end
Note the use of the elementwise division (./) to normalise your initial guesses.
Note that V(:, i) selects all elements of column i.
See this post, for more information about the compatibility issue.

Solve matrix DAE system in matlab

The equations can be found here. As you can see it is set of 8 scalar equations closed to 3 matrix ones. In order to let Matlab know that equations are matrix - wise, I declare variable time dependent vector functions as:
syms t p1(t) p2(t) p3(t)
p(t) = symfun([p1(t);p2(t);p3(t)], t);
p = formula(p(t)); % allows indexing for vector p
% same goes for w(t) and m(t)...
Known matrices are declared as follows:
A = sym('A%d%d',[3 3]);
Fq = sym('Fq%d%d',[2 3]);
Im = diag(sym('Im%d%d',[1 3]));
The system is now ready to be modeled according to guide:
eqs = [diff(p) == A*w + Fq'*m,...
diff(w) == -Im*p,...
Fq*w == 0];
vars = [p; w; m];
At this point, when I try to reduce index (since it equals 2), I receive following error:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
Error using sym/reduceDAEIndex (line 95)
Expecting as many equations as variables.
The error would not arise if we had declared all variables as scalars:
syms A Im Fq real p(t) w(t) m(t)
Quoting symfun documentation (tips section):
Symbolic functions are always scalars, therefore, you cannot index into a function.
However it is hard for me to believe that it's not possible to solve these equations matrix - wise. Obviously, one can expand it to 8 scalar equations, but the multi body system concerned here is very simple and the aim is to be able to solve complex ones - hence the question: is it possible to solve matrix DAE in Matlab, and if so - what has to be fixed in order for this to work?
Ps. I have another issue with Matlab DAE solver: input variables (known coefficient functions) for my model are time variant. As far as example is concerned, they are constant in all domain, however for my problem they change in time. This problem has been brought out here. I would be grateful if you referred to it, should you have any solution.
Finally, I managed to find correct syntax for this problem. I made a mistake of treating matrix variables (such as A, Fq) as a single entity. Below I present code that utilizes matrix approach and solves this particular DAE:
% Define symbolic variables.
q = sym('q%d',[3 1]); % state variables
a = sym('a'); k = sym('k'); % constant parameters
t = sym('t','real'); % independent variable
% Define system variables and group them in vectors:
p1(t) = sym('p1(t)'); p2(t) = sym('p2(t)'); p3(t) = sym('p3(t)');
w1(t) = sym('w1(t)'); w2(t) = sym('w2(t)'); w3(t) = sym('w3(t)');
m1(t) = sym('m1(t)'); m2(t) = sym('m2(t)');
pvect = [p1(t); p2(t); p3(t)];
wvect = [w1(t); w2(t); w3(t)];
mvect = [m1(t); m2(t)];
% Define matrices:
mass = diag(sym('ms%d',[1 3]));
Fq = [0 -1 a;
0 0 1];
A = [1 0 0;
0 1 a;
0 a -q(1)*a] * k;
% Define sets of equations and aggregate them into one set:
set1 = diff(pvect,t) == A*wvect + Fq'*mvect;
set2 = mass*diff(wvect,t) == -pvect;
set3 = Fq*wvect == 0;
eqs = [set1; set2; set3];
% Close all system variables in one vector:
vars = [pvect; wvect; mvect];
% Reduce index of the system and remove redundnat equations:
[DAEs,DAEvars] = reduceDAEIndex(eqs,vars);
[DAEs,DAEvars] = reduceRedundancies(DAEs,DAEvars);
[M,F] = massMatrixForm(DAEs,DAEvars);
We receive very simple 2x2 ODE for two variables p1(t) and w1(t). Keep in mind that after reducing redundancies we got rid of all elements from state vector q. This means that all left variables (k and mass(1,1)) are not time dependent. If there had been time dependency of some variables within the system, the case would have been much harder to solve.
% Replace symbolic variables with numeric ones:
M = odeFunction(M, DAEvars,mass(1,1));
F = odeFunction(F, DAEvars, k);
k = 2000; numericMass = 4;
F = #(t, Y) F(t, Y, k);
M = #(t, Y) M(t, Y, numericMass);
% set the solver:
opt = odeset('Mass', M); % Mass matrix of the system
TIME = [1; 0]; % Time boundaries of the simulation (backwards in time)
y0 = [1 0]'; % Initial conditions for left variables p1(t) and w1(t)
% Call the solver
[T, solution] = ode15s(F, TIME, y0, opt);
% Plot results
plot(T,solution(:,1),T,solution(:,2))

Vectorize with Matlab Meshgrid in Chebfun

I am trying to use meshgrid in Matlab together with Chebfun to get rid of double for loops. I first define a quasi-matrix of N functions,
%Define functions of type Chebfun
N = 10; %number of functions
x = chebfun('x', [0 8]); %Domain
psi = [];
for i = 1:N
psi = [psi sin(i.*pi.*x./8)];
end
A sample calculation would be to compute the double sum $\sum_{i,j=1}^10 psi(:,i).*psi(:,j)$. I can achieve this using two for loops in Matlab,
h = 0;
for i = 1:N
for j = 1:N
h = h + psi(:,i).*psi(:,j);
end
end
I then tried to use meshgrid to vectorize in the following way:
[i j] = meshgrid(1:N,1:N);
h = psi(:,i).*psi(:,j);
I get the error "Column index must be a vector of integers". How can I overcome this issue so that I can get rid of my double for loops and make my code a bit more efficient?
BTW, Chebfun is not part of native MATLAB and you have to download it in order to run your code: http://www.chebfun.org/. However, that shouldn't affect how I answer your question.
Basically, psi is a N column matrix and it is your desire to add up products of all combinations of pairs of columns in psi. You have the right idea with meshgrid, but what you should do instead is unroll the 2D matrix of coordinates for both i and j so that they're single vectors. You'd then use this and create two N^2 column matrices that is in such a way where each column corresponds to that exact column numbers specified from i and j sampled from psi. You'd then do an element-wise multiplication between these two matrices and sum across all of the columns for each row. BTW, I'm going to use ii and jj as variables from the output of meshgrid instead of i and j. Those variables are reserved for the complex number in MATLAB and I don't want to overshadow those unintentionally.
Something like this:
%// Your code
N = 10; %number of functions
x = chebfun('x', [0 8]); %Domain
psi = [];
for i = 1:N
psi = [psi sin(i.*pi.*x./8)];
end
%// New code
[ii,jj] = meshgrid(1:N, 1:N);
%// Create two matrices and sum
matrixA = psi(:, ii(:));
matrixB = psi(:, jj(:));
h = sum(matrixA.*matrixB, 2);
If you want to do away with the temporary variables, you can do it in one statement after calling meshgrid:
h = sum(psi(:, ii(:)).*psi(:, jj(:)), 2);
I don't have Chebfun installed, but we can verify that this calculates what we need with a simple example:
rng(123);
N = 10;
psi = randi(20, N, N);
Running this code with the above more efficient solution gives us:
>> h
h =
8100
17161
10816
12100
14641
9216
10000
8649
9025
11664
Also, running the above double for loop code also gives us:
>> h
h =
8100
17161
10816
12100
14641
9216
10000
8649
9025
11664
If you want to be absolutely sure, we can have both codes run with the outputs as separate variables, then check if they're equal:
%// Setup
rng(123);
N = 10;
psi = randi(20, N, N);
%// Old code
h = 0;
for i = 1:N
for j = 1:N
h = h + psi(:,i).*psi(:,j);
end
end
%// New code
[ii,jj] = meshgrid(1:N, 1:N);
hnew = sum(psi(:, ii(:)).*psi(:, jj(:)), 2);
%// Check for equality
eql = isequal(h, hnew);
eql checks if both variables are equal, and we do get them as such:
>> eql
eql =
1

How can I use syms to address N different variables

I need N variables for my equation system:
X = cell(N,1)
for k=1:N
X(k) = {sym('X(k)')};
end
After creating these variables I want to utilize them in an equation system:
for i=1:N
for j=1:N
if i~=j
S(i)=sum(X(j))
end
end
f(i)=x(i)+2*S(i)+3
end
I get the error Undefined function 'sum' for input arguments type 'cell'. How should I define the variables X(1),...X(N) without using 'cell'?
According to sym documentation you can use eg. A = sym('A%d%d', [2 2]); to create a symbolic matrix.
Is this what you mean?
N = 5;
% Initialize symbolic matrices with proper size
X = sym('x%d', [N 1]);
S = sym(zeros(N, 1));
f = sym(zeros(N, 1));
for i=1:N
for j=1:N
if i~=j
S(i) = S(i) + X(j);
end
end
f(i)=X(i)+2*S(i)+3;
end
You can convert them to a matrix using the cell2mat function.
http://www.mathworks.com/help/matlab/ref/cell2mat.html