Like Jonathan Allan, I wrote a program to search for solved states. I'll present it in Mathematica/Wolfram Language, but I'll also provide a Python script that works the same way.
Encoding
Here is the pyramidix pyramatrix pyramadix triangular Rubik's cube I started with:

The corners are labeled A
-D
and the faces are labeled 1
-4
.
The tip pieces (cyan) and vertex pieces (yellow) are encoded the same way, as a list of the three sides in order of face number. For example, tip piece C
is encoded as {6, 4, 5}
, corresponding to the sides on faces 2
, 3
, and 4
. In the following lists the tips/vertices are encoded in order from A
to D
:
tips = {{1, 4, 9}, {5, 1, 6}, {6, 4, 5}, {7, 2, 4}};
vertices = {{8, 3, 5}, {2, 9, 8}, {5, 3, 7}, {3, 8, 9}};
I also encode a list of which face each side is on:
cornerFaces = {{1, 2, 3}, {1, 2, 4}, {2, 3, 4}, {1, 3, 4}};
I use a similar encoding for the edge (magenta) pieces:
edges = {{4, 7}, {9, 1}, {6, 3}, {2, 6}, {8, 2}, {7, 1}};
edgeFaces = {{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};
Now I define a toFaces
function that takes a piece list (tips
, vertices
, or edges
) and a face list (cornerFaces
or edgeFaces
) and returns a list of the numbers that show on each face:
toFaces[faceList_][pieces_] := Table[Flatten[Pick[pieces, faceList, i]], {i, 4}]
In words: for i
from 1
to 4
, pick the elements from pieces
where faceList
is equal to i
, and flatten the result into a list.
For example, the tip numbers by face in the starting configuration:
toFaces[cornerFaces][tips] === {{1, 5, 7}, {4, 1, 6}, {9, 4, 2}, {6, 5, 4}}
Algorithm
There are two useful facts about this problem:
- We can eliminate a state as soon as we find two of the same number on any face.
- The tips, vertices, and edges can be positioned independently of each other.
We can drastically speed up our search by precomputing the 81 tip states, 81 vertex states, and 11,520 edge states—then discarding those that have duplicate numbers, and therefore cannot be part of the solution.
For the corner pieces, this is relatively simple. For each piece, I use Partition
to generate the possible rotations, e.g.:
Partition[{a, b, c}, 3, 1, 1] (* === {{a, b, c}, {b, c, a}, {c, a, b}} *)
Then I use Tuples
to generate all combinations of those rotations; get the face lists for each state with toFaces
; and finally use Select
to only take the states for which all faces are free of duplicates:
tipStates = toFaces[cornerFaces] /@ Tuples[Partition[#, 3, 1, 1] & /@ tips] //
Select[AllTrue[DuplicateFreeQ]]
(The computation of vertexStates
is identical.) The computation of the edge states is a bit more complex. First, note that the initial orientation of the edge pieces doesn't matter—that is, it doesn't matter which orientation you call "flipped" and which one you call "unflipped." To prove this, imagine reversing the "unflipped" position of an edge X
(but don't change the physical edge position). Then when you swap it with another edge Y
, edge Y
will be flipped into the opposite (physical) orientation; but, since the label on X
was flipped as well (e.g. it changed from "flipped" to "unflipped" when we changed the label) it will be put into the opposite orientation as well. Since this introduces an even number of edge flips, it doesn't produce any illegal states.
Second, note that it doesn't matter whether we do the edge flips first or the edge permutations first. We will still search through all the states, just possibly in a different order.
I start off computing the legal edge flips and permutations. For the flips, I assign a 0
or 1
to each edge (0
for unflipped, and 1
for flipped). Then, I take all the flips for which the sum (total number of flips) is even:
edgeFlips = Tuples[{0, 1}, 6] // Select[Total /* EvenQ];
Then I apply these flips to the edges:
edgeOrientations =
Table[MapThread[If[#2 == 1, Reverse@#, #] &, {edges, f}], {f, edgeFlips}];
Next is the permutations. I generate all the permutations of 6 elements, then select those whose signature is positive (i.e. the permutation is even):
edgePermutations = Permutations[Range[6]] // Select[Signature /* Positive];
Finally I combine these two into the possible edge states, applying toFaces
and selecting those that are free of duplicates:
edgeStates = toFaces[edgeFaces] /@
Flatten[Outer[Permute, edgeOrientations, edgePermutations, 1], 1] //
Select[AllTrue[DuplicateFreeQ]];
If we look at the length of each list of selected states:
Length /@ {tipStates, vertexStates, edgeStates} (* === {19, 9, 4908} *)
Times @@ % (* === 839268 *)
We can see that the number of states we have to search has been tremendously reduced, from over 75 million to under 1 million.
We can further reduce the search space by only taking valid corner states:
cornerStates = Flatten[Tuples[{tipStates, vertexStates}], {{1}, {3}, {2, 4}}] //
Select[AllTrue[DuplicateFreeQ]]
Length /@ {cornerStates, edgeStates} (* === {12, 4908} *)
Times @@ % (* === 58896 *)
Thus we've reduced the final search space by about 1200 times. Altogether, the code is:
tips = {{1, 4, 9}, {5, 1, 6}, {6, 4, 5}, {7, 2, 4}};
vertices = {{8, 3, 5}, {2, 9, 8}, {5, 3, 7}, {3, 8, 9}};
cornerFaces = {{1, 2, 3}, {1, 2, 4}, {2, 3, 4}, {1, 3, 4}};
edges = {{4, 7}, {9, 1}, {6, 3}, {2, 6}, {8, 2}, {7, 1}};
edgeFaces = {{1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}};
toFaces[faceList_][pieces_] :=
Table[Flatten[Pick[pieces, faceList, i]], {i, 4}]
tipStates =
toFaces[cornerFaces] /@ Tuples[Partition[#, 3, 1, 1] & /@ tips] //
Select[AllTrue[DuplicateFreeQ]];
vertexStates =
toFaces[cornerFaces] /@ Tuples[Partition[#, 3, 1, 1] & /@ vertices] //
Select[AllTrue[DuplicateFreeQ]];
edgeFlips = Tuples[{0, 1}, 6] // Select[Total /* EvenQ];
edgeOrientations =
Table[MapThread[If[#2 == 1, Reverse@#, #] &, {edges, f}], {f,
edgeFlips}];
edgePermutations =
Permutations[Range[6]] // Select[Signature /* Positive];
edgeStates =
toFaces[edgeFaces] /@
Flatten[Outer[Permute, edgeOrientations, edgePermutations, 1],
1] // Select[AllTrue[DuplicateFreeQ]];
cornerStates =
Flatten[Tuples[{tipStates, vertexStates}], {{1}, {3}, {2, 4}}] //
Select[AllTrue[DuplicateFreeQ]];
states = Flatten[
Tuples[{cornerStates, edgeStates}], {{1}, {3}, {2, 4}}] //
Select[AllTrue[DuplicateFreeQ]];
Results
The code runs very quickly (just under half a second on my computer) and finds 8 solved states for this pyraminx. They are:
{
{{1,5,7,8,2,3,4,9,6},{4,1,6,3,9,5,7,2,8},{9,4,2,5,3,8,1,6,7},{6,5,4,8,7,9,3,2,1}},
{{1,5,7,8,9,3,6,4,2},{4,1,6,3,8,5,2,9,7},{9,4,2,5,3,8,7,1,6},{6,5,4,2,7,9,8,1,3}},
{{1,5,7,3,8,9,6,2,4},{4,1,6,5,2,7,3,9,8},{9,4,2,8,5,3,6,1,7},{6,5,4,9,3,8,7,2,1}},
{{1,5,4,8,2,3,6,9,7},{4,1,6,3,9,5,2,8,7},{9,4,7,5,3,8,1,2,6},{6,5,2,8,7,9,4,1,3}},
{{1,5,4,8,2,3,6,7,9},{4,1,6,3,9,5,2,8,7},{9,4,7,5,3,8,1,2,6},{6,5,2,8,7,9,1,4,3}},
{{1,6,7,5,2,3,9,4,8},{4,5,6,8,9,7,1,2,3},{9,4,2,3,5,8,7,6,1},{1,5,4,8,3,9,2,6,7}},
{{4,5,7,3,8,9,2,1,6},{9,1,6,5,2,7,8,3,4},{1,4,2,8,5,3,9,6,7},{6,5,4,9,3,8,2,7,1}},
{{9,6,7,5,2,3,8,1,4},{1,5,4,8,9,3,2,7,6},{4,5,2,3,7,8,9,1,6},{1,6,4,8,5,9,7,2,3}}
}
I haven't included any code to "pretty-print" the output, but you can still read off the state from the output. For example, for the first solved state (the original) the output for face 1
is {1, 5, 7, 8, 2, 3, 4, 9, 6}
. Because of the way the states were constructed, the vertices are first, ordered by which corner they are in (in this case 1
, 5
, and 7
, corresponding to corners A
, B
, and D
). Next are the vertices in the same order (8
, 2
, and 3
). Finally, the three edges follow, ordered by the number of the face they connect to (4
, 9
, and 6
, which touch faces 2
, 3
, and 4
).
Here is a translation of this code to a Python script (with pretty-printed output). This is one of the eight solutions it finds (equivalent to the last solution from my Mathematica script).
D----------------- A -----------------D
\ 7 / \ 1 / \ 9 // \\ 4 / \ 9 / \ 2 /
\ / 3 \ / 5 \ // 1 \\ / 3 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 4 / \ 8 // \ 8 / \\ 1 / \ 6 /
\ / 2 \ // 2 \ / 7 \\ / 7 \ /
\-----//----(2)----\\-----/
\ 6 // \ 9 / \ 3 / \\ 5 /
\ // 5 \ / 6 \ / 4 \\ /
B ----------------- C
B-----------------C
\ 1 / \ 2 / \ 6 /
\ / 8 \ / 5 \ /
\----(4)----/
\ 7 / \ 3 /
\ / 9 \ /
\-----/
\ 4 /
\ /
D
Extension
The python code is pretty easily adapted to search for solutions that also have unique numbers in the 'rings' around each vertex. Since the constraints on the rings are the same as the constraints on each face (must have 1
-9
with no repeats), we can just add the rings as four more faces.
Here is a modified version of the code that searches through all possible arrangements for the bottom face to find one with a unique solution with the "rings constraint." It finds six different uniquely solvable bottom face arrangements:
found 1 solutions
searched 230,747/362,880 states (63.6%) in 2:18:27.4 (avg. 36.0 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 6 / \ 1 / \ 7 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 8 /
\ /
D
found 2 solutions
searched 235,787/362,880 states (65.0%) in 2:21:47.8 (avg. 36.1 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 6 / \ 1 / \ 8 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 7 /
\ /
D
found 3 solutions
searched 271,067/362,880 states (74.7%) in 2:44:33.0 (avg. 36.4 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 7 / \ 1 / \ 6 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 8 /
\ /
D
found 4 solutions
searched 276,107/362,880 states (76.1%) in 2:47:53.9 (avg. 36.5 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 7 / \ 1 / \ 8 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 6 /
\ /
D
found 5 solutions
searched 311,387/362,880 states (85.8%) in 3:10:43.3 (avg. 36.7 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 8 / \ 1 / \ 6 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 7 /
\ /
D
found 6 solutions
searched 316,427/362,880 states (87.2%) in 3:14:06.5 (avg. 36.8 ms)
D----------------- A -----------------D
\ 7 / \ 9 / \ 1 // \\ 9 / \ 1 / \ 2 /
\ / 3 \ / 8 \ // 4 \\ / 5 \ / 8 \ /
\----(1)----//-----\\----(3)----/
\ 6 / \ 4 // \ 3 / \\ 6 / \ 7 /
\ / 2 \ // 7 \ / 2 \\ / 3 \ /
\-----//----(2)----\\-----/
\ 5 // \ 9 / \ 5 / \\ 4 /
\ // 1 \ / 8 \ / 6 \\ /
B ----------------- C
B-----------------C
\ 8 / \ 1 / \ 7 /
\ / 3 \ / 9 \ /
\----(4)----/
\ 5 / \ 4 /
\ / 2 \ /
\-----/
\ 6 /
\ /
D
finished
found 6 solutions
searched 362,880/362,880 states (100.0%) in 3:44:26.9 (avg. 37.1 ms)
0000
,0001
, etc. It's just slightly too complex to comprehend for me at the moment with the two/three-number pieces that are fixed, and some other potential issues alike. $\endgroup$C
and only two labeledB
. $\endgroup$