Why in the content of cvx, subs() does not work? - matlab

TLDR
I wrote a code to solve a SDP problem. I used subs to change syms into numerical value before cvx content and I did see the numerical value after I printed out the variables. But whos() did show that the variable is still syms type which I think might be the key point where I went wrong. The error message shows like this:
Unable to convert 'cvx' to 'sym'.
It went wrong at the line trace(rhot*X_ele(:,:,i)) == value(i); where rhot is symbolic instead of numeric.
Details
I'm trying to write a SDP problem as follows:
function [res] = NH_SDP(rhot,p,value,t)
dim = size(rhot,1);
for i = 1:p
drhot(:,:,i) = diff(rhot,t(i));
end
drhot = subs(drhot,t,value);
rhot = subs(rhot,t,value);
rhott = kron(eye(p),rhot);
% cvx process
cvx_begin sdp
% cvx part
variable L_ele(dim,dim,(1+p)*p/2) hermitian;
variable X_ele(dim,dim,p) hermitian;
cnt = 1;
for i = 1:p
for j = i:p
if i == j
L((i-1)*dim+1:i*dim,(j-1)*dim+1:j*dim) = L_ele(:,:,cnt);
cnt = cnt + 1;
else
L((i-1)*dim+1:i*dim,(j-1)*dim+1:j*dim) = L_ele(:,:,cnt);
L((j-1)*dim+1:j*dim,(i-1)*dim+1:i*dim) = L_ele(:,:,cnt);
cnt = cnt + 1;
end
end
end
for i = 1:p
X((i-1)*dim+1:i*dim,1:dim) = X_ele(:,:,i);
end
for i = 1:p
X2(1:dim,(i-1)*dim+1:i*dim) = X_ele(:,:,i);
end
% rhott = [[0, 0, 0, 0, 0, 0, 0, 0]
% [0, 1/2, 7/20, 0, 0, 0, 0, 0]
% [0, 7/20, 1/2, 0, 0, 0, 0, 0]
% [0, 0, 0, 0, 0, 0, 0, 0]
% [0, 0, 0, 0, 0, 0, 0, 0]
% [0, 0, 0, 0, 0, 1/2, 7/20, 0]
% [0, 0, 0, 0, 0, 7/20, 1/2, 0]
% [0, 0, 0, 0, 0, 0, 0, 0]];
rhott = double(rhott);
rhot = double(rhot);
drhot = double(drhot);
minimize(trace(rhott*L));
subject to
% rhot = [0 0 0 0;0 1/2 7/20 0;0 7/20 1/2 0;0 0 0 0];
% drhot
for i =1:p
trace(rhot*X_ele(:,:,i)) == value(i);
end
for i = 1:p
for j = 1:p
if i == j
trace(drhot(:,:,i)*X_ele(:,:,j)) == 1;
else
trace(drhot(:,:,i)*X_ele(:,:,j)) == 0;
end
end
end
% semidefinit constraint.
[L X; X2 eye(dim)] >= 0;
cvx_end
res = cvx_optval;
X_ele
end
There are three line in the above code:
rhott = double(rhott);
rhot = double(rhot);
drhot = double(drhot);
If I delete those three lines, there will be error Unable to convert 'cvx' to 'sym'. while my rhot have already changed into numerical value by subs function and I print it out to see numerical numbers even though whos function show that the type of rhot is still syms.

To cite the documentation on subs():
snew = subs(s,old,new) returns a copy of s, replacing all occurrences
of old with new, and then evaluates s. Here, s is an expression of
symbolic scalar variables or a symbolic function, and old specifies
the symbolic scalar variables or symbolic function to be substituted.
emphasis mine. In other words: subs() changes your symbolic expression to a new symbolic expression.
double() on the other hand, converts whatever you feed it to a type double, i.e. a numeric value. rhott thus initially contains a symbolic value, even if that is a number. double(rhott) then evaluates it to a numeric number, rather than a symbolic one.
I presume the problem is here
drhot = subs(drhot,t,value);
rhot = subs(rhot,t,value);
rhott = kron(eye(p),rhot);
You're substituting all occurrences of t within drhot with value, and then again on rhot. if drhot is already symbolic, i.e. if the inputs rhot, value, t are symbolic, this makes sense and indeed a double call will make your system numeric, rather than symbolic. If, on the other hand, all inputs are numeric, subs() unnecessarily converts everything to symbolic variables, slowing execution speed.

Related

If-then-else in anonymous function

I'm trying to use some kind of if-then-else statement in an anonymous function, which itself is part of cellfun. I have a cell array that contains a number of double matrices. I would like to substitute all positive numbers in all double matrices with +1, and all negative numbers with -1. I am wondering if I can use an anonymous function rather than coding a separate function that I then call from within the cellfun?
Here's the toy example:
mat = [2, 2, 0, -2; -2, 0, 0, 2; -2, 2, -2, 2]
cellarray = repmat({mat}, 3, 1)
I'm looking for something like this:
new_cellarray = cellfun(#(x) if x > 0 then x = 1 elseif x < 0 then x = -1, cellarray, 'UniformOutput', false)
I also tried this, however, apparently I am not allowed to put an equal sign into an anonymous function.
new_cellarray = cellfun(#(x) x(x > 0) = 1, cellarray, 'UniformOutput', false)
new_cellarray = cellfun(#(x) x(x < 0) = -1, cellarray, 'UniformOutput', false)
You can use the built-in function sign, which returns 1, 0, or -1 depending on its input:
mat = [2, 2, 0, -2; -2, 0, 0, 2; -2, 2, -2, 2];
cellarray = repmat({mat}, 3, 1);
new_cellarray = cellfun(#sign, cellarray, 'UniformOutput', false);

connected points and smallest value

I would like to know how I can to take each D(Points) and look at its connexed points (in 8 connex) but only on the side of the limit (ie the diagonal points top and bottom right and on the same line to the right, ie 3 points Connect on 8) and select the coordinates of the connexed point which has the smallest value of D. And I would like to repeat this until I obtain that the smallest value of D equal 0
% Creation of matrix example
c=zeros(500,500);
c(1:100,250)=1;c(100:300,200)=1;c(300:400,270)=1; c(400:500,250)=1;
c(100,200:250)=1;c(300,200:270)=1;c(400,250:270)=1;
figure, imagesc(c)
Points= [211,388;64,200;160,437;237,478;110,270;100,34];
hold on, plot(Points(:,1),Points(:,2),'ro'), hold off
%Distance map
D = bwdist(cumsum(c, 2) > 0, 'euclidean');
figure, imagesc(D)
The key function here is sub2ind which converts subscripts to linear indices. It is very handy when you need to work on specific points inside an array.
% Let's prepare the 8 shifts needed (i add a no-move shift in first place to simplify the algorithm)
delta_x = [0, -1, -1, -1, 0, 0, 1, 1, 1];
delta_y = [0, -1, 0, 1, -1, 1, -1, 0, 1];
sz_D = size(D);
n_points = size(Points, 1);
is_running = true;
while is_running
% All the shift combinaisons
new_ind_x = bsxfun(#plus, Points(:,1), delta_x);
new_ind_y = bsxfun(#plus, Points(:,2), delta_y);
% Saturation to stay in the image
new_ind_x = min(max(new_ind_x, 1), sz_D(2));
new_ind_y = min(max(new_ind_y, 1), sz_D(1));
% Get the values in D and find the index of the minimum points
points_lin_ind = sub2ind(sz_D, new_ind_y, new_ind_x);
points_val = D(points_lin_ind);
[min_values, min_ind] = min(points_val, [], 2);
% Select subscripts in new_ind_x and new_ind_y
min_lin_ind = sub2ind([n_points, 9], (1:n_points).', min_ind);
Points = [new_ind_x(min_lin_ind), new_ind_y(min_lin_ind)];
% Exit condition
if all(min_values == 0)
is_running = false;
end
end
PS : Not tested.

Matlab symbolic function susbstitution

I am defining a symbolic function
syms x(n);
x(n) = (n==0);
n=-1:1;
When I try x(n)+x(n-1)
I get
[ -1 == 0, 0 == 0, 1 == 0]
+[ -2 == 0, -1 == 0, 0 == 0]
=[ -3 == 0, -1 == 0, 1 == 0]
I want to force the symbolic function to substitute the values so I get the following results instead.
[ 0, 1, 0]
+[ 0, 0, 1]
=[ 0, 1, 1]
I tried something like x(n) = logical(n==0); and x(n) = double(n==0); but I got the same result.
I know that double(x(n))+double(x(n-1)) works, but I want to use x(n) directly and do the substitution in the definition of the symbolic function.
Can this be done?
I think piecewise is the only way to cast a boolean to an integer in Mupad. Unfortunately, it only available in Mupad itself, so you have to use evalin to get it:
syms asinteger(fun) x(n)
asinteger(fun) = evalin(symengine,'piecewise([fun,1],[Otherwise,0])');
x(n) = asinteger(n==0);
n=-1:1;
>> x(n)+x(n-1)
ans =
[ 0, 1, 1]
Think of the asinteger function as the symbolic version of double or int64.
I think the easiest way to do is to use isAlways, which evaluates if an expression (equality or inequality) is true:
syms x(n);
x(n) = (n==0);
m = -1:1; % Use a different variable to not overwrite symbolic n
isAlways(x(m))+isAlways(x(m-1))
Or you can use an anonymous function to avoid multiple calls to isAlways:
m = -1:1;
x = #(n)isAlways(sym(n)==0);
x(m)+x(m-1)
Both of these return an array of doubles ([0 1 1]). You can use logical to convert this to a logical array. You might also find sym/isequaln useful in some cases.

Precision of calculations [duplicate]

This question already has an answer here:
How to obtain Fortran precision in MatLAB
(1 answer)
Closed 7 years ago.
I am doing a calculation in Fortran on a double-precision variable, and after the calculation the variable gets the value -7.217301636365630e-24.
However, when I do the same computation in Matlab, the variable just gets the value 0. Is there a way to increase the precision of MatLAB when doing calculations such that I would also be able to get something on the order of 7e-24?
Ideally, it would be something I could apply to all calculations in the script and not just a single variable. Something similar to when using format long.
For me this kind of precision is crucial as I need to determine if a variable is indeed negative or not.
I have added the code. It is rather long, but I couldn't trim it further without throwing away variables and their precision. The last term, Ax(i,:,:), is the one that I would like to have a very high precision on. So the important stuff occurs only in the last line.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CONSTANTS
clc
clear all
sym_weight = [4/9, 1/9,1/9,1/9,1/9, 1/36,1/36,1/36,1/36];
dir_x = [ 0, 1, 0, -1, 0, 1, -1, -1, 1];
dir_y = [ 0, 0, 1, 0, -1, 1, 1, -1, -1];
ly = 11; lx = ly;
xC = 5; yC=xC;
density_high = 1.0;
density_low = 0.1;
radius = 2;
interface_w = 1;
sigma_st = 0.0001;
beta = 12*sigma_st/(interface_w*(density_high-density_low)^4);
kappa = 1.5*sigma_st*interface_w/(density_high-density_low)^2;
saturated_density = 0.5*(density_high+density_low);
for x=1:lx
for y=1:ly
for i=1:9
fIn(i, x, y) = sym_weight(i)*density_high;
gIn(i, x, y) = 3*sym_weight(i);
test_radius = sqrt((x-xC)^2 + (y-yC)^2);
if(test_radius <= (radius+interface_w))
fIn(i, x, y) = sym_weight(i)*( saturated_density - 0.5*(density_high-density_low)*tanh(2*(radius-sqrt((x-xC)^2 + (y-yC)^2))/interface_w) );
end
end
end
end
density_2d = ones(lx)*saturated_density;
for i=1:lx
density_aux(:,:,i) = abs(density_2d(:, i)');
end
density_local = sum(fIn);
L_density_local = (+1.0*(circshift(density_local(1,:,:), [0, +1, +1]) + circshift(density_local(1,:,:), [0, -1, +1]) + circshift(density_local(1,:,:), [0, +1, -1]) + circshift(density_local(1,:,:), [0, -1, -1])) + ...
+4.0*(circshift(density_local(1,:,:), [0, +1, +0]) + circshift(density_local(1,:,:), [0, -1, +0]) + circshift(density_local(1,:,:), [0, +0, +1]) + circshift(density_local(1,:,:), [0, +0, -1])) + ...
-20.0*density_local(1,:,:));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
chem_pot = 4*beta*(density_local-density_low).*(density_local-density_high).*(density_local-density_aux) - kappa*L_density_local/6;
for i=3
Ax(i,:,:) = (+circshift(chem_pot(1,:,:), [0,-2*dir_x(i),-2*dir_y(i)]) - chem_pot(1,:,:));
end
You have not shown the fortran code, but be aware that in Fortran, when you do this:
density_low = 0.1
The literal 0.1 is single precision, regardless of the type of density_low.
All of those literals need to be expressed as 0.1D0 or 0.1_k where k is the appropriate kind integer.
(Sorry if you knew that, but its a common mistake )

Generating an equal number of random integers in response to a previous input in Matlab

I'm trying to create a random number matrix in matlab. However, I'm struggling a little with the logic of some of it. What I want is this:
I need it to loop through a predefined random matrix (2 rows, n columns) 50% of which are 1's and 50% are 0's (I can already do this part). Everytime it encounters a 1 it should then enter another loop which puts a 1,2,3 or 4 in the corresponding position in the second row. However (and this is the part I'm struggling with) I need it to have an equal number of 1's, 2's, 3's and 4's in the second row. So for example:
The matrix n = [0, 1, 0, 1, 1, 1, 0, 0; 0,0,0,0,0,0,0,0] should run through the script and produce something like: n = [0, 1, 0, 1, 1, 1, 0, 0; 0, 1, 0, 3, 2, 4, 0, 0]
This is what I have so far:
function pureToneTimer
ptpschedule = [0, 1, 0, 1, 1, 1, 0, 0; 0, 0, 0, 0, 0 , 0, 0, 0]
a = 0;
b = 0;
c = 0;
d = 0;
x = length(ptpschedule)/4
for n = 1:length(ptpschedule)
if ptpschedule(1,n) == 1
while a < x && b < x && c < x && d < x
i = randi(4)
ptpschedule(2,n) = i;
switch i
case 1
a = a + 1;
case 2
b = b + 1;
case 3
c = c + 1;
case 4
d = d + 1;
end
end
end
end
assignin('caller', 'ptpschedule', ptpschedule)
end
Sorry if this turns out to be a really trivial question. I'm just struggling to wrap my head around it!
Thanks,
Martin
This does what you want:
V = 4; %// number of values you want. 4 in your example
ind = ptpschedule(1,:)>0; %// logical index of positive values in the first row
n = nnz(ind);
vals = mod(0:n-1, V)+1; %// values to be randomly thrown in.
%// This guarantees the same number of each value if n is a multiple of V
ptpschedule(2,ind) = vals(randperm(n)); %// fill values in second row in random order
If the number of 1's in the first row is a multiple of V, this generates each value 1, 2, ... V the same number of times in the second row.
Otherwise, some values in the second row will appear once more than other values.
Example:
ptpschedule = [0, 1, 0, 1, 1, 1, 0, 0
0, 0, 0, 0, 0, 0, 0, 0];
V = 4;
produces
ptpschedule =
0 1 0 1 1 1 0 0
0 3 0 4 2 1 0 0