M.E. Irizarry-Gelpí

Physics impostor. Mathematics interloper. Husband. Father.

The Triangle Product with NumPy


In this post I will discuss an implementation of the triangle product for 3-arrays in NumPy. You start by importing and setting the seed of the random number generator:

from itertools import product
import numpy as np
from numpy import random

random.seed(0)

A 3-array with random integer entries between 0 and 9, and size \(\left(2, 3, 4\right)\) can be obtain with random.random_integers(0, 9, (2, 3, 4)):

[[[5 0 3 3]
  [7 9 3 5]
  [2 4 7 6]]

 [[8 8 1 6]
  [7 7 8 1]
  [5 9 8 9]]]

Given three 3-arrays A, B, and C, the triangle product can be implemented by the function mul_3:

def mul_3(A, B, C):
    a, p, q = A.shape
    p, b, r = B.shape
    q, r, c = C.shape
    D = np.zeros((a, b, c))
    for (i, j, k) in product(xrange(a), xrange(b), xrange(c)):
        P = np.zeros((p, q, r))
        for (x, y, z) in product(xrange(p), xrange(q), xrange(r)):
            P[x, y, z] = A[i, x, y] * B[x, j, z] * C[y, z, k]
        D[i, j, k] = P.sum()
    return D

This is a very yanky implementation, since it implicitly assumes that the size of the three input arrays are such that the triangle product exists. For example:

a = 2; b = 2; c = 2;
p = 3; q = 4; r = 5;

A = random.random_integers(0, 9, (a, p, q))
B = random.random_integers(0, 9, (p, b, r))
C = random.random_integers(0, 9, (q, r, c))

D = mul_3(A, B, C)

Here A has size \(\left(2, 3, 4\right)\):

[[[4 3 0 3]
  [5 0 2 3]
  [8 1 3 3]]

 [[3 7 0 1]
  [9 9 0 4]
  [7 3 2 7]]]

B has size \(\left(3, 2, 5\right)\):

[[[2 0 0 4 5]
  [5 6 8 4 1]]

 [[4 9 8 1 1]
  [7 9 9 3 6]]

 [[7 2 0 3 5]
  [9 4 4 6 4]]]

C has size \(\left(4, 5, 2\right)\):

[[[4 3]
  [4 4]
  [8 4]
  [3 7]
  [5 5]]

 [[0 1]
  [5 9]
  [3 0]
  [5 0]
  [1 2]]

 [[4 2]
  [0 3]
  [2 0]
  [7 5]
  [9 0]]

 [[2 7]
  [2 9]
  [2 3]
  [3 2]
  [3 4]]]

and D has size \(\left(2, 2, 2\right)\):

[[[ 2206.  2374.]
  [ 3832.  3985.]]

 [[ 3386.  3821.]
  [ 5417.  5907.]]]

Now lets consider three cubic 3-arrays with the same size:

n = 3

X = random.random_integers(0, 9, (n, n, n))
Y = random.random_integers(0, 9, (n, n, n))
Z = random.random_integers(0, 9, (n, n, n))

You can compute six triangle products corresponding to the six possible permutations of the inputs:

$$ \operatorname{mul}_{3}\left(\mathbf{X}, \mathbf{Y}, \mathbf{Z} \right), \qquad \operatorname{mul}_{3}\left(\mathbf{Y}, \mathbf{Z}, \mathbf{X} \right), \qquad \operatorname{mul}_{3}\left(\mathbf{Z}, \mathbf{X}, \mathbf{Y} \right), $$
$$ \operatorname{mul}_{3}\left(\mathbf{Z}, \mathbf{Y}, \mathbf{X} \right), \qquad \operatorname{mul}_{3}\left(\mathbf{Y}, \mathbf{X}, \mathbf{Z} \right), \qquad \operatorname{mul}_{3}\left(\mathbf{X}, \mathbf{Z}, \mathbf{Y} \right). $$

Each of these products yields a different 3-array. Explicitly, mul_3(X, Y, Z) gives:

[[[ 2917.  3313.  1870.]
  [ 2174.  2617.  1412.]
  [ 3773.  4375.  2698.]]

 [[ 2658.  2780.  1955.]
  [ 2173.  2202.  1575.]
  [ 3498.  3706.  2647.]]

 [[ 3017.  4176.  1873.]
  [ 2775.  3216.  1796.]
  [ 4315.  5436.  2937.]]]

mul_3(Y, Z, X) gives:

[[[ 2268.  2044.  4254.]
  [ 2243.  1942.  3578.]
  [ 1716.  1550.  3384.]]

 [[ 2904.  2824.  5646.]
  [ 2578.  1982.  4482.]
  [ 1880.  2172.  4745.]]

 [[ 3205.  1942.  4949.]
  [ 2626.  1557.  3865.]
  [ 2362.  1574.  3979.]]]

mul_3(Z, X, Y) gives:

[[[ 3406.  2945.  3785.]
  [ 2895.  3055.  3398.]
  [ 4697.  4184.  5530.]]

 [[ 2469.  2436.  3299.]
  [ 2165.  2311.  2924.]
  [ 3114.  2966.  4680.]]

 [[ 2491.  2186.  2813.]
  [ 2238.  2178.  2546.]
  [ 3777.  3448.  4690.]]]

mul_3(Z, Y, X) gives:

[[[ 2826.  2768.  4890.]
  [ 2035.  2209.  3423.]
  [ 3515.  3538.  6302.]]

 [[ 2528.  1922.  3687.]
  [ 1987.  1414.  2816.]
  [ 3282.  2555.  5036.]]

 [[ 2446.  2282.  3737.]
  [ 1475.  1930.  3222.]
  [ 2569.  2898.  5009.]]]

mul_3(Y, X, Z) gives:

[[[ 2481.  2799.  1741.]
  [ 2148.  2367.  1561.]
  [ 3692.  4362.  2781.]]

 [[ 3121.  3833.  2235.]
  [ 2612.  3306.  1817.]
  [ 4718.  5600.  3897.]]

 [[ 2579.  3197.  1738.]
  [ 2491.  2674.  1571.]
  [ 4290.  4765.  2884.]]]

mul_3(X, Z, Y) gives:

[[[ 2678.  2724.  4463.]
  [ 1895.  2292.  3367.]
  [ 1890.  2000.  3541.]]

 [[ 2439.  2352.  3538.]
  [ 2421.  1986.  3397.]
  [ 2128.  2008.  3253.]]

 [[ 2381.  3040.  4967.]
  [ 2234.  2564.  4066.]
  [ 1952.  2343.  3900.]]]

Thus, the 2-array notion of non-commutativity carries over to 3-arrays with the triangle product.