'Visualizing the permutohedron in 3D plot

I am trying to plot the Permutohedron in python using plotly, numpy, and pandas.

This is my current code:

import plotly.express as px
import numpy as np
import itertools
import pandas as pd

order = 4
items = range(1, order+1)
permuted_items = np.array([*itertools.permutations(items)])

def closest_nodes(node, nodes):
      # Returns the instances in nodes that are closest to node
      nodes = np.asarray(nodes)
      dist_2 = np.sum((nodes - node)**2, axis=1)**.5
      indices = np.where(dist_2 == dist_2.min())[0]

      return nodes[indices]

xyzs = []
colors = []

for i, point in enumerate(permuted_items[:-1]):
      closest_points = closest_nodes(point, permuted_items[i+1:])
      for c_point in closest_points:
            xyzs.extend([point[:3], c_point[:3]])

            # Get unique string as color to group above line while plotting
            c = str(point) + str(c_point[:3])
            colors.extend([c, c])

lines = np.array(xyzs)
x, y, z = lines.T
plotting_data = pd.DataFrame({
      "X": x,
      "Y": y,
      "Z": z,
      "color": colors
})

fig = px.line_3d(plotting_data, x='X', y='Y', z='Z', color="color")
fig.show()

But this plots something that is very skewed:

enter image description here

I.e. my way of showing this shape in 3d (by removing the last dimension) changes the length of each line such that each line does not have a length of sqrt(2).

In reality, this is the shape I am after:

enter image description here

Any help?



Solution 1:[1]

In this case, you need to project your points onto a 3D space, instead of omitting the forth coordinate. The 3x4 matrix transformation is provided here

https://blogs.mathworks.com/graphics/2016/01/29/tiling-hexagons-and-other-permutohedra/

Working code:

import plotly.express as px
import numpy as np
import itertools
import pandas as pd

order = 4
items = range(1, order+1)
permuted_items = np.array([*itertools.permutations(items)])


#Project the points onto 3D space--
A = np.array([[np.sqrt(2)/2, -np.sqrt(2)/2, 0, 0],\
     [np.sqrt(6)/6, np.sqrt(6)/6, -np.sqrt(2/3), 0],\
     [np.sqrt(12)/12, np.sqrt(12)/12, np.sqrt(12)/12, -np.sqrt(3)/2]])

permuted_items = np.einsum('ik,ak->ai',A,permuted_items)
#----------------------------------

xyzs = []
colors = []

for i, point in enumerate(permuted_items):

      d = np.linalg.norm(permuted_items-point[np.newaxis,:],axis=1)

      #Inspired by https://stackoverflow.com/questions/31352486/can-numpy-argsort-handle-ties 
      js = (abs(d - d[d.argsort()[1]])<1e-3).nonzero()[0]

      for j in js:
          xyzs.extend([point,permuted_items[j]])

          # Get unique string as color to group above line while plotting
          c = str(point) + str(permuted_items[j])
          colors.extend([c, c])


lines = np.array(xyzs)
x, y, z = lines.T

plotting_data = pd.DataFrame({
      "X": x,
      "Y": y,
      "Z": z,
      "color": colors,
})

fig = px.line_3d(plotting_data, x='X', y='Y', z='Z', color="color")
fig.show()
           

Note that I have rewritten the part on computing the smallest distance.

plotly graph

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 romanodev