Single solved state for Sudoku Pyraminx

(Not sure if this is the correct stackexchange, but I think it is.)

I recently bought 10 QJ Pyraminxes with an ambitious idea for a mod in my head. After just the first step of modding I realized it wasn't really gonna work, so I dropped the idea. So, now I have 10 Pyraminxes left.

A few days ago I made a Green Blind Man's Pyraminx in the same style as a Green Blind Man's 3x3x3 Cube I've made before. I plan on doing some more similar mods as 3x3x3 mods I've made, like this Beaded Fishing Lines Bandaged Cube and this Color Sudoku Cube, among others.

I've already made four times nine 'tiles' for the Color Sudoku Pyraminx; each of the different tiles representing a number.
When I look at the (light-blue) Sudoku Pyraminx in the TwistyPuzzles Museum I can only distinguish three sides from the picture(s):

  1     4     9
984   732   651
73625 19856 43782
NNNNN
NNN
N


A possible fourth side could be:

      68275
391
4


With the four sides above we have the following pieces:

• Corners: 491 / 46 5 / 72 4 / 15 6
• Edges: 26 / 19 / 47 / 8 2 / 7 1 / 6 3
• Vertex pieces: 358 / 35 7 / 38 9 / 92 8
(the three centers below a corner are combined one vertex piece)

Now my problem: I want to have a single solved state. What is an easy way to check if this configuration above has a single solved state, and if it hasn't, does someone have an alternative configuration for a single solved state Sudoku Pyraminx?

By the way, a regular Pyraminx has $75,582,720$ possible configurations, and only one solved state. The Sudoku Pyraminx has the same amount of possible configurations (when each piece is unique just like on a regular Pyraminx, which is the case with the one displayed above), but does it also have a single solved state?

As to how this amount is calculated:

• There are four vertex pieces, each with three orientations;
• There are six edge pieces, each with two orientations;
• There are four tips, each with three orientations.

These give a maximum of $6! \cdot 2^6 \cdot 3^4 \cdot 3^4$ configurations.
But, this limit is not reached because:

• Only an even number of flipped pieces are possible (2)
• Only an even permutation of edges are possible (2)

So, this leaves $6! \cdot 2^4 \cdot 3^4 \cdot 3^4$ instead, which equals $75,582,720$. (source) I could go through all these configurations by hand to see if it has more than one solved state. :P In all seriousness though, does anyone know how to determine if it has a single solved state?

EDIT: I've completed the super-gluing and decided to post the result of the Color Sudoku Pyraminx here as well. Once again thanks a lot Jonathan Allan, and of course everyone else that helped!

I've made the following tiles each representing a number:

And super-glued them on in a random orientation, according to the following configuration provided by Jonathan:

  1     4     9
984   732   651
73625 19856 43782
73196
524
8


And this is the result:

My goal was to make a regular easy Pyraminx a bit harder. Well, I think I've succeeded! ;D I have yet to solve it, but it sure looks visually very confusing and I'm sure the solve will be even more so.

Once again thanks a lot!

• 75 million is small enough to be easily checked by computer. If no one comes up with a "clever" solution I'll try to cook up a quick program. May 19, 2016 at 7:21
• @2012rcampion A brute-force program can be as much of an answer to my question as some mathematical calculations. I thought about programming it myself, but I don't really know the proper approach to calculate this (neither in an actual program, nor in theoretical pseudo-code). I have to somehow go through all possible configurations, kinda like a 4-digit code can go through all like 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. May 19, 2016 at 7:30
• I just noticed there are four corners labeled C and only two labeled B. May 21, 2016 at 22:55
• It might be helpful to show a labeled net instead of four separate faces. May 21, 2016 at 22:58
• Re your first line: You are exactly at the right place :c) And thanks for posting this here. May 22, 2016 at 13:39

Brute force shows that for the example there are:

$8$ solutions if rings are not considered*
$0$ solutions if rings are considered*

Call the left of the three faces at the top of your net $L$, the middle one $F$, the right one $R$, and the one underneath $D$.

In the current configuration there are:

• four tips $(tip_0, tip_1, tip_2, tip_3)$
which have labels on the faces $(FRL, FRD, FLD, RLD)$
with labels $((4,9,1), (6,4,5), (1,5,6), (2,7,4))$ respectively.
• four "axial" pieces $(axial_0, axial_1, axial_2, axial_3)$
which again have labels on the faces $(FRL, FRD, FLD, RLD)$
these have labels $((3,5,8), (5,3,7), (9,2,8), (8,3,9))$ respectively.
• six edges $(edge_0, edge_1, edge_2, edge_3, edge_4, edge_5)$
which have labels on the faces $(FR, FL, FD, RL, RD, LD)$
with labels $((2,6), (7,4), (8,2), (1,9), (7,1), (6,3))$ respectively

* I noticed that the link describes the solved state as having $[1,9]$ in each of the four "rings"
- the three edges and one axial adjacent to a tip, as shown in this image.
Using this requirement the suggested state is not solved
- the ring around the $FRD$ tip is $2,6,3,7,1,7,2,8,5$

With the constraint to solve the rings there are $6$ possible labellings of $D$ given the provided $L$, $F$, and $R$ labellings.
All six are uniquely solvable (given the ring requirement):

  1     4     9
984   732   651
73625 19856 43782
73196
524
8

1     4     9
984   732   651
73625 19856 43782
83196
524
7

1     4     9
984   732   651
73625 19856 43782
63197
524
8

1     4     9
984   732   651
73625 19856 43782
83197
524
6

1     4     9
984   732   651
73625 19856 43782
63198
524
7

1     4     9
984   732   651
73625 19856 43782
73198
524
6


Following the same domain space reduction as 2012rcampion used we find that of the $9!=362,880$ possible $D$ faces that $7,408$ are uniquely solvable without the ring restriction, about $1$ in $49$. One such example is:

  1     4     9
984   732   651
73625 19856 43782
35821
974
6


Working

One may independently:

• rotate any tip
• rotate any axial
• permute the edges such that the parity remains the same, and
• flip an even number of edges

(Note: we may always rotate the whole Pyraminx such that the tips and axials are in the same positions as they were when we started.)

We may therefore identify a unique state as follows:

• $(tipOrientation_0, tipOrientation_1, tipOrientation_2, tipOrientation_3)$
• $(axialOrientation_0, axialOrientation_1, axialOrientation_2, axialOrientation_3)$
• $(edgePos_0, edgePos_1, edgePos_2, edgePos_3, edgePos_4, edgePos_5)$
• $flippedEdges$

Where:

• the $xOrientations$ are $0$, $1$, or $2$ representing the index of the first label listed,
e.g. $tipOrientation_2=2$ would make $tip_2=(6,1,5)$
such that the label $6$ is on $F$, $1$ is on $L$, and $5$ is on $D$.
• the $edgePos$ identify the number of the original edge that is in the position originally occupied by the index
e.g. $edgePos_4=2$ means that $edge_2$ is where $edge_4$ originally was.
• the $flippedEdges$ is a list of the $edgePos$ that are flipped
e.g. $(0,4)$ would imply the edges now residing where $edge_0$ and $edge_4$ originally were have been flipped.

The current state is solved, if rings are not considered, and would be represented as:

• $(0, 0, 0, 0), (0, 0, 0, 0), (0, 1, 2, 3, 4, 5), ()$

or in a net with $L$ on the left, $F$ in the middle, $R$ on the right and $D$ underneath ($D$ is rotated from your picture to align it's adjoining edge with $F$):

  1     4     9
984   732   651
73625 19856 43782
68275
391
4


There are eight solved states, the other seven being:

• $(0, 0, 1, 0) (2, 2, 0, 0) (0, 3, 5, 1, 4, 2) (2, 4)$
• $(0, 0, 0, 1) (0, 0, 0, 0) (2, 0, 1, 4, 5, 3) (3, 5)$
• $(0, 0, 0, 1) (0, 0, 0, 0) (2, 0, 4, 3, 5, 1) ()$
• $(0, 0, 0, 0) (0, 0, 2, 0) (3, 0, 4, 1, 5, 2) (0, 5)$
• $(0, 0, 0, 0) (1, 2, 1, 1) (3, 5, 2, 0, 4, 1) (0, 1, 3, 5)$
• $(2, 1, 1, 0) (2, 1, 0, 0) (4, 2, 0, 3, 5, 1) (1, 2, 3, 5)$
• $(1, 0, 0, 0) (1, 2, 1, 1) (5, 2, 1, 3, 4, 0) (0, 2, 3, 5)$

in net form, as before:

  1     4     9
459   182   637
73826 59376 45182
18635
297
4

1     4     9
786   238   251
43925 19756 43687
68475
193
2

1     4     9
986   238   251
43725 19756 43687
68175
493
2

1     4     9
486   239   157
73295 18756 43682
62175
893
4

1     4     9
236   359   186
79485 12876 45732
69235
781
4

9     1     4
158   287   139
73426 59634 57682
18256
793
4

4     9     1
132   853   689
79685 12476 45732
69735
281
4


As an example of transitioning from the state provided to one of the other solved states, call:

• $A_n$ a clockwise* rotation of $Axial_n$ and $Tip_n$ together with the three edges currently co-located
• $A_n'$ an anti-clockwise* rotation of the same
• $T_n$ a clockwise* rotation of just $Tip_n$
• $T_n'$ an anticlockwise* rotation of just $Tip_n$

* The directions clockwise and anti-clockwise are if we are looking at the tip.

Performing $A_1' A_0' A_3' A_1' A_3' A_1' A_0' A_3' A_0' T_3'$ will transition to the third of the seven listed solutions.

Explicitly that is $A_{FRD}' A_{FRL}' A_{RLD}' A_{FRD}' A_{RLD}' A_{FRD}' A_{FRL}' A_{RLD}' A_{FRL}' T_{RLD}'$

Python module for brute forcing (not that elegant, especially the print function, but hey) - with optional testRings for the requirement of rings:

import itertools

TIPS    = [[4,9,1], [6,4,5], [1,5,6], [2,7,4]] # [FRL, FRD, FLD, RLD]
AXIALS  = [[3,5,8], [5,3,7], [9,2,8], [8,3,9]] # [FRL, FRD, FLD, RLD]
TA_FACE = [[0,1,2], [0,1,3], [0,2,3], [1,2,3]] # <-- i.e with F,R,L,D = 0,1,2,3

EDGES   = [[2,6], [7,4], [8,2], [1,9], [7,1], [6,3]] # [FR, FL, FD, RL, RD, LD]
E_FACE  = [[0,1], [0,2], [0,3], [1,2], [1,3], [2,3]] # <-- i.e. with F,R,L,D = 0,1,2,3

def printSolvedStates(tips=TIPS, axials=AXIALS, edges=EDGES, testRings=False):
for curTips, curAxials, curEdges, tipOrientations, axialOrientations, edgePermutation, flippedEdges in iterSolvedStates(tips, axials, edges, testRings):
printState(curTips, curAxials, curEdges)

def iterSolvedStates(tips=TIPS, axials=AXIALS, edges=EDGES, testRings=False):
for curTips, curAxials, curEdges, tipOrientations, axialOrientations, edgePermutation, flippedEdges in iterStates(tips, axials, edges):
if isSolved(curTips, curAxials, curEdges, testRings):
yield curTips, curAxials, curEdges, tipOrientations, axialOrientations, edgePermutation, flippedEdges

def iterStates(tips=TIPS, axials=AXIALS, edges=EDGES):
for edgePermutation in itertools.permutations(range(6)):
if isEven(edgePermutation):
for nFlips in range(0, 7, 2):
for flippedEdges in itertools.combinations(range(6), nFlips):
curEdges = [edges[edge] for edge in edgePermutation]
for edge in flippedEdges:
curEdges[edge] = curEdges[edge][-1::-1]
for axialOrientations in itertools.product(range(3), repeat=4):
curAxials = [axial[start::] + axial[0:start] for axial, start in zip(axials, axialOrientations)]
for tipOrientations in itertools.product(range(3), repeat=4):
curTips = [tip[start::] + tip[0:start] for tip, start in zip(tips, tipOrientations)]
yield curTips, curAxials, curEdges, tipOrientations, axialOrientations, edgePermutation, flippedEdges

def isSolved(tips, axials, edges, testRings=False):
labelSet = set()
for face in range(4):
labelSet.clear()
for tip, tipFaces in zip(tips, TA_FACE):
for tipLabel, labelFace in zip(tip, tipFaces):
if face == labelFace:
if tipLabel in labelSet:
return False
for axial, axialFaces in zip(axials, TA_FACE):
for axialLabel, labelFace in zip(axial, axialFaces):
if face == labelFace:
if axialLabel in labelSet:
return False
for edge, edgeFaces in zip(edges, E_FACE):
for edgeLabel, labelFace in zip(edge, edgeFaces):
if face == labelFace:
if edgeLabel in labelSet:
return False
if testRings:
# rings: (eFR eFL eRL aFRL), (eFR eFD eRD aFRD), (eFL eFD eLD aFLD), (eRL eRD eLD aRLD)
for axial, edgeIndexes in enumerate(((0, 1, 3), (0, 2, 4), (1, 2, 5), (3, 4, 5))):
labelSet.clear()
for i in range(3):
if axials[axial][i] in labelSet:
return False
for edge in edgeIndexes:
for i in range(2):
if edges[edge][i] in labelSet:
return False
return True

def isEven(permutation):
even = True
cpy = list(permutation)[:]
l = len(cpy)
for i in range(l-1):
if cpy[i] != i:
even = not even
s = min(range(i,l), key=cpy.__getitem__)
cpy[i], cpy[s] = cpy[s], cpy[i]
return even

def printState(tips, axials, edges):
res = '''  {0}     {1}     {2}
{3}{4}{5}   {6}{7}{8}   {9}{10}{11}
{12}{13}{14}{15}{16} {17}{18}{19}{20}{21} {22}{23}{24}{25}{26}
{27}{28}{29}{30}{31}
{32}{33}{34}
{35}
'''.format(tips[0][2], tips[0][0], tips[0][1], edges[3][1], axials[0][2], edges[1][1], edges[1][0], axials[0][0], edges[0][0], edges[0][1], axials[0][1], edges[3][0], tips[3][1], axials[3][1], edges[5][0], axials[2][1], tips[2][1], tips[2][0], axials[2][0], edges[2][0], axials[1][0], tips[1][0], tips[1][1], axials[1][1], edges[4][0], axials[3][0], tips[3][0], tips[2][2], axials[2][2], edges[2][1], axials[1][2], tips[1][2], edges[5][1], axials[3][2], edges[4][1], tips[3][2])
print(res)

• I think your net diagrams are incorrect. It seems you didn't rotate the bottom face correctly, and have its D&B corners lined up with the B&C corners of the front face. May 22, 2016 at 8:08
• Thanks a lot for all the effort! +1 But Jaapsch is indeed right, the print doesn't seem to be correct. For example, if I look at the first three I can see a 275 as tip, but that tip isn't present at the fourth one. Anyway, it seems to indeed prove that my proposed configuration has more than one solved states. Hmm, is it even possible to have a configuration with a single solved state? I like that print function btw. xD May 22, 2016 at 10:47
• OK after the update to include nets this should now be correct. I'm going to move on to looking for a working $D$ face for you. Thanks @JaapScherphuis. May 23, 2016 at 6:49
• Hi Jonathan. I didn't even knew about the ring requirement, but it does make the puzzle even more interesting/harder, so I don't mind using that same requirement for my configuration. Thanks a lot for all the effort, I'll accept your answer. As for "I don't know yet if it is possible to have a uniquely solvable setup without the ring restriction.", I have the feeling you are enjoying yourself, so it's up to you whether you want to find out or not. ;) I will use these added ring requirements for my Color Sudoku Pyraminx and pick one of your six proposed configurations. May 23, 2016 at 10:58
• Oh that looks much harder than recognising nine unique digits, especially for me as I'm colour blind - I'd have to go through each one checking them over and over, or label them :) May 24, 2016 at 23:06

I'm not sure of the math involved in actually proving this (even by brute force), so I made a program that will just keep randomly scrambling the Pyraminx and checking if its state is a valid solution.

So far it has made 87.5 billion random moves, and hasn't located a solution other than the one given above. It's possible that it hasn't visited every state, but since it has made 1000 times more moves than the total possible number of configurations, I'd say the odds are pretty good.

• Hmm, that sounds promising. Two questions though: which programming language did you use? And might it be possible to store the full state after each random move, so it could be run through all 75M configurations. I haven't seen your code, but if you were able to do a random 'move', wouldn't it also be possible to do controlled 'moves' to go through each configurations? Kinda like going through all configurations of a 4-digit number (0000, 0001, ..., 0100, 0101, ..., 9999). But instead of digits we have the tip-/vertex-/edge-parts. May 20, 2016 at 22:33
• @KevinCruijssen I used C#. It would certainly work to do "controlled" moves, but I couldn't come up with an algorithm to iterate though all possible states and be sure none were missed. May 21, 2016 at 2:58
• @KevinCruijssen I can post the code if you like, although it's not great code; I just slapped it together quickly. May 21, 2016 at 3:00
• If the code isn't too big you can post it here, otherwise you could also post it in an online C# compiler and link towards it here. Luckily I still have Visual Studio on my PC. :) May 21, 2016 at 10:14
• If you assume that your program visits states uniformly at random you can treat this as an instance of the coupon collector problem, so you should, on average, take 1.3 billion moves to cover all the states May 21, 2016 at 18:30

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

• 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)

• Nice domain space reduction. You may find more bottom faces than the six I found since I only considered ones that would be solved as presented and checked they had no other solutions. May 24, 2016 at 22:57
• @JonathanAllan interestingly it looks like I found the exact same solutions you did, and no more. May 25, 2016 at 1:25