feat: add CUDA benchmark CLI and shared search algorithms
This commit is contained in:
parent
b4d677e83b
commit
12e4266950
12 changed files with 846 additions and 2 deletions
8
include/README.md
Normal file
8
include/README.md
Normal 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 重试,不代表普通候选生成已经包含拒绝尾部补扫。
|
||||
286
include/slime/solver_core.hpp
Normal file
286
include/slime/solver_core.hpp
Normal 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
|
||||
71
include/slime/upper_interval.hpp
Normal file
71
include/slime/upper_interval.hpp
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue