introduce bitloops

This commit is contained in:
mike
2026-01-14 20:04:14 +01:00
parent 70b2009723
commit 5849f543c5
3 changed files with 31 additions and 23 deletions

View File

@@ -50,9 +50,9 @@ public final class CsvIndexService
var parts = line.split(",", 5); var parts = line.split(",", 5);
var id = Integer.parseInt(parts[0].trim()); var id = Integer.parseInt(parts[0].trim());
var word = parts[1].trim(); var word = parts[1].trim();
if (!word.matches("^[A-Z]{2,8}$")) { /* if (!word.matches("^[A-Z]{2,8}$")) {
throw new RuntimeException("Invalid word:" + line); throw new RuntimeException("Invalid word:" + line);
} }*/
int score = Integer.parseInt(parts[2].trim()); int score = Integer.parseInt(parts[2].trim());
if (score < 1) { if (score < 1) {
@@ -133,8 +133,7 @@ public final class CsvIndexService
throw new IndexOutOfBoundsException("lineIndex=" + lineIndex + ", max=" + (local.length - 1)); throw new IndexOutOfBoundsException("lineIndex=" + lineIndex + ", max=" + (local.length - 1));
} }
var start = local[lineIndex]; long currentPos = local[lineIndex];
csvChannel.position(start);
// lees in blokjes (sneller dan 1 byte) tot newline // lees in blokjes (sneller dan 1 byte) tot newline
var buf = new byte[8192]; var buf = new byte[8192];
@@ -143,17 +142,15 @@ public final class CsvIndexService
while (true) { while (true) {
var bb = ByteBuffer.wrap(buf); var bb = ByteBuffer.wrap(buf);
var n = csvChannel.read(bb); var n = csvChannel.read(bb, currentPos);
if (n < 0) break; // EOF if (n < 0) break; // EOF
currentPos += n;
var end = n; var end = n;
for (var i = 0; i < end; i++) { for (var i = 0; i < end; i++) {
var b = buf[i]; var b = buf[i];
if (b == (byte) '\n') { if (b == (byte) '\n') {
// reposition kanaal op byte na newline
long back = (end - i - 1);
csvChannel.position(csvChannel.position() - back);
return new String(out, 0, total, StandardCharsets.UTF_8); return new String(out, 0, total, StandardCharsets.UTF_8);
} }
if (b == (byte) '\r') continue; if (b == (byte) '\r') continue;
@@ -194,10 +191,10 @@ public final class CsvIndexService
try (var ch = FileChannel.open(path, StandardOpenOption.READ)) { try (var ch = FileChannel.open(path, StandardOpenOption.READ)) {
var offs = new long[131072]; // start-capacity, groeit indien nodig var offs = new long[131072]; // start-capacity, groeit indien nodig
var c = 0; var c = 0;
offs[c++] = 0L; offs[c++] = 0;
var buf = ByteBuffer.allocateDirect(1 << 20); var buf = ByteBuffer.allocateDirect(1 << 20);
long pos = 0; int pos = 0;
while (true) { while (true) {
buf.clear(); buf.clear();
@@ -230,8 +227,7 @@ public final class CsvIndexService
public static long[] readIndex(Path in) throws IOException { public static long[] readIndex(Path in) throws IOException {
try (var dis = new DataInputStream(new BufferedInputStream(Files.newInputStream(in)))) { try (var dis = new DataInputStream(new BufferedInputStream(Files.newInputStream(in)))) {
var magic = dis.readInt(); if (dis.readInt() != MAGIC) throw new IOException("Not a LIDX file");
if (magic != MAGIC) throw new IOException("Not a LIDX file");
var version = dis.readInt(); var version = dis.readInt();
if (version != VERSION) throw new IOException("Unsupported version: " + version); if (version != VERSION) throw new IOException("Unsupported version: " + version);

View File

@@ -44,16 +44,16 @@ public class Main {
public static class Opts { public static class Opts {
public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis()); public int seed = (int) (System.nanoTime() ^ System.currentTimeMillis());
public int clueSize = 24; public int clueSize = 20;
public int pop = 40; public int pop = 40;
public int offspring = 60; public int offspring = 60;
public int gens = 700; public int gens = 500;
public String wordsPath = "nl_score_hints_v3.csv"; public String wordsPath = "nl_score_hints_v3.csv";
public double minSimplicity = 0; // 0 means no limit public double minSimplicity = 0; // 0 means no limit
public int threads = Math.max(1, Runtime.getRuntime().availableProcessors()); public int threads = Math.max(1, Runtime.getRuntime().availableProcessors());
public int tries = threads; public int tries = threads;
public boolean reindex = false; public boolean reindex = false;
public boolean verbose = false; public boolean verbose = true;
} }
@@ -206,7 +206,7 @@ public class Main {
--clues 18 --clues 18
--pop 40 --pop 40
--offspring 60 --offspring 60
--gens 700 --gens 500
--words nl_score_hints.csv --words nl_score_hints.csv
--min-simplicity 0 (no limit) --min-simplicity 0 (no limit)
--threads %d --threads %d
@@ -286,9 +286,10 @@ public class Main {
try { try {
// Keep at least some tasks in flight // Keep at least some tasks in flight
final var service = CsvIndexService.SC.get();
for (int i = 0; i < opts.threads; i++) { for (int i = 0; i < opts.threads; i++) {
final int attempt = ++submitted; final int attemptIdx = ++submitted;
completionService.submit(() -> attempt(new Rng(opts.seed + attempt), dict, opts)); completionService.submit(() -> ScopedValue.where(CsvIndexService.SC, service).call(() -> attempt(new Rng(opts.seed + attemptIdx), dict, opts)));
} }
while (System.currentTimeMillis() < deadline) { while (System.currentTimeMillis() < deadline) {
@@ -304,8 +305,8 @@ public class Main {
// Submit another task if we still have time // Submit another task if we still have time
if (System.currentTimeMillis() < deadline) { if (System.currentTimeMillis() < deadline) {
final int attempt = ++submitted; final int attemptIdx = ++submitted;
completionService.submit(() -> attempt(new Rng(opts.seed + attempt), dict, opts)); completionService.submit(() -> ScopedValue.where(CsvIndexService.SC, service).call(() -> attempt(new Rng(opts.seed + attemptIdx), dict, opts)));
} }
} }
if (resFinal == null) warn("status : UNSOLVED (timeout)"); if (resFinal == null) warn("status : UNSOLVED (timeout)");
@@ -316,6 +317,11 @@ public class Main {
warn("status : ERROR (" + e.getMessage() + ")"); warn("status : ERROR (" + e.getMessage() + ")");
} finally { } finally {
executor.shutdownNow(); executor.shutdownNow();
try {
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} }
} else { } else {
@@ -369,9 +375,11 @@ public class Main {
} }
} }
static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) { static PuzzleResult _attempt(Rng rng, Dict dict, Opts opts) {
long t0 = System.currentTimeMillis();
TOTAL_ATTEMPTS.incrementAndGet(); TOTAL_ATTEMPTS.incrementAndGet();
var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty()); var swe = new SwedishGenerator(rng, new int[STACK_SIZE], Clues.createEmpty());
var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, Math.max(opts.offspring, (int) Math.floor(1.5 * opts.pop))); var mask = swe.generateMask(opts.clueSize, opts.pop, opts.gens, opts.offspring);
if (mask == null) return null;
var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid()); var filled = fillMask(rng, extractSlots(mask, dict.index()), mask.toGrid());
@@ -387,10 +395,11 @@ public class Main {
var status = filled.ok() ? "SUCCESS" : "FAILED"; var status = filled.ok() ? "SUCCESS" : "FAILED";
var simplicity = String.format(Locale.ROOT, "%.2f", filled.stats().simplicity); var simplicity = String.format(Locale.ROOT, "%.2f", filled.stats().simplicity);
var nps = (int) (filled.stats().nodes / Math.max(0.001, filled.stats().seconds)); var nps = (int) (filled.stats().nodes / Math.max(0.001, filled.stats().seconds));
var totalTime = (System.currentTimeMillis() - t0) / 1000.0;
System.out.printf(Locale.ROOT, System.out.printf(Locale.ROOT,
"[ATTEMPT] thread=%s | status=%s | nodes=%d | backtracks=%d | nps=%d | simplicity=%s | time=%.1fs%n", "[ATTEMPT] thread=%s | status=%s | nodes=%d | backtracks=%d | nps=%d | simplicity=%s | time=%.1fs%n",
name, status, filled.stats().nodes, filled.stats().backtracks, nps, simplicity, filled.stats().seconds name, status, filled.stats().nodes, filled.stats().backtracks, nps, simplicity, totalTime
); );
if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) { if (filled.ok() && (opts.minSimplicity <= 0 || filled.stats().simplicity >= opts.minSimplicity)) {

View File

@@ -687,6 +687,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize); if (Main.VERBOSE) System.out.println("generateMask init pop: " + popSize + " clueSize: " + clueSize);
var pop = new ArrayList<GridAndFit>(); var pop = new ArrayList<GridAndFit>();
for (var i = 0; i < popSize; i++) { for (var i = 0; i < popSize; i++) {
if (Thread.currentThread().isInterrupted()) return null;
pop.add(new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180))); pop.add(new GridAndFit(hillclimb(randomMask(clueSize), clueSize, 180)));
} }
@@ -695,6 +696,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
var children = new ArrayList<GridAndFit>(); var children = new ArrayList<GridAndFit>();
for (var k = 0; k < offspring; k++) { for (var k = 0; k < offspring; k++) {
if (Thread.currentThread().isInterrupted()) break;
var p1 = pop.get(rng.randint(0, pop.size() - 1)); var p1 = pop.get(rng.randint(0, pop.size() - 1));
var p2 = pop.get(rng.randint(0, pop.size() - 1)); var p2 = pop.get(rng.randint(0, pop.size() - 1));
var child = crossover(p1.grid, p2.grid); var child = crossover(p1.grid, p2.grid);
@@ -720,6 +722,7 @@ public record SwedishGenerator(Rng rng, int[] stack, Clues cache) {
if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit()); if (Main.VERBOSE && (gen & 15) == 15) System.out.println(" gen " + gen + "/" + gens + " bestFitness=" + pop.get(0).fit());
} }
if (pop.isEmpty()) return null;
GridAndFit best = pop.get(0); GridAndFit best = pop.get(0);
for (int i = 1; i < pop.size(); i++) { for (int i = 1; i < pop.size(); i++) {
var x = pop.get(i); var x = pop.get(i);