Fortran 90 array subsets - arrays

I am trying to read some Fortran code but there is something I can't understand with array subsets operations like this one
Assume n = 3
And the arrays
INTEGER, PARAMETER :: dp = SELECTED_REAL_KIND(12)
REAL(KIND=dp) :: P(n+1),P0(n)
what does this line exactly do?
DO i=1,n-1
…..
P(3:i+2) = P(3:i+2) - i*P0(1:i) / (i+1)
….
END DO
Is it a nested loop? Like j from 3 to i+2 for P and k from 1 to i for P0?
Thanks in advance.

Take the line
P(3:i+2) = P(3:i+2) - i*P0(1:i) / (i+1)
and replace i with 1 (the first value it takes in the do loop)
P(3:3) = P(3:3) - 1*P0(1:1) / 2
On the lhs you have a slice (or section) of array P from element 3 to element 3, so in this case just one element -- but still an array slice not a scalar. This is updated by subtracting 1 times the (same sized) slice of array P0 and divided by 2.
It's a bit more interesting in the next iteration, with i==2 and
P(3:4) = P(3:4) - 2*P0(1:2) / 3
where the array slices are now 2 elements each. The operations on array slices are applied on corresponding elements from each array so this statement is approximately equivalent to the two statements
P(3) = P(3) - 2*P0(1) / 3
P(4) = P(4) - 2*P0(2) / 3
It's better to think of this in Fortran terms, as operations on array sections, than as some kind of syntactic sugar for nested loops.

Related

Julia / Cellular Automata: efficient way to get neighborhood

I'd like to implement a cellular automaton (CA) in Julia. Dimensions should be wrapped, this means: the left neighbor of the leftmost cell is the rightmost cell etc.
One crucial question is: how to get the neighbors of one cell to compute it's state in the next generation? As dimensions should be wrapped and Julia does not allow negative indices (as in Python) i had this idea:
Considered a 1D CA, one generation is a one-dimensional array:
0 0 1 0 0
What if we create a two dimensional Array, where the first row is shifted right and the third is shifted left, like this:
0 0 0 1 0
0 0 1 0 0
0 1 0 0 0
Now, the first column contain the states of the first cell and it's neighbors etc.
i think this can easily be generalized for two and more dimensions.
First question: do you think this is a good idea, or is this a wrong track?
EDIT: Answer to first question was no, second Question and code example discarded.
Second question: If the approach is basically ok, please have a look at the following sketch:
EDIT: Other approach, here is a stripped down version of a 1D CA, using mod1() for getting neighborhood-indices, as Bogumił Kamiński suggested.
for any cell:
- A array of all indices
- B array of all neighborhood states
- C states converted to one integer
- D lookup next state
function digits2int(digits, base=10)
int = 0
for digit in digits
int = int * base + digit
end
return int
end
gen = [0,0,0,0,0,1,0,0,0,0,0]
rule = [0,1,1,1,1,0,0,0]
function nextgen(gen, rule)
values = [mod1.(x .+ [-1,0,1], size(gen)) for x in 1:length(gen)] # A
values = [gen[value] for value in values] # B
values = [digits2int(value, 2) for value in values] # C
values = [rule[value+1] for value in values] # D
return values
end
for _ in 1:100
global gen
println(gen)
gen = nextgen(gen, rule)
end
Next step should be to extend it to two dimensions, will try it now...
The way I typically do it is to use mod1 function for wrapped indexing.
In this approach, no matter what dimensionality of your array a is then when you want to move from position x by delta dx it is enough to write mod1(x+dx, size(a, 1)) if x is the first dimension of an array.
Here is a simple example of a random walk on a 2D torus counting the number of times a given cell was visited (here I additionally use broadcasting to handle all dimensions in one expression):
function randomwalk()
a = zeros(Int, 8, 8)
pos = (1,1)
for _ in 1:10^6
# Von Neumann neighborhood
dpos = rand(((1,0), (-1,0), (0,1), (0,-1)))
pos = mod1.(pos .+ dpos, size(a))
a[pos...] += 1
end
a
end
Usually, if the CA has cells that are only dependent on the cells next to them, it's simpler just to "wrap" the vector by adding the last element to the front and the first element to the back, doing the simulation, and then "unwrap" by taking the first and last elements away again to get the result length the same as the starting array length. For the 1-D case:
const lines = 10
const start = ".........#........."
const rules = [90, 30, 14]
rule2poss(rule) = [rule & (1 << (i - 1)) != 0 for i in 1:8]
cells2bools(cells) = [cells[i] == '#' for i in 1:length(cells)]
bools2cells(bset) = prod([bset[i] ? "#" : "." for i in 1:length(bset)])
function transform(bset, ruleposs)
newbset = map(x->ruleposs[x],
[bset[i + 1] * 4 + bset[i] * 2 + bset[i - 1] + 1
for i in 2:length(bset)-1])
vcat(newbset[end], newbset, newbset[1])
end
const startset = cells2bools(start)
for rul in rules
println("\nUsing Rule $rul:")
bset = vcat(startset[end], startset, startset[1]) # wrap ends
rp = rule2poss(rul)
for _ in 1:lines
println(bools2cells(bset[2:end-1])) # unwrap ends
bset = transform(bset, rp)
end
end
As long as only the adjacent cells are used in the simulation for any given cell, this is correct.
If you extend this to a 2D matrix, you would also "wrap" the first and last rows as well as the first and last columns, and so forth.

Filling a row and columns of a ndarray with a loop

I'm starting with Python and I have a basic question with "for" loop
I have two array which contains a values of a same variables:
A = data_lac[:,0]
In the first array, I have values of area and in the second on, values of mean depth.
I would like to find a way to automatize my calculation with different value of a parameter. The equation is the following one:
g= (np.sqrt(A/pi))/n
Here I can calculte my "g" for each row. Now I want to have a loop with differents values of "n". I did this:
i=0
while i <= len(A)-1:
for n in range(2,6):
g[i] = (np.sqrt(A[i]/pi))/n
i += 1
break
In this case, I just have one column with the calculation for n = 2 but not the following one. I tried to add a second dimension to my array but I have an error message saying that I have too many indices for array.
In other, I would like this array:
g[len(A),5]
g has 5 columns each one calculating with a different "n"
Any tips would be very helpful,
Thanks
Update of the code:
data_lac=np.zeros((106,7))
data_lac[:,0:2]=np.loadtxt("/home...", delimiter=';', skiprows=1, usecols=(0,1))
data_lac[:,1]=data_lac[:,1]*0.001
#Initialisation
A = data_lac[:,0]
#example for A with 4 elements
A=[2.1, 32.0, 4.6, 25]
g = np.zeros((len(A),))
I believe you share the indexes within both loops. You were increasing the i (index for the upper while loop) inside the inner for loop (which index with n).
I guess you have A (1 dim array) and you want to produce G (2 dim array) with size of (Len(A, 5))
I am not sure I'm fully understand your require output but I believe you want something like:
i=0
while i <= len(A)-1:
for n in range(2,6):
g[i][n-2] = (np.sqrt(A[i]/pi))/n # n-2 is to get first index as 0 and last as 4
i += 1 # notice the increace of the i is for the upper while loop
break
Important - remember that in python indentation means a lot -> so make sure the i +=1 is under the while scope and not indent to be inside the for loop
Notice - G definition should be as:
g = np.zeros((len(A),4), dtype=float)
The way you define it (without the 4) cause it to be 1 dim array and not 2-dim

Substitute a vector value with two values in MATLAB

I have to create a function that takes as input a vector v and three scalars a, b and c. The function replaces every element of v that is equal to a with a two element array [b,c].
For example, given v = [1,2,3,4] and a = 2, b = 5, c = 5, the output would be:
out = [1,5,5,3,4]
My first attempt was to try this:
v = [1,2,3,4];
v(2) = [5,5];
However, I get an error, so I do not understand how to put two values in the place of one in a vector, i.e. shift all the following values one position to the right so that the new two values fit in the vector and, therefore, the size of the vector will increase in one. In addition, if there are several values of a that exist in v, I'm not sure how to replace them all at once.
How can I do this in MATLAB?
Here's a solution using cell arrays:
% remember the indices where a occurs
ind = (v == a);
% split array such that each element of a cell array contains one element
v = mat2cell(v, 1, ones(1, numel(v)));
% replace appropriate cells with two-element array
v(ind) = {[b c]};
% concatenate
v = cell2mat(v);
Like rayryeng's solution, it can replace multiple occurrences of a.
The problem mentioned by siliconwafer, that the array changes size, is here solved by intermediately keeping the partial arrays in cells of a cell array. Converting back to an array concenates these parts.
Something I would do is to first find the values of v that are equal to a which we will call ind. Then, create a new output vector that has the output size equal to numel(v) + numel(ind), as we are replacing each value of a that is in v with an additional value, then use indexing to place our new values in.
Assuming that you have created a row vector v, do the following:
%// Find all locations that are equal to a
ind = find(v == a);
%// Allocate output vector
out = zeros(1, numel(v) + numel(ind));
%// Determine locations in output vector that we need to
%// modify to place the value b in
indx = ind + (0:numel(ind)-1);
%// Determine locations in output vector that we need to
%// modify to place the value c in
indy = indx + 1;
%// Place values of b and c into the output
out(indx) = b;
out(indy) = c;
%// Get the rest of the values in v that are not equal to a
%// and place them in their corresponding spots.
rest = true(1,numel(out));
rest([indx,indy]) = false;
out(rest) = v(v ~= a);
The indx and indy statements are rather tricky, but certainly not hard to understand. For each index in v that is equal to a, what happens is that we need to shift the vector over by 1 for each index / location of v that is equal to a. The first value requires that we shift the vector over to the right by 1, then the next value requires that we shift to the right by 1 with respect to the previous shift, which means that we actually need to take the second index and shift by the right by 2 as this is with respect to the original index.
The next value requires that we shift to the right by 1 with respect to the second shift, or shifting to the right by 3 with respect to the original index and so on. These shifts define where we're going to place b. To place c, we simply take the indices generated for placing b and move them over to the right by 1.
What's left is to populate the output vector with those values that are not equal to a. We simply define a logical mask where the indices used to populate the output array have their locations set to false while the rest are set to true. We use this to index into the output and find those locations that are not equal to a to complete the assignment.
Example:
v = [1,2,3,4,5,4,4,5];
a = 4;
b = 10;
c = 11;
Using the above code, we get:
out =
1 2 3 10 11 5 10 11 10 11 5
This successfully replaces every value that is 4 in v with the tuple of [10,11].
I think that strrep deserves a mention here.
Although it's called string replacement and warns for non-char input, it still works perfectly fine for other numbers as well (including integers, doubles and even complex numbers).
v = [1,2,3,4]
a = 2, b = 5, c = 5
out = strrep(v, a, [b c])
Warning: Inputs must be character arrays or cell arrays of strings.
out =
1 5 5 3 4
You are not attempting to overwrite an existing value in the vector. You're attempting to change the size of the vector (meaning the number of rows or columns in the vector) because you're adding an element. This will always result in the vector being reallocated in memory.
Create a new vector, using the first and last half of v.
Let's say your index is stored in the variable index.
index = 2;
newValues = [5, 5];
x = [ v(1:index), newValues, v(index+1:end) ]
x =
1 2 5 5 3 4

Assigning array elements based on elements in another array

First time post here, and I had a (likely very simple) question.
I wanted to assign the values of elements in one array (A) based on the corresponding values in another array(B), eg if A(1)=2 assign 4 to B(1).
I imagined this would be achieved using DO loops and some if statements. However the If staments I'm using seem to refering to the loop index rather than the specific element at that point.
Can anyone point me in the right direction ?
You could also try a construct with merge... merge constructs a new array from two existing arrays using a mask to choose the correct value:
program test
integer,parameter :: LENGTH=5
integer :: A(LENGTH)
integer :: B(LENGTH)
real :: R(LENGTH)
integer :: i
call random_number(R)
A = int( R*3 )
B = [ ( i,i=1,LENGTH) ]
print *,'A:',A
print *,'B:',B
B = merge( 4, B, A == 2 )
print *,'B:',B
end program
Output:
A: 2 1 2 2 1
B: 1 2 3 4 5
B: 4 2 4 4 5
Explanation:
B = merge( 4, B, A == 2 )
A == 2 constructs a temporary logical array which is .true. at i if A(i) == 2
4 in this case is a temporary array with the same length as B
So, merge choses the value from 4 if the temporary logical array is .true., and the correspond value from B, otherwise.
The resulting vector is written back to B (=)
You might be able to use masked array assignment. Given two arrays, A and B, of the same shape (ie same number of ranks, same size in each dimension), and if you wanted to set elements of B to 4 where the corresponding element of A equals 2 you could simply write
where(A==2) B = 4
where teams up with elsewhere, rather like if and else if, and there is an end where too. Consult your favourite documentation for further details.
If you can't express your operation with where you might (if you have an up to date compiler) be interested in the do concurrent construct. And, if all else fails, there are good old-fashioned do and if to fall back on.

Im looking for a way to add up elements of two arrays

i am looking for a way that i can add up elements in an array such that the first element of the first array is added to every element in the second array, then the second element in the first array is added to all every element in the second array and so on. The final vector will be length(a)*length(b) long
for example...
a=[1,2,3,4] b=[5,6,7]
answer =
[(1+5),(1+6),(1+7),(2+5),(2+6),(2+7),(3+5),(3+6),(3+7),(4+5),(4+6),(4+7)]
=[6,7,8,7,8,9,8,9,10,9,10,11]
Read up on bsxfun. It's very useful for this kind of things (and usually faster than arrayfun or for loops):
result = bsxfun(#plus, a(:).', b(:)); %'// matrix of size numel(b) x numel(a)
result = result(:).'; %'// linearize to a vector
Or, a little more freak: kron does what you want with products instead of sums. So:
result = log(kron(exp(a),exp(b)));
My first thought is to do this with arrayfun using an anonymous function that adds each scalar element of a to the full array in b. Then since you get a cell array result you can expand that cell array into the array you are looking for:
>> a=[1,2,3,4], b=[5,6,7]
>> result = arrayfun(#(x) x+b, a,'UniformOutput',false);
>> result = [result{:}]
result =
6 7 8 7 8 9 8 9 10 9 10 11
Use meshgrid to create matrices of a and b and use matrix addition to compute a+b
a=[1,2,3,4], b=[5,6,7]
[A_matrix,B_matrix] = meshgrid(a,b)
result = A_matrix + B_matrix
result = result(:)'

Resources