2

I would like to loop over following check_matrix in such a way that code recognize whether the first and second element is 1 and 1 or 1 and 2 etc? Then for each separate class of pair i.e. 1,1 or 1,2 or 2,2, the code should store in the new matrices, the sum of last element (which in this case has index 8) times exp(-i*q(check_matrix[k][2:5]-check_matrix[k][5:8])), where i is iota (complex number), k is the running index on check_matrix and q is a vector defined as given below. So there are 20 q vectors.

import numpy as np

q= []
for i in np.linspace(0, 10, 20):
    q.append(np.array((0, 0, i)))
q = np.array(q)

check_matrix = np.array([[1, 1, 0, 0, 0, 0,    0,       -0.7977, -0.243293],
                         [1, 1, 0, 0, 0, 0,    0,       1.5954,  0.004567],
                         [1, 2, 0, 0, 0, -1,   0,       0,       1.126557],
                         [2, 1, 0, 0, 0, 0.5,  0.86603, 1.5954,  0.038934],
                         [2, 1, 0, 0, 0, 2,    0,       -0.7977, -0.015192],
                         [2, 2, 0, 0, 0, -0.5, 0.86603, 1.5954,  0.21394]])

This means in principles I will have to have 20 matrices of shape 2x2, corresponding to each q vector.

For the moment my code is giving only one matrix, which appears to be the last one, even though I am appending in the Matrices. My code looks like below,

for i in range(2):
    i = i+1
    for j in range(2):
        j= j +1
        j_list = []
        Matrices = []
        for k in range(len(check_matrix)):
            if check_matrix[k][0] == i and check_matrix[k][1] == j:
                j_list.append(check_matrix[k][8]*np.exp(-1J*np.dot(q,(np.subtract(check_matrix[k][2:5],check_matrix[k][5:8])))))
                j_11 = np.sum(j_list)
                I_matrix[i-1][j-1] = j_11
                Matrices.append(I_matrix)

I_matrix is defined as below:

I_matrix= np.zeros((2,2),dtype=np.complex_)

At the moment I get following output.

Matrices = [array([[-0.66071446-0.77603624j, -0.29038112+2.34855023j],         [-0.31387562-0.08116629j,  4.2788    +0.j        ]])]

But, I desire to get a matrix corresponding to each q value meaning that in total there should be 20 matrices in this case, where each 2x2 matrix element would be containing sums such that elements belong to 1,1 and 1,2 and 2,2 pairs in following manner

 array([[11., 12.],
       [21., 22.]])

I shall highly appreciate your suggestion to correct it. Thanks in advance!

ClimateUnboxed
  • 7,106
  • 3
  • 41
  • 86
ali
  • 61
  • 2
  • 9
  • How do you end up with the matrix with the first elements being (1, 1) (1, 2) and so on? Maybe if you use smarter indexing / reshaping you do not have to retrieve the information in such a convoluted way! And `q_1`, `df_matrix` and `I_matrix` are missing, please provide all the information to run your code. – scleronomic Sep 13 '20 at 13:47
  • Thanks for your kind response. I made small changes and these are that there exits only `q`, but not `q_1`. There is no `df_matrix`. And `I_matrix` is defined now. – ali Sep 13 '20 at 15:59
  • Still I wonder how you obtain `check_matrix` and what it is you calculate here, it seems unnecessary complicated to me. – scleronomic Sep 13 '20 at 19:21
  • Well, check matrix is a small portion of the data, which I obtained from another code. I intend to perform Fourier transformation on my data. – ali Sep 14 '20 at 18:31
  • did you try the 3-liner I suggested? – ClimateUnboxed Oct 26 '20 at 08:14
  • unfortunately not yet, let's see if I would come back to improve my code further. I shall comment here after using 3 liner for sure. – ali Oct 26 '20 at 18:23

2 Answers2

2

I am pretty sure you can solve this problem in an easier way and I am not 100% sure that I understood you correctly, but here is some code that does what I think you want. If you have a possibility to check if the results are valid, I would suggest you do so.

import numpy as np

n = 20
q = np.zeros((20, 3))
q[:, -1] = np.linspace(0, 10, n)

check_matrix = np.array([[1, 1, 0, 0, 0, 0,    0,       -0.7977, -0.243293],
                         [1, 1, 0, 0, 0, 0,    0,       1.5954,  0.004567],
                         [1, 2, 0, 0, 0, -1,   0,       0,       1.126557],
                         [2, 1, 0, 0, 0, 0.5,  0.86603, 1.5954,  0.038934],
                         [2, 1, 0, 0, 0, 2,    0,       -0.7977, -0.015192],
                         [2, 2, 0, 0, 0, -0.5, 0.86603, 1.5954,  0.21394]])
check_matrix[:, :2] -= 1  # python indexing is zero based

matrices = np.zeros((n, 2, 2), dtype=np.complex_)

for i in range(2):
    for j in range(2):
        k_list = []
        for k in range(len(check_matrix)):
            if check_matrix[k][0] == i and check_matrix[k][1] == j:
                k_list.append(check_matrix[k][8] *
                              np.exp(-1J * np.dot(q, check_matrix[k][2:5] 
                                                     - check_matrix[k][5:8])))

        matrices[:, i, j] = np.sum(k_list, axis=0)

NOTE: I changed your indices to have consistent zero-based indexing.


Here is another approach where I replaced the k-loop with a vectored version:

for i in range(2):
    for j in range(2):
        k = np.logical_and(check_matrix[:, 0] == i, check_matrix[:, 1] == j)
        temp = np.dot(check_matrix[k, 2:5] - check_matrix[k, 5:8], q[:, :, np.newaxis])[..., 0]
        temp = check_matrix[k, 8:] * np.exp(-1J * temp)
        matrices[:, i, j] = np.sum(temp, axis=0)

scleronomic
  • 4,392
  • 1
  • 13
  • 43
  • I think these are nice suggestions to store data appropriately, but I wonder how to see 20 matrices according to each `q` value. – ali Sep 13 '20 at 16:01
  • Thank you very much. I tried the first version i.e. without vectorized version, and I achieved the desired result. I shall try the second version later and shall update. – ali Sep 14 '20 at 18:30
  • Sorry, I am new to stackoverflow, so didn't know that for upvoting I must click on that arrow. I thought making it a green tick would have already worked, but thumbs up and thanks again! – ali Sep 14 '20 at 22:33
0

3 line solution

You asked for efficient solution in your original title so how about this solution that avoids nested loops and if statements in a 3 liner, which is thus hopefully faster?

fac=2*(check_matrix[:,0]-1)+(check_matrix[:,1]-1)
grp=np.split(check_matrix[:,8], np.cumsum(np.unique(fac,return_counts=True)[1])[:-1])
[np.sum(x) for x in grp]

output:

[-0.23872600000000002, 1.126557, 0.023742000000000003, 0.21394]

How does it work?

I combine the first two columns into a single index, treating each as "bits" (i.e. base 2)

fac=2*(check_matrix[:,0]-1)+(check_matrix[:,1]-1)

( If you have indexes that exceed 2, you can still use this technique but you will need to use a different base to combine the columns. i.e. if your indices go from 1 to 18, you would need to multiply column 0 by a number equal to or larger than 18 instead of 2. )

So the result of the first line is

array([0., 0., 1., 2., 2., 3.])

Note as well it assumes the data is ordered, that one column changes fastest, if this is not the case you will need an extra step to sort the index and the original check matrix. In your example the data is ordered.

The next step groups the data according to the index, and uses the solution posted here.

np.split(check_matrix[:,8], np.cumsum(np.unique(fac,return_counts=True)[1])[:-1])

[array([-0.243293,  0.004567]), array([1.126557]), array([ 0.038934, -0.015192]), array([0.21394])]

i.e. it outputs the 8th column of check_matrix according to the grouping of fac

then the last line simply sums those... knowing how the first two columns were combined to give the single index allows you to map the result back. Or you could simply add it to check matrix as a 9th column if you wanted.

ClimateUnboxed
  • 7,106
  • 3
  • 41
  • 86