aboutsummaryrefslogtreecommitdiff
path: root/doc/h48.md
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2024-08-23 10:41:34 +0200
committerSebastiano Tronto <sebastiano@tronto.net>2024-08-23 10:41:34 +0200
commit9c5bdcb09dc56104f7ea34c71a9418c7908ed227 (patch)
tree3228d67ffa79d112ad9a7ea6bee32cf9a4bdf48a /doc/h48.md
parentb940b93f6f17d1ac65ca51ba9ec82a1cce0f5017 (diff)
downloadnissy-core-9c5bdcb09dc56104f7ea34c71a9418c7908ed227.tar.gz
nissy-core-9c5bdcb09dc56104f7ea34c71a9418c7908ed227.zip
Added h48 solver description - work in progress
Diffstat (limited to '')
-rw-r--r--doc/h48.md330
1 files changed, 330 insertions, 0 deletions
diff --git a/doc/h48.md b/doc/h48.md
new file mode 100644
index 0000000..f6fec2e
--- /dev/null
+++ b/doc/h48.md
@@ -0,0 +1,330 @@
1# The H48 optimal solver
2
3This document contains information on the H48 Rubik's Cube optimal solver.
4The implementation of the solver is still in progress. This document
5partly describes ideas that have not been implemented yet, and it will
6be updated to reflect the actual implementation.
7
8I highly encourage the reader to check out Jaap Scherphuis'
9[Computer Puzzling page](https://www.jaapsch.net/puzzles/compcube.htm)
10before reading this document.
11
12Other backround material that will be referenced throughout the text includes:
13
14* [Cube coordinates](https://sebastiano.tronto.net/speedcubing/coordinates)
15 by Sebastiano Tronto
16* [Nxopt](https://github.com/rokicki/cube20src/blob/master/nxopt.md)
17 by Tomas Rokicki
18* [The Mathematics behind Cube Explorer](https://kociemba.org/cube.htm)
19 by Herber Kociemba
20
21Initially I intended to give minimum information here, citing the
22above resources when needed, but I ended up rewriting even some of the
23basic things in this document.
24
25## Optimal solvers
26
27### IDA*
28
29The basic idea behind the solver is an iterative-deepening
30depth-first search with pruning tables, also known as IDA*. You
31can find a detailed explanation of this idea in
32[Jaap's page](https://www.jaapsch.net/puzzles/compcube.htm#tree).
33
34To summarize, the IDA* algorithm finds the shortest solution(s) for a
35scrambled puzzle by using multiple depth-first searches at increasing
36depth. A breadth-first search is not applicable in this scenario because
37of the large amount of memory required. At each depth, the puzzle's state
38is evaluated to obtain an estimated lower bound for how many moves are
39required to solve the puzzle; this is done by employing large pruning
40tables, as well as other techniques. If the lower bound exceeds the
41number of moves required to reach the current target depth, the branch is
42pruned. Otherwise, every possible move is tried, except those that would
43"cancel" with the preceding moves, obtaining new positions at depth N+1.
44
45### Pruning tables and coordinates
46
47A pruning table associates to a cube position a value that is a
48lower bound for the number of moves it takes to solve that position.
49To acces this value, the cube must be turned into an index for the
50given table. This is done using a
51[*coordinate*](https://sebastiano.tronto.net/speedcubing/coordinates),
52by which we mean a function from the set of (admissible, solvable) cube
53positions to the set of non-negative integers. The maximum value N of a
54coordinate must be less than the size of the pruning table; preferably
55there are no "gaps", that is the coordinate reaches all values between
560 and N and the pruning table has size N+1.
57
58Coordinates are often derived from the cosets of a *target subgroup*.
59As an example, consider the pruning table that has one entry for each
60possible corner position, and the stored values denote the length of
61an optimal corner solution for that position. In this situation, the
62target subgroup is the set of all cube positions with solved corners
63(for the Mathematician: this is indeed a subgroup of the group of all
64cube positions). This group consists of `12! * 2^10` positions. The set
65of cosets of this subgroup can be identified with the set of corner
66configurations, and it has size `8! * 3^7`. By looking at corners,
67from each cube position we can compute a number from `0` to `8! * 3^7 - 1`
68and use it as an index for the pruning table.
69
70The pruning table can be filled in many ways. A common way is starting
71with the coordinate of the solved cube (often 0) and visiting the set
72of all coordinates with a breadth-first search. To do this, one needs
73to apply a cube move to a coordinate; this can either be done directly
74(for example using transistion tables, as explained in Jaap's page) or
75by converting the coordinate back into a valid cube position, applying
76the move and then computing the coordinate of the new position.
77
78The largest a pruning table is, the more information it contains,
79the faster the solvers that uses it is. It is therefore convenient to
80compress the tables as much as possible. This can be achieved by using
814 bits per position to store the lower bound, or as little as 2 bits
82per position with some caveats (see Jaap's page or Rokicki's nxopt
83description for two possible ways to achieve this). Tables that use
84only 1 bit per position are possible, but the only information they
85can provide is wether the given position requires more or less than a
86certain number of moves to be solved.
87
88### Symmetries
89
90Another, extremely effective way of reducing the size of a pruning table
91is using symmetries. In the example above of the corner table, one can
92notice that all corner positions that are one quarter-turn away from being
93solved are essentially the same: they can be turned one into the other
94by rotating the cube and optionally applying a mirroring transformation.
95Indeed, every position belongs to a set of up to 48 positions that are
96"essentially the same" in this way. Keeping a separate entry in the
97pruning table for each of these positions is a waste of memory.
98
99There are ways to reduce a coordinate by symmetry, grouping every
100transformation-equivalent position into the same index. You can
101find a description in my
102[cube coordinates page](https://sebastiano.tronto.net/speedcubing/coordinates).
103By doing so, the size of the pruning table is reduced without loss of
104information.
105
106Some well-known solvers (Cube Explorer, nxopt) do not take advantage
107of the full group of 48 symmetries of the cube, but they use only the
10816 symmetries that fix the UD axis. However, they make up for this by
109doing 3 pruning table lookups, one for each axis of the cube.
110
111Some cube positions are self-symmetric: when applying certain
112transformations to them, they remain the same. For example, the
113cube obtained by applying U2 to a solved cube is invariant with
114respect to the mirroring along the RL-plane. This fact has a couple
115of consequences:
116
117* Reducing by a group of N symmetries does not reduce the size of
118 the pruning table by a factor of N, but by a slightly smaller
119 factor. If the size of the coordinate is large, this fact is
120 harmless.
121* When combining symmetry-reduced coordinates with other coordinates,
122 one has to be extra careful with handling self-symmetric coordinates.
123 This is a much more painful point, but a precise description of
124 the adjustments needed to handle these cases is out of the scope
125 of this document.
126
127## The H48 solver
128
129### The target subgroup: H48 coordinates
130
131The H48 solver uses a target group that is invariant under all 48
132symmetries. This group is defined as follows:
133
134* Each corner is placed in the *corner tetrad* it belongs to, and it
135 is oriented. Here by corner tetrad we means one of the two sets of
136 corners {UFR, UBL, DFL, DBR} and {UFL, UBR, DFR, DBL}. For a corner
137 that is placed in its own tetrad, the orientation does not depend on
138 the reference axis chosen, and an oriented corner can be defined
139 has having the U or D sticker facing U or D.
140* Each edge is placed in the *slice* it belongs to. The three edge slices
141 are M = {UF, UB, DB, DF}, S = {UR, UL, DL, DR} and E = {FR, FL, BL, BR}.
142
143Other options are available for corners: for example, we could
144impose that the corner permutation is even or that corners are in
145*half-turn reduction* state, that is they are in the group generated
146by {U2, D2, R2, L2, F2, B2}. We settled on the corner group described
147above because it is easy to calculate and it gives enough options
148for pruning table sizes, depending on how edge orientation is
149considered (see the next section).
150
151We call the coordinate obtained from the target subgroup described
152above the **h0** coordinate. This coordinate has `C(8,4) * 3^7 *
153C(12,8) * C(8,4) = 5.304.568.500` possible values. If the corner
154part of the coordinate is reduced by symmetry, it consists of only
1553393 elements, giving a total of `3393 * C(12,8) * C(8,4) =
156117.567.450` values. This is not very large for a pruning table,
157as with 2 bits per entry it would occupy less than 30MB. However,
158it is possible to take edge orientation (partially) into account
159to produce larger tables.
160
161### Edge orientation
162
163Like for corners, the orientation of an edge is well-defined independently
164of any axis when said edge is placed in the slice it belongs to. We may
165modify the target subgroup defined in the previous section by imposing
166that the edges are all oriented. This yields a new coordinate, which we
167call **h11**, whose full size with symmetry-reduced corners is `3393 *
168C(12,8) * C(8,4) * 2^11 = 240.778.137.600`. A pruning table based on
169this coordinate with 2 bits per entry would occupy around 60GB, which
170is a little too much for most personal computers.
171
172One can wonder if it is possible to use a coordinate that considers
173the orientation of only *some* of the edges, which we may call **h1**
174to **h10**. Such coordinates do exist, but it is not invariant under the
175full symmetry group: indeed an edge whose orientation we keep track of
176could be moved to any of the untracked edges' positions by one of the
177symmetries, making the whole coordinate ill-defined.
178
179It is however possible to compute the symmetry-reduced pruning tables
180for these coordinates. One way to construct them is by taking the **h11**
181pruning table and "forgetting" about some of the edge orientation values,
182collapsing 2 (or a power thereof) values to one by taking the minimum.
183It is also possible to compute these tables directly, as explained in
184the **Pruning table computation** section below (work in progress).
185
186### Coordinate computation for pruning value estimation
187
188In order to access the pruning value for a given cube position, we first
189need to compute its coordinate value. Let's use for simplicity the **h0**
190coordinate, but everything will be valid for any other coordinate of
191the type described in the previous section.
192
193As described in the symmetric-composite coordinate section of
194[my coordinates page](https://sebastiano.tronto.net/speedcubing/coordinates),
195we first need to compute the part of the coordinate where the symmetry
196is applied, that is the corner separation + orientation (also called
197*cocsep*). The value of the coordinate depends on the equivalence
198class of the corner configuration, and is memorized in a table called
199`cocsepdata`. To avoid lengthy computations, the index for a cube position
200in this table is determined by the corner orientation (between `0` and
201`3^7-1`) and a binary representation of the corner separation part (an
2028-bit number with 4 zero digits and 4 one digits, where zeros denote
203corners in one tetrad and 1 denotes corners in the other; the last bit
204can be ignored, as it can easily be deduced from the other 7). This is
205slightly less space-efficient than computing the actual corner-separation
206coordinate as a value between `0` and `C(8,4)`, but it is much faster.
207
208We can now access the value in the `cocsepdata` table corresponding to
209our cube position. This table contains the value of the symmetry-reduced
210coordinate and the so-called *ttrep* (transformation to representative)
211necessary to compute the full **h0** coordinate. For convenience,
212this table also stores a preliminary pruning value based on the corner
213coordinate. If this value is large enough, we may skip the computation of
214the **h0** coordinate and any further estimate. These three values can be
215stored in a single 32 bit integer, so that the table uses less than 12MB.
216
217### Getting the pruning value
218
219Once we have computed the full h0 coordinate, we can access the correct
220entry in the full pruning table. As mentioned above, the pruning table
221can be one of three kinds:
222
223(Work in progress - the only kind of table currently implemented is
224the 4 bits per entry table)
225
226* 4 bits per entry, or `k4`: In this case the pruning value (between 0
227 and 15) can be simply read off the table.
228* 2 bits per entry, or `k2`: Tables of this kind work as described by
229 Rokicki in the
230 [nxopt document](https://github.com/rokicki/cube20src/blob/master/nxopt.md).
231 In this case the pruning table also has a *base value*, that determines
232 the offset to be added to each entry (each entry can only be 0, 1, 2 or 3).
233 If the base value is `b`, a pruning value of 1, 2 or 3 can be used directly
234 as a lower bound of b+1, b+2 and b+3 respectively. However, a value of 0
235 could mean that the actual lower bound is anything between 0 and b, so we
236 cannot take b as a lower bount. Instead we have to use a pruning value from
237 another table, for example the corner-only table mentioned in the previous
238 section, or a completely new one.
239 (Work in progress - `k2` tables not available in the code yet)
240* 1 bit per entry, or `k1`: With one bit per entry, the only information we
241 can get from the pruning table is wether or not the current position
242 requires more or fewer moves than a fixed base value b. This can still be
243 valuable if most positions are more or less equally split between two
244 pruning values.
245 (Work in progress - `k1` tables not available in the code yet)
246
247### Estimation refinements
248
249After computing the pruning value, there are a number of different tricks
250that can be used to improve the estimation.
251
252(Work in progress - the following techniques are not implemented yet)
253
254#### Inverse estimate
255
256A cube position and its inverse will, in general, give a different
257pruning value. If the normal estimate is not enough to prune the branch,
258the inverse of the position can be computed and a new estimate can be
259obtained from there.
260
261#### Reducing the branching factor - tight bounds
262
263*This trick and the next are and adaptation of a similar technique
264introduced by Rokicki in nxopt.*
265
266We can take advantage of the fact that the **h0** (and **h11**) coordinate
267is invariant under the subgroup `<U2, D2, R2, L2, F2, B2>`. Suppose we
268are looking for a solution of length `m`, we are at depth `d` and the
269pruning value `p` *for the inverse position* is a strict bound, that is
270`d+p = m`. In this situation, from the inverse point of view the solution
271cannot end with a 180° move: if this was the case, since these moves are
272contained in the target group for **h0** (or **h11**), it must be that
273we were in the target subgroup as well *before the last move*, i.e. we
274have found a solution for the **h0** coordinate of length `p-1`. But this
275is in contradiction with the fact that the inverse pruning value is `p`.
276
277From all of this, we conclude that trying any 180° move as next move is
278useless (because it would be the last move from the inverse position).
279We can therefore reduce the branching factor significantly and only try
280quarter-turn moves.
281
282#### Reducing the branching factor - switching to inverse
283
284We can expand on the previous trick by using a technique similar to NISS.
285
286Suppose that we have a strict bound for the *normal position*. As above,
287we can deduce that the last move cannot be a 180° move, but this does not
288tell us anything about the possibilities for the next move. However, if
289we *switch to the inverse scramble* we can take advantage of this fact
290as described above. For doing this, we need to replace the cube with its
291inverse, and keep track of the moves done from now on so that we can
292invert them at the end to construct the final solution.
293
294### Other optimizations
295
296Other possible (low-level) optimizations include:
297
298* **Avoid inverse computation**: computing the inverse of a cube position
299 is expensive. We can avoid doing that (for the inverse pruning value
300 estimate) if we bring along both the normal and the inverse cube during
301 the search, and we use *premoves* to apply moves to the inverse scramble.
302 (Work in progress - premoves are not implemented yet, but it will take
303 little work to add them)
304* **Multi-threading (multiple scrambles)**: It is easy to parallelize this
305 algorithm when solving multiple cubes at once, by firing up multiple
306 instances of the solver. It is important to make sure that the same
307 (read-only) pruning table is used for all instances, to avoid expensive
308 memory duplication.
309* **Multi-threading (single scramble)**: It is also possible to parallelize
310 the search for a single scramble. For example, we can generate 18 different
311 cubes, one for each possible starting move, and solve each of them in a
312 separate thread. Some coordination between threads is necessary to stop
313 the search when the desired number of solutions has been found.
314
315## Pruning table computation
316
317### The h0 table
318
319TODO - short explanation of how this is computed
320
321### The intermediate tables (h1, ... h10)
322
323TODO - explain why these are more complicated (if one does not
324want to compute the full **h11** table first)
325
326Work in progress - these tables are not implemented yet.
327
328### The h11 table
329
330Work in progress - this tables is not implemented yet.

Generated with cgit - Back to sebastiano.tronto.net