introduce bitloops

This commit is contained in:
mike
2026-01-19 19:11:31 +01:00
parent 1fa112ab65
commit 5d186ae0ba
4 changed files with 56 additions and 89 deletions

View File

@@ -332,7 +332,6 @@ public record Export() {
for (long w = lemma & Lemma.LETTER_MASK; w != 0; w >>>= 5, i++) { for (long w = lemma & Lemma.LETTER_MASK; w != 0; w >>>= 5, i++) {
pos[i][(int) ((w & 31) - 1)].add(idx); pos[i][(int) ((w & 31) - 1)].add(idx);
} }
// for (i = 0; i < L; i++) entry.pos()[i][Lemma.byteAt(lemma, i) - 1].add(idx);
} }
for (int i = 2; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i); for (int i = 2; i < index.length; i++) if (index[i].words().size() <= 0) throw new RuntimeException("No words for length " + i);
return new Dict(Arrays.stream(index).map(i -> { return new Dict(Arrays.stream(index).map(i -> {

View File

@@ -43,10 +43,10 @@ public record SwedishGenerator() {
public static final int MIN_LEN = 3;//Config.MIN_LEN; public static final int MIN_LEN = 3;//Config.MIN_LEN;
public static final int MAX_TRIES_PER_SLOT = 700;//Config.MAX_TRIES_PER_SLOT; public static final int MAX_TRIES_PER_SLOT = 700;//Config.MAX_TRIES_PER_SLOT;
public static final int STACK_SIZE = 128; public static final int STACK_SIZE = 128;
public static final char C_DASH = '\0';
public static final byte DASH = (byte) C_DASH;
public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L; public static final long RANGE_0_SIZE = (long) SIZE_MIN_1 - 0L + 1L;
public static final long RANGE_0_624 = 624L - 0L + 1L; public static final long RANGE_0_624 = 624L - 0L + 1L;
static final int PICK_NOT_DONE = -1;
static final int PICK_DONE = 0;
public static boolean isLo(int n) { return (n & 64) == 0; } public static boolean isLo(int n) { return (n & 64) == 0; }
interface Bit1029 { interface Bit1029 {
@@ -57,40 +57,18 @@ public record SwedishGenerator() {
static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); } static void clear(long[] bits, int bitIndex) { bits[wordIndex(bitIndex)] &= ~(1L << bitIndex); }
} }
@AllArgsConstructor
public static class Pick {
public Slotinfo slot;
public int[] indices;
public int count;
}
//@formatter:off //@formatter:off
public static record Dict(DictEntry[] index, int length) { } @AllArgsConstructor @NoArgsConstructor static final class Assign { long w; }
@AllArgsConstructor @NoArgsConstructor static class Assign { long w; }
public static final class FillStats { public double simplicity; } public static final class FillStats { public double simplicity; }
@AllArgsConstructor public static final class Grid { public final byte[] g; public long lo, hi; }
public static record Dict(DictEntry[] index, int length) { }
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
//@formatter:on //@formatter:on
@AllArgsConstructor
public static class Grid {
public final byte[] g; public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX;
public long lo, hi; public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
} public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
public static record FillResult(boolean ok, long nodes, long backtracks, int lastMRV, long elapsed, FillStats stats) { }
public static record DictEntry(long[] words, long[][] posBitsets, int length, int numlong) { }
public static final long[] OFFSETS_D_IDX = Neighbors9x8.OFFSETS_D_IDX;
public static final long[] NBR8_PACKED_LO = Neighbors9x8.NBR8_PACKED_LO;
public static final long[] NBR8_PACKED_HI = Neighbors9x8.NBR8_PACKED_HI;
public static final long[] PATH_LO = Neighbors9x8.PATH_LO;
public static final long[] PATH_HI = Neighbors9x8.PATH_HI;
public static final Pick PICK_DONE = null;//new Pick(null, null, 0, true);
public static final Pick PICK_NOT_DONE = new Pick(null, null, 0);
public static final class Rng { public static final class Rng {
@@ -111,12 +89,11 @@ public record SwedishGenerator() {
public byte randint2bitByte() { public byte randint2bitByte() {
int r = nextU32() & 7; int r = nextU32() & 7;
if (r == 4) return (byte) 4; if (r == 4) return (byte) 4;
//if (r < 4) return (byte) r;
return (byte) (r & 3); return (byte) (r & 3);
} }
public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length /*- 0L*/ /*+ 1L*/)))]; } public <T> T rand(T[] p) { return p[(int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.length)))]; }
public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size() /*- 0L*/ /*+ 1L*/)))); } public <T> T rand(List<T> p) { return p.get((int) (((nextU32() & 0xFFFFFFFFL) % ((long) p.size())))); }
public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max /*- 0L*/ /*+ 1L*/))); } public int randint(int max) { return (int) (((nextU32() & 0xFFFFFFFFL) % ((long) max))); }
public int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); } public int randint0_SIZE() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_SIZE)); }
public int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); } public int randint0_624() { return (int) (((nextU32() & 0xFFFFFFFFL) % RANGE_0_624)); }
public double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; } public double nextFloat() { return (nextU32() & 0xFFFFFFFFL) / 4294967295.0; }
@@ -140,9 +117,7 @@ public record SwedishGenerator() {
static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); } static int length0(long word) { return ((63 - numberOfLeadingZeros(word & LETTER_MASK)) / 5); }
public static String asWord(long word, byte[] bytes) { public static String asWord(long word, byte[] bytes) {
int bi = 0; int bi = 0;
for (long w = word & LETTER_MASK; w != 0; w >>>= 5) { for (long w = word & LETTER_MASK; w != 0; w >>>= 5) bytes[bi++] = (byte) ((w & 31) | 64);
bytes[bi++] = (byte) ((w & 31) | 64); // neem laagste 5 bits
}
return new String(bytes, 0, bi, US_ASCII); return new String(bytes, 0, bi, US_ASCII);
} }
static int unpackIndex(long w) { return (int) (w >>> 40); } static int unpackIndex(long w) { return (int) (w >>> 40); }
@@ -164,7 +139,7 @@ public record SwedishGenerator() {
lo |= slot.lo; lo |= slot.lo;
hi |= slot.hi; hi |= slot.hi;
} }
return new Grid(new byte[SIZE], ~lo, ~hi /*& 0xFF*/); return new Grid(new byte[SIZE], ~lo, ~hi);
} }
} }
@@ -229,19 +204,18 @@ public record SwedishGenerator() {
public static FillResult fillMask(final Rng rng, final Slotinfo[] slots, public static FillResult fillMask(final Rng rng, final Slotinfo[] slots,
final Grid grid) { final Grid grid) {
val used = new long[2048]; val used = new long[2048];
val bitset = new long[2500]; val bitset = new long[2500];
val g = grid.g; val g = grid.g;
val TOTAL = slots.length; val TOTAL = slots.length;
val t0 = System.currentTimeMillis(); val t0 = System.currentTimeMillis();
val CARRIER = new Pick(null, null, 0);
class Solver { class Solver {
long nodes; long nodes;
long backtracks; long backtracks;
int lastMRV; long glo = grid.lo, ghi = grid.hi;
long lastLog = t0, glo = grid.lo, ghi = grid.hi; Slotinfo currentSlot;
Pick current = CARRIER; int[] currentIndices;
boolean placeWordDec(final long lo, final long hi, final long w) { boolean placeWordDec(final long lo, final long hi, final long w) {
int idx; int idx;
int bcHi = bitCount(hi); int bcHi = bitCount(hi);
@@ -274,19 +248,17 @@ public record SwedishGenerator() {
} }
return true; return true;
} }
void chooseMRV() { int chooseMRV() {
Slotinfo best = null; Slotinfo best = null;
for (int i = 0, count, count2 = -1, bestScore = -1, n = TOTAL; i < n; i++) { int count2 = -1, bestScore = -1;
for (int i = 0, n = TOTAL; i < n; i++) {
var s = slots[i]; var s = slots[i];
if (s.assign.w != X) continue; if (s.assign.w != X) continue;
var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi); var pattern = patternForSlot(glo, ghi, g, s.key, s.lo, s.hi);
var index = s.entry; var index = s.entry;
count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong); int count = pattern == X ? index.length : candidateCountForPattern(bitset, pattern, index.posBitsets, index.numlong);
if (count == 0) { if (count == 0) return PICK_NOT_DONE;
current = PICK_NOT_DONE;
return;
}
if (best == null if (best == null
|| count < count2 || count < count2
|| (count == count2 && s.score > bestScore)) { || (count == count2 && s.score > bestScore)) {
@@ -296,36 +268,30 @@ public record SwedishGenerator() {
if (count <= 1) break; if (count <= 1) break;
} }
} }
if (best == null) { if (best == null) return PICK_DONE;
current = PICK_DONE;
return;
}
var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi); var pattern = patternForSlot(glo, ghi, g, best.key, best.lo, best.hi);
var index = best.entry; currentSlot = best;
current = CARRIER; var index = best.entry;
current.slot = best; currentIndices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
current.count = index.length; return 1;
current.indices = pattern == X ? null : candidateInfoForPattern(bitset, pattern, index.posBitsets, index.numlong);
} }
boolean backtrack(int depth) { boolean backtrack(int depth) {
if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false; if (Thread.currentThread().isInterrupted() || (System.currentTimeMillis() - t0) > 20_000) return false;
nodes++; nodes++;
chooseMRV(); int status = chooseMRV();
var pick = current; if (status == PICK_DONE) return true;
if (pick == PICK_DONE) return true; if (status == PICK_NOT_DONE) {
if (pick.slot == null) {
backtracks++; backtracks++;
return false; return false;
} }
val info = pick.indices; val info = currentIndices;
lastMRV = pick.count; val s = currentSlot;
val inc = Slotinfo.increasing(s.key);
val s = pick.slot; val slo = s.lo;
val inc = Slotinfo.increasing(s.key); val shi = s.hi;
val slo = s.lo; val assign = s.assign;
val shi = s.hi; val words = s.entry.words;
val entry = s.entry;
long low, top; long low, top;
if (info != null && info.length > 0) { if (info != null && info.length > 0) {
var idxs = info; var idxs = info;
@@ -333,7 +299,7 @@ public record SwedishGenerator() {
var tries = Math.min(MAX_TRIES_PER_SLOT, L); var tries = Math.min(MAX_TRIES_PER_SLOT, L);
for (var t = 0; t < tries; t++) { for (var t = 0; t < tries; t++) {
var w = entry.words[idxs[rng.biasedIndexPow3(L - 1)]]; var w = words[idxs[rng.biasedIndexPow3(L - 1)]];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
@@ -345,9 +311,9 @@ public record SwedishGenerator() {
} }
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
s.assign.w = w; assign.w = w;
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
s.assign.w = X; assign.w = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);
glo = low; glo = low;
ghi = top; ghi = top;
@@ -356,11 +322,11 @@ public record SwedishGenerator() {
return false; return false;
} }
var N = entry.words.length; var N = words.length;
var tries = Math.min(MAX_TRIES_PER_SLOT, N); var tries = Math.min(MAX_TRIES_PER_SLOT, N);
for (var t = 0; t < tries; t++) { for (var t = 0; t < tries; t++) {
var w = entry.words[rng.biasedIndexPow3(N - 1)]; var w = words[rng.biasedIndexPow3(N - 1)];
var lemIdx = Lemma.unpackIndex(w); var lemIdx = Lemma.unpackIndex(w);
if (Bit1029.get(used, lemIdx)) continue; if (Bit1029.get(used, lemIdx)) continue;
low = glo; low = glo;
@@ -372,9 +338,9 @@ public record SwedishGenerator() {
} }
Bit1029.set(used, lemIdx); Bit1029.set(used, lemIdx);
s.assign.w = w; assign.w = w;
if (backtrack(depth + 1)) return true; if (backtrack(depth + 1)) return true;
s.assign.w = X; assign.w = X;
Bit1029.clear(used, lemIdx); Bit1029.clear(used, lemIdx);
glo = low; glo = low;
@@ -391,6 +357,7 @@ public record SwedishGenerator() {
grid.lo = solver.glo; grid.lo = solver.glo;
grid.hi = solver.ghi; grid.hi = solver.ghi;
return new FillResult(ok, solver.nodes, solver.backtracks, solver.lastMRV, System.currentTimeMillis() - t0, new FillStats()); return new FillResult(ok, solver.nodes, solver.backtracks, solver.currentSlot == null ? 0 : solver.currentSlot.entry.length, System.currentTimeMillis() - t0,
new FillStats());
} }
} }

View File

@@ -57,7 +57,7 @@ public class MainTest {
this.tries = 1; this.tries = 1;
this.verbose = false; this.verbose = false;
}}; }};
final Dict dict = DictData.DICT;//loadDict(opts.wordsPath); static final Dict dict = DictData.DICT;//loadDict(opts.wordsPath);
@Test @Test
void testExtractSlots() { void testExtractSlots() {
@@ -239,7 +239,6 @@ public class MainTest {
System.out.println("[DEBUG_LOG] Grid:"); System.out.println("[DEBUG_LOG] Grid:");
System.out.println(res.grid().renderHuman(res.clues().c())); System.out.println(res.grid().renderHuman(res.clues().c()));
System.out.println(res.grid().gridToString(res.clues().c())); System.out.println(res.grid().gridToString(res.clues().c()));
System.out.println(res.grid().renderHuman(res.clues().c()));
break; break;
} }
} }

View File

@@ -19,6 +19,8 @@ import static puzzle.SwedishGeneratorTest.Idx.IDX_0_0;
public class SwedishGeneratorTest { public class SwedishGeneratorTest {
public static final char C_DASH = '\0';
public static final byte DASH = (byte) C_DASH;
static Grid createEmpty() { return new Grid(new byte[SIZE], X, X); } static Grid createEmpty() { return new Grid(new byte[SIZE], X, X); }
record Context(long[] bitset) { record Context(long[] bitset) {