9

Consider a regular matrix that represents nodes numbered as shown in the figure:

NodesAndTriangles

I want to make a list with all the triangles represented in the figure. Which would result in the following 2 dimensional list:[[0,1,4],[1,5,4],[1,2,5],[2,6,5],...,[11,15,14]]

Assuming that the dimensions of the matrix are (NrXNc) ((4X4) in this case), I was able to achieve this result with the following code:

def MakeFaces(Nr,Nc):
    Nfaces=(Nr-1)*(Nc-1)*2
    Faces=np.zeros((Nfaces,3),dtype=np.int32)
    for r in range(Nr-1):
        for c in range(Nc-1):
            fi=(r*(Nc-1)+c)*2
            l1=r*Nc+c
            l2=l1+1
            l3=l1+Nc
            l4=l3+1
            Faces[fi]=[l1,l2,l3]
            Faces[fi+1]=[l2,l4,l3]
    return Faces

However, the double loop operations make this approach quite slow. Is there a way of using numpy in a smart way to do this faster?

2 Answers 2

8

We could play a multi-dimensional game based on slicing and multi-dim assignment that are perfect in NumPy environment on efficiency -

def MakeFacesVectorized1(Nr,Nc):

    out = np.empty((Nr-1,Nc-1,2,3),dtype=int)

    r = np.arange(Nr*Nc).reshape(Nr,Nc)

    out[:,:, 0,0] = r[:-1,:-1]
    out[:,:, 1,0] = r[:-1,1:]
    out[:,:, 0,1] = r[:-1,1:]

    out[:,:, 1,1] = r[1:,1:]
    out[:,:, :,2] = r[1:,:-1,None]

    out.shape =(-1,3)
    return out

Runtime test and verification -

In [226]: Nr,Nc = 100, 100

In [227]: np.allclose(MakeFaces(Nr, Nc), MakeFacesVectorized1(Nr, Nc))
Out[227]: True

In [228]: %timeit MakeFaces(Nr, Nc)
100 loops, best of 3: 11.9 ms per loop

In [229]: %timeit MakeFacesVectorized1(Nr, Nc)
10000 loops, best of 3: 133 µs per loop

In [230]: 11900/133.0
Out[230]: 89.47368421052632

Around 90x speedup for Nr, Nc = 100, 100!

Sign up to request clarification or add additional context in comments.

Comments

3

You can achieve a similar result without any explicit loops if you recast the problem correctly. One way would be to imagine the result as three arrays, each containing one of the vertices: first, second and third. You can then zip up or otherwise convert the arrays into whatever format you like in a fairly inexpensive operation.

You start with the actual matrix. This will make indexing and selecting elements much easier:

m = np.arange(Nr * Nc).reshape(Nr, Nc)

The first array will contain all the 90-degree corners:

c1 = np.concatenate((m[:-1, :-1].ravel(), m[1:, 1:].ravel()))

m[:-1, :-1] are the corners that are at the top, m[1:, 1:] are the corners that are at the bottom.

The second array will contain the corresponding top acute corners:

c2 = np.concatenate((m[:-1, 1:].ravel(), m[:-1, 1:].ravel()))

And the third array will contain the bottom corners:

c2 = np.concatenate((m[1:, :-1].ravel(), m[1:, :-1].ravel()))

You can now get an array like your original one back by zipping:

faces = list(zip(c1, c2, c3))

I am sure that you can find ways to improve this algorithm, but it is a start.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.