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 )
Related
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.
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);
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.
I'm trying to get into the field of computer vision, and to start I implemented a Sobel filter in MATLAB, which I read about here: http://en.wikipedia.org/wiki/Sobel_operator
Here is the code:
image = double(image);
kernelx = [ -1, 0, 1;
-2, 0, 2;
-1, 0, 1];
kernely = [ 1, 2, 1;
0, 0, 0;
-1, 0, 1];
height = size(image,1);
width = size(image,2);
channel = size(image,3);
for i = 2:height - 1
for j = 2:width - 1
for k = 1:channel
magx = 0;
magy = 0;
for a = 1:3
for b = 1:3
magx = magx + (kernelx(a, b) * image(i + a - 2, j + b - 2, k));
magy = magy + (kernely(a, b) * image(i + a - 2, j + b - 2, k));
end;
end;
edges(i,j,k) = sqrt(magx^2 + magy^2);
end;
end;
end;
Here is an image I tested it on:
This is the result:
I don't know where to go from here, I've tried looking at line thinning or thresholding, what steps should I take to make this run better?
Your kernel in the y direction seems to be incorrect, it should be
[ 1, 2, 1;
0, 0, 0;
-1, -2, -1];
Further, if you want to improve edge detection, you can look into Hysteresis, its an easy way to complete some obvious contours in an image which might be missed out
http://en.wikipedia.org/wiki/Canny_edge_detector#Tracing_edges_through_the_image_and_hysteresis_thresholding
Results first
1 Typo in Kernel
As Bharat Singh pointed out, your y-Kernel looks wrong. (Later analysis shows that it changes the results but that isn't the main problem.) If you want you can use your original kernel in my code below to see what the result is. (For posterity: kernely(3,:) = [-1, 0, 1];) Basically, it looks like the input image.
2 Use Convolution to make debugging fast
Before you do anything else, just use the convolution function that is provided by Matlab.
Also, to speed things up, use conv2. While you are experimenting, you might want to use parfor instead of the outer for-loop. The convolution is instantaneous on my machine and the parfor version takes minutes.
3 imshow is causing problems
There is an alternative named imagesc, that scales the image automatically. Or you can call imshow(image, []). But the problem in this case is that each channel looks right but the automated mixing of the channels by Matlab doesn't work. (Remember they are not RGB anymore but more like magnitude of the R-channel derivative |dR| etc. )
Check this by looking at each resulting channel individually (imshow(E(:,:,1), [])) or as a euclidean average (see my code). If you run imshow(E, []) you get the blown out highlights. The same happens if you pass it through rgb2gray first.
Code
image = imread('cat.jpg');
image = double(image);
kernelx = [ -1, 0, 1;
-2, 0, 2;
-1, 0, 1];
kernely = [ 1, 2, 1;
0, 0, 0;
-1, -2, -1];
height = size(image,1);
width = size(image,2);
channel = size(image,3);
edges = zeros(height, width, channel);
if exist('chooseSlow', 'var')
parfor i = 2:height - 1
for j = 2:width - 1
for k = 1:channel
magx = 0;
magy = 0;
for a = 1:3
for b = 1:3
magx = magx + (kernelx(a, b) * image(i + a - 2, j + b - 2, k));
magy = magy + (kernely(a, b) * image(i + a - 2, j + b - 2, k));
end;
end;
edges(i,j,k) = sqrt(magx^2 + magy^2);
end;
end;
end;
end
%% Convolution approach
E = zeros(height, width, channel);
for k=1:channel
Magx = conv2(image(:,:,k), kernelx, 'same');
Magy = conv2(image(:,:,k), kernely, 'same');
E(:,:,k) = sqrt(Magx .^2 + Magy .^2);
end
imshow(sqrt(E(:,:,1).^2 + E(:,:,2).^2 + E(:, :, 3) .^2 ), []);
print('result.png', '-dpng');
I would like to write a matlab function to find an equation of a linear classifier for 2 separable sets of points using one single-layer perceptron. I have got 2 files:
script file - run.m:
x_1 = [3, 3, 2, 4, 5];
y_1 = [3, 4, 5, 2, 2];
x_2 = [6, 7, 5, 9, 8];
y_2 = [3, 3, 4, 2, 5];
target_array = [0 0 0 0 0 1 1 1 1 1];
[ func ] = classify_perceptron([x_1 x_2; y_1 y_2], target_array);
x = -2:10;
y = arrayfun(func, x);
plot(x_1, y_1, 'o', x_2, y_2, 'X', x, y);
axis([-2, 10, -2, 10]);
classify_perceptron.m
function [ func ] = classify_perceptron( points, target )
% points - matrix of x,y coordinates
% target - array of expected results
% func - function handler which appropriately classifies a point
% given by x, y arguments supplied to this function
target_arr = target;
weights = rand(1, 2);
translation = rand();
for i=1:size(points, 2)
flag = true;
while flag
result = weights * points(:, i) + translation;
y = result > 0;
e = target_arr(1, i) - y;
if e ~= 0
weights = weights + (e * points(:, i))';
translation = translation + e;
else
flag = false;
end
end
end
func = #(x)(-(translation + (weights(1, 1) * x)) / weights(1, 2));
return
end
The problem is that I don't know where I am making the mistake that leads to incorrect result. It looks like the slope of the line is right, however translation should be a bit bigger. I would be really thankful for pointing me in the right direction. The result I get is presented in the picture below:
Ok, so I have made a significant progress. In case someone runs into the same problem I present to you the solution. The problem has been solved by adding a variable learning_rate = 0.1 and packing the loop iterating over points into another loop iterating as many times as specified in the variable epochs (e.g. 300) .