|
| 1 | +require 'narray' |
| 2 | +require 'numo/gnuplot' |
| 3 | + |
| 4 | +def generate_hypercube_edges(dimensions) |
| 5 | + num_vertices = 2 ** dimensions |
| 6 | + gray_codes = (0...num_vertices).map { |i| i ^ (i >> 1) } |
| 7 | + edges = [] |
| 8 | + (0...num_vertices).each do |i| |
| 9 | + (0...dimensions).each do |j| |
| 10 | + neighbor = i ^ (1 << j) |
| 11 | + if gray_codes[i] < gray_codes[neighbor] |
| 12 | + edges << [i, neighbor] |
| 13 | + end |
| 14 | + end |
| 15 | + end |
| 16 | + edges |
| 17 | +end |
| 18 | + |
| 19 | +# Define the number of dimensions for the hypercube |
| 20 | +num_dimensions = 5 |
| 21 | + |
| 22 | +# Generate hypercube edges |
| 23 | +edges = generate_hypercube_edges(num_dimensions) |
| 24 | + |
| 25 | +# Generate hypercube vertices |
| 26 | +num_vertices = 2 ** num_dimensions |
| 27 | +vertices = (0...num_vertices).map do |vertex| |
| 28 | + (0...num_dimensions).map { |i| (vertex >> i) & 1 == 1 ? -1 : 1 } |
| 29 | +end |
| 30 | + |
| 31 | +# Define rotation matrices for different dimensions |
| 32 | +angles = (0...(num_dimensions + 1)).map { |i| 2 * Math::PI * i / num_dimensions }[0...-1] |
| 33 | +rotation_matrices = angles.map do |angle| |
| 34 | + [ |
| 35 | + [Math.cos(angle), -Math.sin(angle), 0], |
| 36 | + [Math.sin(angle), Math.cos(angle), 0], |
| 37 | + [0, 0, 1] |
| 38 | + ] |
| 39 | +end |
| 40 | + |
| 41 | +# Apply rotations to the vertices |
| 42 | +projected_vertices = vertices.map { |v| v.take(3) } |
| 43 | +(num_dimensions - 3).times do |i| |
| 44 | + projected_vertices = projected_vertices.map { |v| v.dot(rotation_matrices[i + 3]) } |
| 45 | +end |
| 46 | + |
| 47 | +# Add random offsets to each vertex |
| 48 | +offsets = Numo::DFloat.new(num_vertices, 3).rand(-0.2..0.2) |
| 49 | +projected_vertices = projected_vertices.zip(offsets).map { |v, o| v + o } |
| 50 | + |
| 51 | +# Create a plot |
| 52 | +Numo.gnuplot do |
| 53 | + set view: [45, 45] |
| 54 | + set title: 'Hypercube Projection' |
| 55 | + set xlabel: 'X' |
| 56 | + set ylabel: 'Y' |
| 57 | + set zlabel: 'Z' |
| 58 | + set xrange: -1.5..1.5 |
| 59 | + set yrange: -1.5..1.5 |
| 60 | + set zrange: -1.5..1.5 |
| 61 | + unset :xtics |
| 62 | + unset :ytics |
| 63 | + unset :ztics |
| 64 | + |
| 65 | + # Plot the hypercube edges with modified transparency and linewidth |
| 66 | + edges.each do |edge| |
| 67 | + vertices = edge.map do |vertex| |
| 68 | + (0...num_dimensions).map { |i| (vertex >> i) & 1 == 1 ? -1 : 1 } |
| 69 | + end |
| 70 | + splot [vertices, with: 'lines', lt: 1, lc: 'red', lw: 2, notitle: true] |
| 71 | + end |
| 72 | + |
| 73 | + # Plot projected vertices with labels |
| 74 | + projected_vertices.each_with_index do |v, i| |
| 75 | + label = (0...num_dimensions).map { |j| v[j] > 0 ? '1' : '0' }.join('') |
| 76 | + splot [[v], with: 'points', pt: 7, ps: 2, lc: 'viridis', notitle: true] |
| 77 | + set label: label, at: v[0..1], center: true, font: '"Arial,8"' |
| 78 | + end |
| 79 | + |
| 80 | + # Create illusion lines connecting projected vertices in 3D space |
| 81 | + (0...num_vertices).each do |i| |
| 82 | + ((i + 1)...num_vertices).each do |j| |
| 83 | + splot [[projected_vertices[i], projected_vertices[j]], with: 'lines', lt: 0, lc: 'black', lw: 1, notitle: true] |
| 84 | + end |
| 85 | + end |
| 86 | + |
| 87 | + # Add a color bar for the third dimension |
| 88 | + set :cblabel, 'Third Dimension' |
| 89 | + unset :colorbox |
| 90 | + |
| 91 | + pause mouse: '' |
| 92 | +end |
0 commit comments