feat: add CUDA benchmark CLI and shared search algorithms

This commit is contained in:
Yaossg 2026-10-11 12:27:26 +08:00
parent b4d677e83b
commit 12e4266950
12 changed files with 846 additions and 2 deletions

8
include/README.md Normal file
View file

@ -0,0 +1,8 @@
# 共用算法头文件
- `slime/solver_core.hpp`:Java 整数回绕、48 位随机数、精确史莱姆判定、低位约束和原高位求解器。
- `slime/upper_interval.hpp`:输出差必要条件与高位区间求交,用于实心图案的普通候选分支。
CLI 直接依赖这里。`research/slime_solver_core.hpp` 和原实验目录的 `interval_solver.hpp` 仅转发包含,以便已有研究程序、教学代码和历史复现命令继续工作。
本次只迁移实现位置,保留现有命名空间及算法语义。精确验证支持 Java 重试,不代表普通候选生成已经包含拒绝尾部补扫。

View file

@ -0,0 +1,286 @@
#pragma once
#include <cstddef>
#include <cstdint>
#if defined(__CUDACC__)
#define SLIME_HD __host__ __device__
#define SLIME_INLINE __forceinline__
#else
#define SLIME_HD
#define SLIME_INLINE inline
#endif
namespace slime_research {
constexpr uint64_t kJavaMultiplier = 0x5DEECE66DULL;
constexpr uint64_t kJavaAddend = 0xBULL;
constexpr uint64_t kMask48 = (1ULL << 48) - 1ULL;
constexpr uint64_t kSlimeXor = 987234911ULL;
constexpr uint64_t kCombinedXor = kSlimeXor ^ kJavaMultiplier;
constexpr int kLowerBits = 35;
constexpr int kUpperBits = 13;
constexpr uint32_t kUpperSize = 1U << kUpperBits;
constexpr uint32_t kUpperMask = kUpperSize - 1U;
constexpr uint32_t kHalfDeltaInverse = 4965U;
constexpr int kMaxPatternCells = 64;
constexpr uint32_t kX2Coeff = 4987142U;
constexpr uint32_t kXCoeff = 5947611U;
constexpr uint32_t kZ2Coeff = 4392871U;
constexpr uint32_t kZCoeff = 389711U;
enum Requirement : uint8_t {
kNonSlime = 0,
kSlime = 1,
};
struct CellTerm {
uint64_t term;
uint8_t requirement;
};
struct UpperEvent {
uint16_t threshold;
uint8_t before;
};
SLIME_HD SLIME_INLINE uint64_t javaIntAsLongBits(uint32_t value) {
return static_cast<uint64_t>(static_cast<int64_t>(static_cast<int32_t>(value)));
}
SLIME_HD SLIME_INLINE uint64_t coordinateTerm(int32_t chunkX, int32_t chunkZ) {
const uint32_t x = static_cast<uint32_t>(chunkX);
const uint32_t z = static_cast<uint32_t>(chunkZ);
return javaIntAsLongBits(x * x * kX2Coeff) +
javaIntAsLongBits(x * kXCoeff) +
javaIntAsLongBits(z * z) * kZ2Coeff +
javaIntAsLongBits(z * kZCoeff);
}
SLIME_HD SLIME_INLINE uint64_t coordinateXTerm(int32_t chunkX) {
const uint32_t x = static_cast<uint32_t>(chunkX);
return javaIntAsLongBits(x * x * kX2Coeff) +
javaIntAsLongBits(x * kXCoeff);
}
SLIME_HD SLIME_INLINE uint64_t coordinateZTerm(int32_t chunkZ) {
const uint32_t z = static_cast<uint32_t>(chunkZ);
return javaIntAsLongBits(z * z) * kZ2Coeff +
javaIntAsLongBits(z * kZCoeff);
}
SLIME_HD SLIME_INLINE uint64_t nextStateFromTerm(uint64_t seed48, uint64_t term) {
uint64_t state = ((seed48 + term) ^ kCombinedXor) & kMask48;
return (state * kJavaMultiplier + kJavaAddend) & kMask48;
}
SLIME_HD SLIME_INLINE bool exactIsSlimeFromTerm(uint64_t seed48, uint64_t term) {
uint64_t state = ((seed48 + term) ^ kCombinedXor) & kMask48;
for (;;) {
state = (state * kJavaMultiplier + kJavaAddend) & kMask48;
const uint32_t bits = static_cast<uint32_t>(state >> 17);
const uint32_t value = bits % 10U;
if (bits - value + 9U <= 0x7FFFFFFFU) {
return value == 0U;
}
}
}
SLIME_HD SLIME_INLINE bool verifyPattern(
uint64_t seed48,
const CellTerm* cells,
int cellCount
) {
for (int i = 0; i < cellCount; ++i) {
const bool slime = exactIsSlimeFromTerm(seed48, cells[i].term);
if (slime != (cells[i].requirement == kSlime)) {
return false;
}
}
return true;
}
SLIME_HD SLIME_INLINE bool partialPatternPossible(
uint64_t seedPrefix,
int knownBits,
const CellTerm* cells,
int cellCount
) {
const uint64_t mask = (1ULL << knownBits) - 1ULL;
seedPrefix &= mask;
for (int i = 0; i < cellCount; ++i) {
uint64_t state = (((seedPrefix + cells[i].term) ^ kCombinedXor) *
kJavaMultiplier + kJavaAddend) & mask;
const uint64_t knownOutput = state >> 17;
if (cells[i].requirement == kSlime && (knownOutput & 1ULL) != 0ULL) {
return false;
}
}
return true;
}
SLIME_HD SLIME_INLINE uint32_t firstWithResidue(
uint32_t start,
uint32_t residue
) {
return start + ((residue + 5U - (start % 5U)) % 5U);
}
SLIME_HD SLIME_INLINE bool upper13HasCandidate(
uint64_t lower35,
const CellTerm* cells,
int cellCount
) {
UpperEvent events[kMaxPatternCells];
uint16_t residueCounts[5] = {0, 0, 0, 0, 0};
int eventCount = 0;
lower35 &= (1ULL << kLowerBits) - 1ULL;
for (int i = 0; i < cellCount; ++i) {
if (cells[i].requirement != kSlime) continue;
const uint32_t output = static_cast<uint32_t>(
nextStateFromTerm(lower35, cells[i].term) >> 17);
const uint32_t low17 = output & ((1U << 17) - 1U);
if ((low17 & 1U) != 0U) return false;
const uint32_t upper14 = output >> 17;
const uint32_t parity = upper14 & 1U;
const uint32_t v0 = (upper14 - parity) >> 1;
const uint32_t wanted = (3U * ((5U - ((low17 >> 1) % 5U) +
5U - parity) % 5U)) % 5U;
const uint32_t before = (wanted + 5U - (v0 % 5U)) % 5U;
const UpperEvent event{static_cast<uint16_t>(kUpperSize - v0),
static_cast<uint8_t>(before)};
int insert = eventCount;
while (insert > 0 && events[insert - 1].threshold > event.threshold) {
events[insert] = events[insert - 1];
--insert;
}
events[insert] = event;
++eventCount;
++residueCounts[before];
}
if (eventCount == 0) return true;
uint32_t start = 0;
int index = 0;
for (;;) {
const uint32_t stop = index < eventCount ? events[index].threshold : kUpperSize;
for (uint32_t residue = 0; residue < 5; ++residue) {
if (residueCounts[residue] == eventCount &&
firstWithResidue(start, residue) < stop) {
return true;
}
}
if (index == eventCount) return false;
const uint32_t threshold = events[index].threshold;
while (index < eventCount && events[index].threshold == threshold) {
const uint32_t before = events[index].before;
--residueCounts[before];
++residueCounts[(before + 2U) % 5U];
++index;
}
start = threshold;
}
}
template <typename Emit>
SLIME_HD SLIME_INLINE uint32_t solveUpper13(
uint64_t lower35,
const CellTerm* cells,
int cellCount,
Emit& emit
) {
UpperEvent events[kMaxPatternCells];
uint16_t residueCounts[5] = {0, 0, 0, 0, 0};
int eventCount = 0;
lower35 &= (1ULL << kLowerBits) - 1ULL;
for (int i = 0; i < cellCount; ++i) {
if (cells[i].requirement != kSlime) {
continue;
}
const uint32_t output = static_cast<uint32_t>(
nextStateFromTerm(lower35, cells[i].term) >> 17
);
const uint32_t low17 = output & ((1U << 17) - 1U);
if ((low17 & 1U) != 0U) {
return 0;
}
const uint32_t upper14 = output >> 17;
const uint32_t parity = upper14 & 1U;
const uint32_t v0 = (upper14 - parity) >> 1;
const uint32_t wanted = (3U * ((5U - ((low17 >> 1) % 5U) +
5U - parity) % 5U)) % 5U;
const uint32_t before = (wanted + 5U - (v0 % 5U)) % 5U;
UpperEvent event{
static_cast<uint16_t>(kUpperSize - v0),
static_cast<uint8_t>(before),
};
int insert = eventCount;
while (insert > 0 && events[insert - 1].threshold > event.threshold) {
events[insert] = events[insert - 1];
--insert;
}
events[insert] = event;
++eventCount;
++residueCounts[before];
}
uint32_t emitted = 0;
if (eventCount == 0) {
for (uint32_t n = 0; n < kUpperSize; ++n) {
const uint32_t m = (kHalfDeltaInverse * n) & kUpperMask;
const uint64_t seed = lower35 + (uint64_t(m) << kLowerBits);
if (verifyPattern(seed, cells, cellCount)) {
emit(seed);
++emitted;
}
}
return emitted;
}
uint32_t start = 0;
int index = 0;
for (;;) {
const uint32_t stop = index < eventCount ? events[index].threshold : kUpperSize;
int commonResidue = -1;
for (int residue = 0; residue < 5; ++residue) {
if (residueCounts[residue] == eventCount) {
commonResidue = residue;
break;
}
}
if (commonResidue >= 0 && start < stop) {
for (uint32_t n = firstWithResidue(start, uint32_t(commonResidue));
n < stop;
n += 5U) {
const uint32_t m = (kHalfDeltaInverse * n) & kUpperMask;
const uint64_t seed = lower35 + (uint64_t(m) << kLowerBits);
if (verifyPattern(seed, cells, cellCount)) {
emit(seed);
++emitted;
}
}
}
if (index == eventCount) {
break;
}
const uint32_t threshold = events[index].threshold;
while (index < eventCount && events[index].threshold == threshold) {
const uint32_t before = events[index].before;
--residueCounts[before];
++residueCounts[(before + 2U) % 5U];
++index;
}
start = threshold;
}
return emitted;
}
} // namespace slime_research
#undef SLIME_HD
#undef SLIME_INLINE

View file

@ -0,0 +1,71 @@
#pragma once
#include "solver_core.hpp"
#if defined(__CUDACC__)
#define FS_HD __host__ __device__
#define FS_INLINE __forceinline__
#else
#define FS_HD
#define FS_INLINE inline
#endif
namespace full_search_experiment {
namespace sr = slime_research;
// Solid patterns only. Each surviving residue has a single interval.
// Keeping just the anchor's two residues makes intersection constant-space.
template<class Emit>
FS_HD FS_INLINE void solveIntervals(uint64_t low, const sr::CellTerm* cells,
int count, Emit& emit) {
low &= (1ULL << 35) - 1;
uint32_t q = uint32_t(sr::nextStateFromTerm(low, cells[0].term) >> 17);
if (q & 1U) return;
uint32_t b = q >> 18;
uint32_t r = q & ((1U << 18) - 1);
uint32_t threshold = 8192 - b;
uint32_t residue[2] = {(r % 5 + 5 - b % 5) % 5, 0};
residue[1] = (residue[0] + 2) % 5;
uint32_t lo[2] = {0, threshold}, hi[2] = {threshold, 8192};
unsigned active = 0;
for (int k = 0; k < 2; ++k)
if (sr::firstWithResidue(lo[k], residue[k]) < hi[k]) active |= 1U << k;
for (int i = 1; i < count && active; ++i) {
q = uint32_t(sr::nextStateFromTerm(low, cells[i].term) >> 17);
if (q & 1U) return;
b = q >> 18;
r = q & ((1U << 18) - 1);
threshold = 8192 - b;
const uint32_t before = (r % 5 + 5 - b % 5) % 5;
const uint32_t after = (before + 2) % 5;
for (int k = 0; k < 2; ++k) if (active & (1U << k)) {
if (residue[k] == before) hi[k] = hi[k] < threshold ? hi[k] : threshold;
else if (residue[k] == after) lo[k] = lo[k] > threshold ? lo[k] : threshold;
else { active &= ~(1U << k); continue; }
if (sr::firstWithResidue(lo[k], residue[k]) >= hi[k]) active &= ~(1U << k);
}
}
for (int k = 0; k < 2; ++k) if (active & (1U << k)) {
for (uint32_t t = sr::firstWithResidue(lo[k], residue[k]); t < hi[k]; t += 5) {
const uint64_t seed = low + (uint64_t((4965U * t) & 8191U) << 35);
if (sr::verifyPattern(seed, cells, count)) emit(seed);
}
}
}
// A cheap necessary condition before either exact high-bit solver.
// Adding the common high-bit shift can wrap q_i-q_0 once: delta mod 10 is 0 or 8.
FS_HD FS_INLINE bool differenceFilter(uint64_t low, const sr::CellTerm* cells, int count) {
const uint32_t q0 = uint32_t(sr::nextStateFromTerm(low, cells[0].term) >> 17);
for (int i = 1; i < count; ++i) {
const uint32_t qi = uint32_t(sr::nextStateFromTerm(low, cells[i].term) >> 17);
const uint32_t delta = (qi - q0) & 0x7fffffffU;
const uint32_t r = delta % 10;
if (r != 0 && r != 8) return false;
}
return true;
}
}
#undef FS_HD
#undef FS_INLINE