h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

commit a7444e9ab414e1eb73451a806289a3f7c4f02047
parent 1983bacfbb84e2a410ebe663ac1fa7c1b22eb096
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Fri, 30 Aug 2024 07:52:04 +0200

Merge branch 'master' of tronto.net:h48

Diffstat:
Msrc/solvers/h48/gendata_h48.h | 31+++++++++++++++++++++----------
Msrc/solvers/h48/map.h | 5+++--
Msrc/utils/math.h | 1+
Mtools/001_gendata_h48h0k4/gendata_h48h0k4.c | 7+++----
Atools/002_gendata_h48h0k2/gendata_h48h0k2.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtools/002_stats_tables_h48/stats_tables_h48.c | 143-------------------------------------------------------------------------------
Dtools/003_solve_small/solve_small.c | 94-------------------------------------------------------------------------------
Atools/010_stats_tables_h48/stats_tables_h48.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/020_solve_small/solve_small.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Dtools/timerun.h | 51---------------------------------------------------
Atools/tool.h | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 347 insertions(+), 304 deletions(-)

diff --git a/src/solvers/h48/gendata_h48.h b/src/solvers/h48/gendata_h48.h @@ -1,6 +1,7 @@ #define H48_COORDMAX_NOEO ((int64_t)(COCSEP_CLASSES * _12c4 * _8c4)) #define H48_COORDMAX(h) ((int64_t)(H48_COORDMAX_NOEO << (int64_t)(h))) -#define H48_TABLESIZE(h, k) ((size_t)H48_COORDMAX((h)) / ((size_t)8 / (size_t)(k))) +#define H48_DIV(k) ((size_t)8 / (size_t)(k)) +#define H48_TABLESIZE(h, k) _div_round_up((size_t)H48_COORDMAX((h)), H48_DIV(k)) #define H48_COEFF(k) (UINT32_C(32) / (uint32_t)(k)) #define H48_INDEX(i, k) ((uint32_t)(i) / H48_COEFF(k)) @@ -140,7 +141,7 @@ gendata_h48(gendata_h48_arg_t *arg) cocsepsize = gendata_cocsep( (void *)arg->cocsepdata, arg->selfsim, arg->crep); arg->h48data = arg->cocsepdata + (cocsepsize / sizeof(uint32_t)); - arg->info = arg->h48data + + arg->info = arg->h48data + 1 + (H48_TABLESIZE(arg->h, arg->k) / sizeof(uint32_t)); if (arg->buf != NULL) @@ -341,18 +342,27 @@ gendata_h48k2(gendata_h48_arg_t *arg) }; i = 0; +int jj = 0; for (kv = h48map_nextkvpair(&shortcubes, &i); i != shortcubes.capacity; kv = h48map_nextkvpair(&shortcubes, &i) ) { - /* TODO maybe over all sim? */ dfsarg.cube = invcoord_h48(kv.key, arg->crep, 11); + +#if 1 gendata_h48k2_dfs(&dfsarg); +#else + /* It looks like this is not necessary, we get the same result */ + _foreach_h48sim( + dfsarg.cube, arg->cocsepdata, arg->selfsim, arg->h, + gendata_h48k2_dfs(&dfsarg); + ) +#endif +if ((++jj) % 1000 == 0) LOG("Done %d distance 8 cubes\n", jj); } h48map_destroy(&shortcubes); - /* TODO: move info update to dfs? */ memset(arg->info, 0, 5 * sizeof(arg->info[0])); arg->info[0] = base[arg->k]; for (j = 0; j < H48_COORDMAX(arg->h); j++) { @@ -367,7 +377,7 @@ gendata_h48k2_return_size: _static void gendata_h48k2_dfs(h48k2_dfs_arg_t *arg) { - uint8_t nmoves; + uint8_t nmoves, oldval, newval; uint64_t val; int64_t coord, fullcoord; h48k2_dfs_arg_t nextarg; @@ -378,12 +388,13 @@ gendata_h48k2_dfs(h48k2_dfs_arg_t *arg) val = h48map_value(arg->shortcubes, fullcoord); - if (arg->depth >= arg->base && arg->depth <= arg->base + 2) - set_esep_pval( - arg->h48data, coord, arg->k, arg->depth - arg->base); + if (arg->depth >= arg->base && arg->depth <= arg->base + 2) { + oldval = get_esep_pval(arg->h48data, coord, arg->k); + newval = _min(oldval, arg->depth - arg->base); + set_esep_pval(arg->h48data, coord, arg->k, newval); + } - if ((val < arg->shortdepth) || - (arg->depth > arg->shortdepth && val != MAP_UNSET) || + if ((arg->depth > arg->shortdepth && val != MAP_UNSET_VAL) || (arg->depth >= arg->maxdepth || arg->depth >= arg->base + 2)) return; diff --git a/src/solvers/h48/map.h b/src/solvers/h48/map.h @@ -1,6 +1,7 @@ #define MAP_UNSET UINT64_C(0xFFFFFFFFFFFFFFFF) #define MAP_KEYMASK UINT64_C(0xFFFFFFFFFF) #define MAP_KEYSHIFT UINT64_C(40) +#define MAP_UNSET_VAL (MAP_UNSET >> MAP_KEYSHIFT) typedef struct { uint64_t n; @@ -84,8 +85,8 @@ h48map_nextkvpair(h48map_t *map, uint64_t *p) kvpair_t kv; uint64_t pair; - kv.key = MAP_UNSET; - kv.val = MAP_UNSET; + kv.key = MAP_KEYMASK; + kv.val = MAP_UNSET_VAL; DBG_ASSERT(*p < map->capacity, kv, "Error looping over map: given index %" PRIu64 " is out of " diff --git a/src/utils/math.h b/src/utils/math.h @@ -1,6 +1,7 @@ #define _swap(x, y) do { x ^= y; y ^= x; x ^= y; } while (0) #define _min(x, y) ((x) < (y) ? (x) : (y)) #define _max(x, y) ((x) > (y) ? (x) : (y)) +#define _div_round_up(n, d) (((n) + (d) - 1) / (d)) _static int64_t factorial(int64_t); _static bool isperm(uint8_t *, int64_t); diff --git a/tools/001_gendata_h48h0k4/gendata_h48h0k4.c b/tools/001_gendata_h48h0k4/gendata_h48h0k4.c @@ -1,5 +1,4 @@ -#include "../timerun.h" -#include "../../src/nissy.h" +#include "../tool.h" #define MAXDEPTH 20 #define HVALUE 0 @@ -38,12 +37,12 @@ void run(void) { printf("Error generating table\n"); } else { printf("Succesfully generated %" PRId64 " bytes. Table:\n", s); - h48info = (uint32_t *)buf + (ETABLESIZE(HVALUE) + COCSEPSIZE) / 4; + h48info = (uint32_t *)buf + 1 + (ETABLESIZE(HVALUE) + COCSEPSIZE) / 4; for (i = 0; i < MAXDEPTH+1 && h48info[i+1]; i++) { x = h48info[i+1]; printf("%d:\t%" PRIu32, i, x); if (x != expected[i]) - printf(" <--- Error! Expected: %" PRIu32 "\n", + printf(" <--- Error! Expected: %" PRIu32, expected[i]); printf("\n"); } diff --git a/tools/002_gendata_h48h0k2/gendata_h48h0k2.c b/tools/002_gendata_h48h0k2/gendata_h48h0k2.c @@ -0,0 +1,62 @@ +#include "../tool.h" + +#define MAXDEPTH 20 +#define HVALUE 0 +#define OPTIONS "0;2;20" +#define LONGOPTIONS "h = 0, k = 2, max depth = 20" + +#define COCSEPSIZE 1119792 +#define ETABLESIZE(h) (((3393 * 495 * 70) >> 2) << (size_t)(h)) + +uint32_t expected[21] = { + /* Base value is 8 */ + [0] = 5473562, + [1] = 34776317, + [2] = 68566704, + [3] = 8750867, +}; + +char *buf; + +void run(void) { + uint32_t *h48info, x; + int i; + int64_t s; + + s = nissy_gendata("h48", OPTIONS, buf); + + if (s == -1) { + printf("Error generating table\n"); + } else { + printf("Succesfully generated %" PRId64 " bytes. Table:\n", s); + h48info = (uint32_t *)buf + 1 + (ETABLESIZE(HVALUE) + COCSEPSIZE) / 4; + for (i = 0; i < 4; i++) { + x = h48info[i+1]; + printf("%d:\t%" PRIu32, i, x); + if (x != expected[i]) + printf(" <--- Error! Expected: %" PRIu32, + expected[i]); + printf("\n"); + } + } +} + +int main(void) { + int64_t size; + + nissy_setlogger(log_stderr); + + size = nissy_datasize("h48", OPTIONS); + if (size == -1) { + printf("gendata_h48 benchmark: error in datasize\n"); + return 1; + } + + buf = malloc(size); + + timerun(run, "benchmark gendata_h48 " LONGOPTIONS); + + free(buf); + + return 0; +} diff --git a/tools/002_stats_tables_h48/stats_tables_h48.c b/tools/002_stats_tables_h48/stats_tables_h48.c @@ -1,143 +0,0 @@ -#include <pthread.h> -#include <time.h> -#include "../timerun.h" -#include "../../src/nissy.h" - -#define MAXMOVES 20 -#define NTHREADS 32 -#define NCUBES_PER_THREAD 10000 -#define LOG_EVERY (NCUBES_PER_THREAD / 10) - -typedef struct { - int n; - int thread_id; - int64_t v[12][100]; -} thread_arg_t; - -const char *filename = "tables/h48h0k4"; -char *buf; - -uint64_t rand64(void) { - uint64_t i, ret; - - for (i = 0, ret = 0; i < 64; i++) - ret |= (uint64_t)(rand() % 2) << i; - - return ret; -} - -static void * -run_thread(void *arg) -{ - char sols[12], cube[22]; - int64_t ep, eo, cp, co; - int i, j; - - thread_arg_t *a = (thread_arg_t *)arg; - - for (i = 0; i < a->n; i++) { - ep = rand64(); - eo = rand64(); - cp = rand64(); - co = rand64(); - nissy_getcube(ep, eo, cp, co, "fix", cube); - nissy_solve(cube, "h48stats", "", "", - 0, MAXMOVES, 1, -1, buf, sols); - for (j = 0; j < 12; j++) - a->v[j][(int)sols[j]]++; - if ((i+1) % LOG_EVERY == 0) - fprintf(stderr, "[thread %d] %d cubes solved...\n", - a->thread_id, i+1); - } - - return NULL; -} - -void run(void) { - int64_t i, j, k, tot; - double avg; - pthread_t thread[NTHREADS]; - thread_arg_t arg[NTHREADS]; - - for (i = 0; i < NTHREADS; i++) { - arg[i] = (thread_arg_t) { - .thread_id = i, - .n = NCUBES_PER_THREAD, - .v = {{0}} - }; - pthread_create(&thread[i], NULL, run_thread, &arg[i]); - } - - for (i = 0; i < NTHREADS; i++) - pthread_join(thread[i], NULL); - - for (j = 0; j < 12; j++) { - printf("Data for h=%" PRId64 "\n", j); - for (k = 0, avg = 0.0; k <= 16; k++) { - for (i = 0, tot = 0; i < NTHREADS; i++) - tot += arg[i].v[j][k]; - printf("%" PRId64 "\t%" PRId64 "\n", k, tot); - avg += tot * k; - } - avg /= (double)(NCUBES_PER_THREAD * NTHREADS); - printf("Average: %.4lf\n", avg); - printf("\n"); - } -} - -int getdata(int64_t size) { - int64_t s; - FILE *f; - - buf = malloc(size); - - if ((f = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "Table file not found, generating them." - " This can take a while.\n"); - s = nissy_gendata("h48stats", "", buf); - if (s != size) { - fprintf(stderr, "Error generating table"); - if (s != -1) - fprintf(stderr, " (got %" PRId64 " bytes)", s); - fprintf(stderr, "\n"); - return 1; - } - if ((f = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "Could not write tables to file %s" - ", will be regenerated next time.\n", filename); - } else { - fwrite(buf, size, 1, f); - fclose(f); - } - } else { - fprintf(stderr, "Reading tables from file %s\n", filename); - fread(buf, size, 1, f); - fclose(f); - } - - return 0; -} - -int main(void) { - int64_t size; - - srand(time(NULL)); - - nissy_setlogger(log_stderr); - size = nissy_datasize("h48stats", ""); - if (size == -1) { - printf("h48 stats: error in datasize\n"); - return 1; - } - - if (getdata(size) != 0) { - printf("Error getting table, stopping\n"); - free(buf); - return 1; - } - - timerun(run, "h48 table stats"); - - free(buf); - return 0; -} diff --git a/tools/003_solve_small/solve_small.c b/tools/003_solve_small/solve_small.c @@ -1,94 +0,0 @@ -#include <pthread.h> -#include <time.h> -#include "../timerun.h" -#include "../../src/nissy.h" - -#define OPTIONS "0;4;20" - -const char *filename = "tables/h48h0k4"; -char *buf; -char *scrambles[] = { - "R D' R2 D R U2 R' D' R U2 R D R'", /* 12 optimal */ - "RLUD RLUD RLUD", /* 12 optimal */ - NULL -}; - -void run(void) { - int i; - int64_t n; - char sol[100], cube[22]; - - printf("Solved the following scrambles:\n\n"); - for (i = 0; scrambles[i] != NULL; i++) { - printf("%s\n", scrambles[i]); - fprintf(stderr, "Solving scramble %s\n", scrambles[i]); - if (nissy_frommoves(scrambles[i], cube) == -1) { - fprintf(stderr, "Invalid scramble, " - "continuing with next scramble\n"); - continue; - } - n = nissy_solve( - cube, "h48", OPTIONS, "", 0, 20, 1, -1, buf, sol); - if (n == 0) - fprintf(stderr, "No solution found, " - "continuing with next scramble\n"); - } - printf("\n"); -} - -int getdata(int64_t size) { - int64_t s; - FILE *f; - - buf = malloc(size); - - if ((f = fopen(filename, "rb")) == NULL) { - fprintf(stderr, "Table file not found, generating them." - " This can take a while.\n"); - s = nissy_gendata("h48", OPTIONS, buf); - if (s != size) { - fprintf(stderr, "Error generating table"); - if (s != -1) - fprintf(stderr, " (got %" PRId64 " bytes)", s); - fprintf(stderr, "\n"); - return 1; - } - if ((f = fopen(filename, "wb")) == NULL) { - fprintf(stderr, "Could not write tables to file %s" - ", will be regenerated next time.\n", filename); - } else { - fwrite(buf, size, 1, f); - fclose(f); - } - } else { - fprintf(stderr, "Reading tables from file %s\n", filename); - fread(buf, size, 1, f); - fclose(f); - } - - return 0; -} - -int main(void) { - int64_t size; - - srand(time(NULL)); - - nissy_setlogger(log_stderr); - size = nissy_datasize("h48", OPTIONS); - if (size == -1) { - printf("h48 stats: error in datasize\n"); - return 1; - } - - if (getdata(size) != 0) { - printf("Error getting table, stopping\n"); - free(buf); - return 1; - } - - timerun(run, "small solver benchmark"); - - free(buf); - return 0; -} diff --git a/tools/010_stats_tables_h48/stats_tables_h48.c b/tools/010_stats_tables_h48/stats_tables_h48.c @@ -0,0 +1,100 @@ +#include <pthread.h> + +#include "../tool.h" + +#define MAXMOVES 20 +#define NTHREADS 32 +#define NCUBES_PER_THREAD 10000 +#define LOG_EVERY (NCUBES_PER_THREAD / 10) + +const char *solver = "h48stats"; +const char *options = ""; +const char *filename = "tables/h48h0k4"; +char *buf; + +typedef struct { + int n; + int thread_id; + int64_t v[12][100]; +} thread_arg_t; + +uint64_t rand64(void) { + uint64_t i, ret; + + for (i = 0, ret = 0; i < 64; i++) + ret |= (uint64_t)(rand() % 2) << i; + + return ret; +} + +static void * +run_thread(void *arg) +{ + char sols[12], cube[22]; + int64_t ep, eo, cp, co; + int i, j; + + thread_arg_t *a = (thread_arg_t *)arg; + + for (i = 0; i < a->n; i++) { + ep = rand64(); + eo = rand64(); + cp = rand64(); + co = rand64(); + nissy_getcube(ep, eo, cp, co, "fix", cube); + nissy_solve(cube, "h48stats", "", "", + 0, MAXMOVES, 1, -1, buf, sols); + for (j = 0; j < 12; j++) + a->v[j][(int)sols[j]]++; + if ((i+1) % LOG_EVERY == 0) + fprintf(stderr, "[thread %d] %d cubes solved...\n", + a->thread_id, i+1); + } + + return NULL; +} + +void run(void) { + int64_t i, j, k, tot; + double avg; + pthread_t thread[NTHREADS]; + thread_arg_t arg[NTHREADS]; + + for (i = 0; i < NTHREADS; i++) { + arg[i] = (thread_arg_t) { + .thread_id = i, + .n = NCUBES_PER_THREAD, + .v = {{0}} + }; + pthread_create(&thread[i], NULL, run_thread, &arg[i]); + } + + for (i = 0; i < NTHREADS; i++) + pthread_join(thread[i], NULL); + + for (j = 0; j < 12; j++) { + printf("Data for h=%" PRId64 "\n", j); + for (k = 0, avg = 0.0; k <= 16; k++) { + for (i = 0, tot = 0; i < NTHREADS; i++) + tot += arg[i].v[j][k]; + printf("%" PRId64 "\t%" PRId64 "\n", k, tot); + avg += tot * k; + } + avg /= (double)(NCUBES_PER_THREAD * NTHREADS); + printf("Average: %.4lf\n", avg); + printf("\n"); + } +} + +int main(void) { + srand(time(NULL)); + nissy_setlogger(log_stderr); + + if (getdata(solver, options, &buf, filename) != 0) + return 1; + + timerun(run, "h48 table stats"); + + free(buf); + return 0; +} diff --git a/tools/020_solve_small/solve_small.c b/tools/020_solve_small/solve_small.c @@ -0,0 +1,51 @@ +#include <pthread.h> + +#include "../tool.h" + +const char *solver = "h48"; +const char *options = "0;4;20"; +const char *filename = "tables/h48h0k4"; +char *buf; + +char *scrambles[] = { + "R D' R2 D R U2 R' D' R U2 R D R'", /* 12 optimal */ + "RLUD RLUD RLUD", /* 12 optimal */ + NULL +}; + +void run(void) { + int i; + int64_t n; + char sol[100], cube[22]; + + printf("Solved the following scrambles:\n\n"); + for (i = 0; scrambles[i] != NULL; i++) { + printf("%s\n", scrambles[i]); + fprintf(stderr, "Solving scramble %s\n", scrambles[i]); + if (nissy_frommoves(scrambles[i], cube) == -1) { + fprintf(stderr, "Invalid scramble, " + "continuing with next scramble\n"); + continue; + } + n = nissy_solve( + cube, "h48", options, "", 0, 20, 1, -1, buf, sol); + if (n == 0) + fprintf(stderr, "No solution found, " + "continuing with next scramble\n"); + } + printf("\n"); +} + +int main(void) { + + srand(time(NULL)); + nissy_setlogger(log_stderr); + + if (getdata(solver, options, &buf, filename) != 0) + return 1; + + timerun(run, "small solver benchmark"); + + free(buf); + return 0; +} diff --git a/tools/timerun.h b/tools/timerun.h @@ -1,51 +0,0 @@ -#include <stdarg.h> -#include <stdbool.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> - -void -log_stderr(const char *str, ...) -{ - va_list args; - - va_start(args, str); - vfprintf(stderr, str, args); - va_end(args); -} - - -double -timerun(void (*run)(void), char *name) -{ - struct timespec start, end; - double tdiff, tdsec, tdnano; - - printf("\n"); - fflush(stdout); - - if (run == NULL) { - printf("> %s: nothing to run!\n", name); - fflush(stdout); - return -1.0; - } - - printf("Running tool: %s\n", name); - printf("==========\n"); - fflush(stdout); - - clock_gettime(CLOCK_MONOTONIC, &start); - run(); - clock_gettime(CLOCK_MONOTONIC, &end); - - tdsec = end.tv_sec - start.tv_sec; - tdnano = end.tv_nsec - start.tv_nsec; - tdiff = tdsec + 1e-9 * tdnano; - - printf("==========\n"); - printf("\nTotal time: %.4fs\n", tdiff); - fflush(stdout); - - return tdiff; -} diff --git a/tools/tool.h b/tools/tool.h @@ -0,0 +1,106 @@ +#include <time.h> +#include <stdarg.h> +#include <stdbool.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "../src/nissy.h" + +static void +log_stderr(const char *str, ...) +{ + va_list args; + + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); +} + +static double +timerun(void (*run)(void), char *name) +{ + struct timespec start, end; + double tdiff, tdsec, tdnano; + + printf("\n"); + fflush(stdout); + + if (run == NULL) { + printf("> %s: nothing to run!\n", name); + fflush(stdout); + return -1.0; + } + + printf("Running tool: %s\n", name); + printf("==========\n"); + fflush(stdout); + + clock_gettime(CLOCK_MONOTONIC, &start); + run(); + clock_gettime(CLOCK_MONOTONIC, &end); + + tdsec = end.tv_sec - start.tv_sec; + tdnano = end.tv_nsec - start.tv_nsec; + tdiff = tdsec + 1e-9 * tdnano; + + printf("==========\n"); + printf("\nTotal time: %.4fs\n", tdiff); + fflush(stdout); + + return tdiff; +} + +static int +getdata( + const char *solver, + const char *options, + char **buf, + const char *filename +) { + int64_t s, size, sizeread; + FILE *f; + + if ((size = nissy_datasize(solver, options)) == -1) { + printf("Error in datasize\n"); + goto getdata_error_nofree; + } + + *buf = malloc(size); + + if ((f = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "Table file not found, generating them." + " This can take a while.\n"); + s = nissy_gendata(solver, options, *buf); + if (s != size) { + fprintf(stderr, "Error generating table"); + if (s != -1) + fprintf(stderr, " (got %" PRId64 " bytes)", s); + fprintf(stderr, "\n"); + goto getdata_error; + } + if ((f = fopen(filename, "wb")) == NULL) { + fprintf(stderr, "Could not write tables to file %s" + ", will be regenerated next time.\n", filename); + } else { + fwrite(*buf, size, 1, f); + fclose(f); + } + } else { + fprintf(stderr, "Reading tables from file %s\n", filename); + sizeread = fread(*buf, size, 1, f); + fclose(f); + if (sizeread != 1) { + fprintf(stderr, "Error reading table, stopping\n"); + goto getdata_error; + } + } + + return 0; + +getdata_error: + free(*buf); +getdata_error_nofree: + return 1; +}