buddyd16
buddyd16

Reputation: 3

Numpy Dot product with nested array

trying to come up with a method to perform load combinations and transient load patterning for structural/civil engineering applications.

without patterning it's fairly simple:

list of load results = [[d],[t1],...,[ti]], where [ti] = transient load result as a numpy array = A

list of combos = [[1,0,....,0],[0,1,....,1], [dfi, tf1,.....,tfi]] , where tfi = code load factor for transient load = B

in python this works as numpy.dot(A,B)

so my issue arises where:

`list of load results = [[d],[t1],.....[ti]]`, where [t1] = [[t11]......[t1i]] for i pattern possibilities and [t1i] = numpy array

so I have a nested array within another array and want to multiply by a matrix of load combinations. Is there a way to implement this in one matrix operation, I can come up with a method by looping the pattern possibilities then a dot product with the load combos, but this is computationally expensive. Any thoughts?

Thanks

for an example not considering patterning see: https://github.com/buddyd16/Structural-Engineering/blob/master/Analysis/load_combo_test.py

essential I need a method that gives similar results assuming that for loads = np.array([[D],[Ex],[Ey],[F],[H],[L],[Lr],[R],[S],[Wx],[Wy]]) --> [L],[Lr],[R],[S] are actually nested arrays ie if D = 1x500 array/vector, L, Lr, R, or S could = 100x500 array.

my simple solution is:

combined_pattern = []
for pattern in load_patterns:
    loads = np.array([[D],[Ex],[Ey],[F],[H],[L[pattern]],[Lr[pattern]],[R[pattern]],[S[pattern]],[Wx],[Wy]])
    combined_pattern.append(np.dot(basic_factors, loads))

Simpler Example:

import numpy as np

#Simple

A = np.array([1,0,0])
B = np.array([0,1,0])
C = np.array([0,0,1])

Loads = np.array([A,B,C])

Factors = np.array([[1,1,1],[0.5,0.5,0.5],[0.25,0.25,0.25]])

result = np.dot(Factors, Loads)

# Looking for a faster way to accomplish the below operation   
# this works but will be slow for large data sets
# bi can be up to 1x5000 in size and i can be up to 500

A = np.array([1,0,0])
b1 = np.array([1,0,0])
b2 = np.array([0,1,0])
b3 = np.array([0,0,1])
B = np.array([b1,b2,b3])
C = np.array([0,0,1])

result_list = []

for load in B:
    Loads = np.array([A,load,C])

    Factors = np.array([[1,1,1],[0.5,0.5,0.5],[0.25,0.25,0.25]])

    result = np.dot(Factors, Loads)

    result_list.append(result)

edit: Had Factors and Loads reversed in the np.dot().

Upvotes: 0

Views: 769

Answers (1)

hpaulj
hpaulj

Reputation: 231325

In your simple example, the array shapes are:

In [2]: A.shape
Out[2]: (3,)
In [3]: Loads.shape
Out[3]: (3, 3)
In [4]: Factors.shape
Out[4]: (3, 3)
In [5]: result.shape
Out[5]: (3, 3)

The rule in dot is that the last dimension of Loads pairs with the 2nd to the last of Factors

result = np.dot(Loads,Factors)
(3,3) dot (3,3) => (3,3)    # 3's in common
(m,n) dot (n,l) => (m,l)    # n's in common

In the iteration, A,load and C are all (3,) and Loads is (3,3).

result_list is a list of 3 (3,3) arrays, and np.array(result_list) would be (3,3,3).

Let's make a 3d array of all the Loads:

In [16]: Bloads = np.array([np.array([A,load,C]) for load in B])
In [17]: Bloads.shape
Out[17]: (3, 3, 3)
In [18]: Bloads
Out[18]: 
array([[[1, 0, 0],
        [1, 0, 0],
        [0, 0, 1]],

       [[1, 0, 0],
        [0, 1, 0],
        [0, 0, 1]],

       [[1, 0, 0],
        [0, 0, 1],
        [0, 0, 1]]])

I can easily do a dot of this Bloads and Factors with einsum:

In [19]: np.einsum('lkm,mn->lkn', Bloads, Factors)
Out[19]: 
array([[[1.  , 1.  , 1.  ],
        [1.  , 1.  , 1.  ],
        [0.25, 0.25, 0.25]],

       [[1.  , 1.  , 1.  ],
        [0.5 , 0.5 , 0.5 ],
        [0.25, 0.25, 0.25]],

       [[1.  , 1.  , 1.  ],
        [0.25, 0.25, 0.25],
        [0.25, 0.25, 0.25]]])

einsum isn't the only way, but it's the easiest way (for me) to keep track of dimensions.

It's even easier to keep dimensions straight if they differ. Here they are all 3, so it's hard to keep them separate. But if B was (5,4) and Factors (4,2), then Bloads would be (5,3,4), and the einsum result (5,3,2) (the size 4 dropping out in the dot).

Constructing Bloads without a loop is a bit trickier, since the rows of B are interleaved with A and C.

In [38]: np.stack((A[None,:].repeat(3,0),B,C[None,:].repeat(3,0)),1)
Out[38]: 
array([[[1, 0, 0],
        [1, 0, 0],
        [0, 0, 1]],

       [[1, 0, 0],
        [0, 1, 0],
        [0, 0, 1]],

       [[1, 0, 0],
        [0, 0, 1],
        [0, 0, 1]]])

To understand this test the subexpressions, e.g. A[None,:], the repeat etc.

Equivalently:

np.array((A[None,:].repeat(3,0),B,C[None,:].repeat(3,0))).transpose(1,0,2)

Upvotes: 1

Related Questions