Hello again logical friends!
I’m aware this is quite an involved question so please bear with me! I think I’ve managed to get it down to two specifics:- I need two loops which I can’t seem to get working…
Firstly; The variable rollers(1).ink is a (12x1) vector containing ink values. This program shares the ink equally between rollers at each connection. I’m attempting to get rollers(1).ink to interact with rollers(2) only at specific timesteps. The ink should transfer into the system once for every full revolution i.e. nTimesSteps = each multiple of nBins_max. The ink should not transfer back to rollers(1).ink as the system rotates – it should only introduce ink to the system once per revolution and not take any back out. Currently I’ve set rollers(1).ink = ones but only for testing. I’m truly stuck here!
Secondly; The reason it needs to do this is because at the end of the sim I also wish to remove ink in the form of a printed image. The image should be a reflection of the ink on the last roller in my system and half of this value should be removed from the last roller and taken out of the system at each revolution. The ink remaining on the last roller should be recycled and ‘re-split’ in the system ready for the next rotation.
So…I think it’s around the loop beginning line86 where I need to do all this stuff. In pseudo, for the intermittent in-feed I’ve been trying something like:
For k = 1:nTimeSteps
While nTimesSteps = mod(nTimeSteps, nBins_max) == 0 % This should only output when nTimeSteps is a whole multiple of nBins_max i.e. one full revolution
‘Give me the ink on each segment at each time step in a matrix’
End
The output for averageAmountOfInk is the exact format I would like to return this data except I don’t really need the average, just the actual value at each moment in time. I keep getting errors for dimensional mismatches when I try to re-create this using something like:
For m = 1:nTimeSteps
For n = 1:N
Rollers(m,n) = rollers(n).ink’;
End
End
I’ll post the full code below if anyone is interested to see what it does currently. There’s a function at the end also which of course needs to be saved out to a separate file.
I’ve posted variations of this question a couple of times but I’m fully aware it’s quite a tricky one and I’m finding it difficult to get my intent across over the internets!
If anyone has any ideas/advice/general insults about my lack of programming skills then feel free to reply!
%% Simple roller train
% # Single forme roller
% # Ink film thickness = 1 micron
clc
clear all
clf
% # Initial state
C = [0,70; % # Roller centres (x, y)
10,70;
21,61;
11,48;
21,34;
27,16;
0,0
];
R = [5.6,4.42,9.8,6.65,10.59,8.4,23]; % # Roller radii (r)
% # Direction of rotation (clockwise = -1, anticlockwise = 1)
rotDir = [1,-1,1,-1,1,-1,1]';
N = numel(R); % # Amount of rollers
% # Find connected rollers
isconn = #(m, n)(sum(([1, -1] * C([m, n], :)).^2)...
-sum(R([m, n])).^2 < eps);
[Y, X] = meshgrid(1:N, 1:N);
conn = reshape(arrayfun(isconn, X(:), Y(:)), N, N) - eye(N);
% # Number of bins for biggest roller
nBins_max = 50;
nBins = round(nBins_max*R/max(R))';
% # Initialize roller struct
rollers = struct('position',{}','ink',{}','connections',{}',...
'rotDirection',{}');
% # Initialise matrices for roller properties
for ii = 1:N
rollers(ii).ink = zeros(1,nBins(ii));
rollers(ii).rotDirection = rotDir(ii);
rollers(ii).connections = zeros(1,nBins(ii));
rollers(ii).position = 1:nBins(ii);
end
for ii = 1:N
for jj = 1:N
if(ii~=jj)
if(conn(ii,jj) == 1)
connInd = getConnectionIndex(C,ii,jj,nBins(ii));
rollers(ii).connections(connInd) = jj;
end
end
end
end
% # Initialize averageAmountOfInk and calculate initial distribution
nTimeSteps = 1*nBins_max;
averageAmountOfInk = zeros(nTimeSteps,N);
inkPerSeg = zeros(nTimeSteps,N);
for ii = 1:N
averageAmountOfInk(1,ii) = mean(rollers(ii).ink);
end
% # Iterate through timesteps
for tt = 1:nTimeSteps
rollers(1).ink = ones(1,nBins(1));
% # Rotate all rollers
for ii = 1:N
rollers(ii).ink(:) = ...
circshift(rollers(ii).ink(:),rollers(ii).rotDirection);
end
% # Update all roller-connections
for ii = 1:N
for jj = 1:nBins(ii)
if(rollers(ii).connections(jj) ~= 0)
index1 = rollers(ii).connections(jj);
index2 = find(ii == rollers(index1).connections);
ink1 = rollers(ii).ink(jj);
ink2 = rollers(index1).ink(index2);
rollers(ii).ink(jj) = (ink1+ink2)/2;
rollers(index1).ink(index2) = (ink1+ink2)/2;
end
end
end
% # Calculate average amount of ink on each roller
for ii = 1:N
averageAmountOfInk(tt,ii) = sum(rollers(ii).ink);
end
end
image(5:20) = (rollers(7).ink(5:20))./2;
inkPerSeg1 = [rollers(1).ink]';
inkPerSeg2 = [rollers(2).ink]';
inkPerSeg3 = [rollers(3).ink]';
inkPerSeg4 = [rollers(4).ink]';
inkPerSeg5 = [rollers(5).ink]';
inkPerSeg6 = [rollers(6).ink]';
inkPerSeg7 = [rollers(7).ink]';
This is an extended comment rather than a proper answer, but the comment box is a bit too small ...
Your code overwhelms me, I can't see the wood for the trees. I suggest that you eliminate all the stuff we don't need to see to help you with your immediate problem (all those lines drawing figures for example) -- I think it will help you to debug your code yourself to put all that stuff into functions or scripts.
Your code snippet
For k = 1:nTimeSteps
While nTimesSteps = mod(nTimeSteps, nBins_max) == 0
‘Give me the ink on each segment at each time step in a matrix’
End
might be (I don't quite understand your use of the while statement, the word While is not a Matlab keyword, and as you have written it the value returned by the statement doesn't change from iteration to iteration) equivalent to
For k = 1:nBins_max:nTimeSteps
‘Give me the ink on each segment at each time step in a matrix’
End
You seem to have missed an essential feature of Matlab's colon operator ...
1:8 = [1 2 3 4 5 6 7 8]
but
1:2:8 = [1 3 5 7]
that is, the second number in the triplet is the stride between successive elements.
Your matrix conn has a 1 at the (row,col) where rollers are connected, and a 0 elsewhere. You can find the row and column indices of all the 1s like this:
[ri,ci] = find(conn==1)
You could then pick up the (row,col) locations of the 1s without the nest of loops and if statements that begins
for ii = 1:N
for jj = 1:N
if(ii~=jj)
if(conn(ii,jj) == 1)
I could go on, but won't, that's enough for one comment.
Related
I wrote a code for generating random number of rods on Matlab within a specified domain and then saving the output in a text file. I would like to ask for help on adding the following options to the code;
(i) if the randomly generated rod exceeds the specified domain size, the length of that rod should be shortened so that to keep it in that particular domain.
(ii) i would like to avoid the overlapping of the newly generated number (rod) with that of the previous one, in case of overlap generate another place for the new rod.
I can't figure out how shall I do it. It would be of much help if someone may help me write code for these two options.
Thank you
% myrandom.m
% Units are mm.
% domain size
bx = 160;
by = 40;
bz = 40;
lf = 12; % rod length
nf = 500; % Number of rods
rns = rand(nf,3); % Start
rne = rand(nf,3)-0.5; % End
% Start Points
for i = 1:nf
rns(i,1) = rns(i,1)*bx;
rns(i,2) = rns(i,2)*by;
rns(i,3) = rns(i,3)*bz;
end
% Unit Deltas
delta = zeros(nf,1);
for i = 1:nf
temp = rne(i,:);
delta(i) = norm(temp);
end
% Length Deltas
rne = lf*rne./delta;
% End Points
rne = rns + rne;
fileID = fopen('scfibers.txt','w');
for i = 1:nf
fprintf(fileID,'%12.8f %12.8f %12.8f\r\n',rns(i,1),rns(i,2),rns(i,3));
fprintf(fileID,'%12.8f %12.8f %12.8f\r\n\r\n',rne(i,1),rne(i,2),rne(i,3));
end
fclose(fileID);
I would start from writing a function that creates the random rods:
function [rns,rne] = myrandom(domain,len,N)
rns = rand(N,3).*domain; % Start --> rns = bsxfun(#times,rand(N,3),domain)
rne = rand(N,3)-0.5; % End
% Unit Deltas
delta = zeros(N,1);
for k = 1:N
delta(k) = norm(rne(k,:));
end
% Length Deltas
rne = len*rne./delta; % --> rne = len*bsxfun(#rdivide,rne,delta)
% End Points
rne = rns + rne;
% remove rods the exceed the domain:
notValid = any(rne>domain,2); % --> notValid = any(bsxfun(#gt,rne,domain),2);
rns(notValid,:)=[];
rne(notValid,:)=[];
end
This function gets the domain as [bx by bz] and also the length of the rods as len, and N the number of rods to generate. Note that using elementwise multiplication (.*) I have eliminated the first for loop.
In case you use MATLAB version prior to 2016b, you need to use bsxfun:
In MATLAB® R2016b and later, the built-in binary functions listed in this table independently support implicit expansion.
The affected lines are marked with --> in the code (with the alternative).
The last three lines in the function remove from the result all the rodes that exceed the domain size (I hope I got you correctly on this).
Next, I call this function within a script:
% domain size
bx = 160;
by = 40;
bz = 40;
domain = [bx by bz];
lf = 12; % rod length
nf = 500; % Number of rods
[rns,rne] = myrandom(domain,lf,nf);
u = unique([rns rne],'rows');
remain = nf-size(u,1);
while remain>0
[rns_temp,rne_temp] = myrandom(domain,lf,remain);
rns = [rns;rns_temp];
rne = [rne;rne_temp];
u = unique([rns rne],'rows');
remain = nf-size(u,1);
end
After the basic definitions, the function is called and returns rne and rns, which are probably smaller than nf. Then we check for duplicates, and store all unique rods in u. We calculate the rods remain to compute, and we use a while loop to generate new rods as needed. In each iteration of the loop, we add the newly created rods to those we have in rne and rns, and check how many unique vectors we have now, and if there are enough we quit the loop (then you can add printing to the file).
Note that:
I was not sure what you mean by "in case of overlap generate another place for the new rod" - do you want to have more than nf rods if some are duplicates, that from which nf are unique (what the code above does)? or you want to remove the duplicates and remain only with nf unique rods? In the case of the latter option, I would insert the unique function part into the function that creates the rods myrandom.
The wile loop as written above is not efficient since no preallocating of memory is done. I'm not sure that this is possible if you just want to create more rods but keep the duplicates, but if not (the second option in 1 above) and if you are going to use this allot, then preallocating is very recommended.
I'm using a for loop to add more nodes and edges on my plot. However, when I add labels on new edges the old labels are removed. I don't know how to keep old edge-labels nor how to store the results of labeledge.
This is what I have got so far.
for r = 1: 10
for j = 1:10
H = addnode(P,nodeName{r}{j});
P = addedge(H, s{r}{j}, t{r}{j}, w{r}{j});
figure;
hold on;
h = plot(P);
labeledge(h,s{r}{j},t{r}{j},labelText{r}{j})
end
end
Every time in the new plot, I can only see the newest cluster of labels while old labels are gone. Ideally, I'd love to hold on the results of labeledge but hold on can't do this. I need to show labels in each step in the loop, thus adding another overall labeledge is not my ideal solution. Any hint would be appreciated.
Edit: All my variables are multiple cells of difference sizes in cell arrays. I use for loop to help to pick up vectors from cells because I don't know how to insert all the levels of information from such cell arrays of cells etc. into addNode function.
The main problem in your code is that you keep plotting the graph again and again. This isn't necessary. Instead, use one loop to create the graph object (G), then plot it all at once, and then use another loop for labeling the graph:
P = graph;
for r = 1: 10
for j = 1:10
P = addedge(P, s{r}{j}, t{r}{j}, w{r}{j});
end
end
h = plot(P);
for r = 1: 10
for j = 1:10
labeledge(h,s{r}{j},t{r}{j},labelText{r}{j})
end
end
If you wish to plot your graph on every iteration, you can use subgraph for that:
for k = 1:height(P.Nodes)
H = subgraph(P,1:k);
figure;
h = plot(H);
c = 1;
out = false;
for r = 1: 10
if ~out
for j = 1:10
if c < k
labeledge(h,c,labelText{r}{j})
else
out = true;
break
end
c = c+1;
end
else
break
end
end
end
Besides that, you should know that (from Matlab documentation):
For the best performance, construct graphs all at once using a single call to graph. Adding nodes or edges in a loop can be slow for large graphs.
Also, regardless of the above recommendation, for an easier manipulation of your data, you should first convert your cells to an array. If your cell array contains a different number of elements in each cell, then it is better to collapse it all to one column:
C = [s{:}]; % and the same for t and w
while any(cellfun(#iscell,C))
C = vertcat(C{:});
end
C = cellfun(#(x) x(:),C,'UniformOutput', false);
S = vertcat(C{:});
Labels = [labelText{:}]; % and the same nodeName
while any(cellfun(#iscell,Labels))
Labels = vertcat(Labels{:});
end
Try to remove the 'figure;' command out of the FOR loop and try to see if this worked.
I am running a program that needs to take some time. It is better to show the running progress in real time, showing up in the command window. Something like below:
>>>>>>>> completed 90%
Assume the program runs though multiple loops:
for ii = 1:n
for jj = 1:m
for kk = 1:s
.....
end
end
end
Is there any efficient way or some special function to achieve that? I don't want to use waitbar.m since I also have other results printed in real time in the command window. These printed results, coupled with the texted progress, are used for the convenience of inspection.
First of all you need to compute the percentage step every time the inner loop advances. This can be done by computing:
percent_step = 1.0 / n / m / s
Then you can just force MATLAB to print on the same line by using a concatenation of \b (backspace character). Here is a MWE that just compute a random 10x10 matrix and get its transpose (just to show the percentage progress):
backspaces = '';
percentage = 0;
% DEFINE n, m, s as you wish, here I put some random values
n = 100;
m = 15;
s = 24;
percent_step = 100.0 / n / m / s;
for ii = 1:n
for jj = 1:m
for kk = 1:s
% Do stuff
a = rand(10);
b = a';
% Print percentage progress
percentage = percentage + percent_step;
perc_str = sprintf('completed %3.1f', percentage);
fprintf([backspaces, perc_str]);
backspaces = repmat(sprintf('\b'), 1, length(perc_str));
end
end
end
If you don't mind a progress display opening up in a separate window, you could use the Matlab waitbar.
For your example, say you wanted to compute a waitbar based on the number of iterations completed over the outer loop, it would be something like
h=waitbar(0, 'My waitbar');
for ii = 1:n
for jj = 1:m
for kk = 1:s
.....
end
end
fraction_done = ii/n;
waitbar(fraction_done)
end
close (h)
If you really want a text-based display, there is the waittext available on MATLAB File Exchange at https://www.mathworks.com/matlabcentral/fileexchange/56424-waittext.
I have not worked with this but it appears to be similar to what you wanted.
I'm looking for a way to update certain elements in a vector [nx113] for every full rotation of my system.
%% # Iterate through timesteps
for tt = 1:nTimeSteps
% # Initialise ink on transfer roller
rollers(2).ink = [zeros(1,98),ones(1,5),zeros(1,113)];
% # Rotate all rollers
for ii = 1:N
rollers(ii).ink(:) = ...
circshift(rollers(ii).ink(:),rollers(ii).rotDirection);
end
% # Update all roller-connections
for ii = 1:N
for jj = 1:nBins(ii)
if(rollers(ii).connections(jj) ~= 0)
index1 = rollers(ii).connections(jj);
index2 = find(ii == rollers(index1).connections);
ink1 = rollers(ii).ink(jj);
ink2 = rollers(index1).ink(index2);
rollers(ii).ink(jj) = (ink1+ink2)/2;
rollers(index1).ink(index2) = (ink1+ink2)/2;
end
end
end
% # Calculate average amount of ink on each roller
for ii = 1:N
averageAmountOfInk(tt,ii) = mean(rollers(ii).ink);
end
rollers(18).TakeOff = averageAmountOfInk*0.6;
end
the vector rollers(2).ink is the vector i'd like to update. currently the vector is populated only once so i have ones from row 98:103. I would like this range of elements to be populated for each 'rotation' of my system not just the first time.
The reason - I'm trying to show ink being added intermittently from only a small section of the roller surface, hence the need for only five cells to be populated.
i thought that if i iterated from 1 to the number of timesteps, in steps size nBins-Max in the loop:
for tt = 1:nBins_max:nTimeSteps
this doesn't seem to be what i'm after.
I'm also hoping to remove ink from the system at the end. for every revolution i would like to be able to remove a percentage of ink on each rotation so it does not stay in the system (as if it was being printed onto a sheet and taken away).
Hopefully someone can understand this and perhaps offer some advice on how to proceed on either or both of my issues.
Your explanation doesn't quite match your code (or vice-versa if you prefer) so I'm not entirely sure what you want to do, but the following may help you towards a solution or towards expressing your problem more clearly.
The vector rollers(2).ink has 1 row and 216 columns, so an operation such as rollers(2).ink(98:103) = something is not updating rows 98 through to 103. Note also that element 98 of that vector is initialised to 0, it's not included in the elements which are initialised to 1.
You write that you want to update a range of the elements in that vector, then write a loop statement for tt = 1:nBins_max:nTimeSteps which strides over a vector of time steps. Surely you want to write something like rollers(2).ink(99:103) = new_values.
As for removing ink from the rollers at every rotation, you could just execute a line such as rollers(2).ink = rollers(2).ink * 0.975 every rotation; obviously you'll want to replace the removal rate of 2.5% every rotation that I have chosen with whatever is right for your simulation.
I have the following code, pasted below. I would like to change it to only average the 10 most recently filtered images and not the entire group of filtered images. The line I think I need to change is: Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;, but how do I do it?
j=1;
K = 1:3600;
window = zeros(1,10);
Yout = zeros(10,column,row);
figure;
y = 0; %# Preallocate memory for output
%Load one image
for i = 1:length(K)
disp(i)
str = int2str(i);
str1 = strcat(str,'.mat');
load(str1);
D{i}(:,:) = A(:,:);
%Go through the columns and rows
for p = 1:column
for q = 1:row
if(mean2(D{i}(p,q))==0)
x = 0;
else
if(i == 1)
meanvalue = mean2(D{i}(p,q));
end
%Calculate the temporal mean value based on previous ones.
meanvalue = (meanvalue+D{i}(p,q))/2;
x = double(D{i}(p,q)/meanvalue);
end
%Filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
end
end
end
% for k = 2:10
% subplot(5,2,k)
% subimage(Yout(k)*5000, [0 100]);
% colormap jet
% end
% pause(0.01);
end
disp('Done Loading...')
The best way to do this (in my opinion) would be to use a circular-buffer to store your images. In a circular-, or ring-buffer, the oldest data element in the array is overwritten by the newest element pushed in to the array. The basics of making such a structure are described in the short Mathworks video Implementing a simple circular buffer.
For each iteration of you main loop that deals with a single image, just load a new image into the circular-buffer and then use MATLAB's built in mean function to take the average efficiently.
If you need to apply a window function to the data, then make a temporary copy of the frames multiplied by the window function and take the average of the copy at each iteration of the loop.
The line
Yout(k,p,q) = (Yout(k,p,q) + (y.^2))/2;
calculates a kind of Moving Average for each of the 10 bands over all your images.
This line calculates a moving average of meanvalue over your images:
meanvalue=(meanvalue+D{i}(p,q))/2;
For both you will want to add a buffer structure that keeps only the last 10 images.
To simplify it, you can also just keep all in memory. Here is an example for Yout:
Change this line: (Add one dimension)
Yout = zeros(3600,10,column,row);
And change this:
for q = 1:row
[...]
%filtering for 10 bands, based on the previous state
for k = 1:10
[y, ZState{k}] = filter(bCoeff{k},aCoeff{k},x,ZState{k});
Yout(i,k,p,q) = y.^2;
end
YoutAvg = zeros(10,column,row);
start = max(0, i-10+1);
for avgImg = start:i
YoutAvg(k,p,q) = (YoutAvg(k,p,q) + Yout(avgImg,k,p,q))/2;
end
end
Then to display use
subimage(Yout(k)*5000, [0 100]);
You would do sth. similar for meanvalue