Transformer

This commit is contained in:
Efim Beshmenev
2026-07-12 21:48:44 +03:00
parent ffd46f89e3
commit 9e3dd7ce8b
11614 changed files with 16818 additions and 4458 deletions
+6 -4
View File
@@ -99,7 +99,7 @@
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\TransformerRanker;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
@@ -117,7 +117,7 @@
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\TransformerRanker;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
<BufferSecurityCheck>false</BufferSecurityCheck>
@@ -139,7 +139,7 @@
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\TransformerRanker;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
@@ -157,7 +157,7 @@
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\src\NeighborlyCore;$(ProjectDir)..\..\src\Checkpoint;$(ProjectDir)..\..\src\SearchArchive;$(ProjectDir)..\..\src\OnlineSurrogate;$(ProjectDir)..\..\src\TrainingArchive;$(ProjectDir)..\..\src\TransformerRanker;$(ProjectDir)..\..\src\CudaSearch;$(ProjectDir)..\..\external\eigen-3.4.0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
<BufferSecurityCheck>false</BufferSecurityCheck>
@@ -179,6 +179,7 @@
<ClCompile Include="..\..\src\SearchArchive\SearchArchive.cpp" />
<ClCompile Include="..\..\src\OnlineSurrogate\OnlineSurrogate.cpp" />
<ClCompile Include="..\..\src\TrainingArchive\TrainingArchive.cpp" />
<ClCompile Include="..\..\src\TransformerRanker\TransformerRanker.cpp" />
<ClCompile Include="..\..\src\NeighborlyCore\util.cpp" />
</ItemGroup>
<ItemGroup Condition="'$(CudaToolkitAvailable)'!='true'">
@@ -199,6 +200,7 @@
<ClInclude Include="..\..\src\SearchArchive\SearchArchive.h" />
<ClInclude Include="..\..\src\OnlineSurrogate\OnlineSurrogate.h" />
<ClInclude Include="..\..\src\TrainingArchive\TrainingArchive.h" />
<ClInclude Include="..\..\src\TransformerRanker\TransformerRanker.h" />
<ClInclude Include="..\..\src\CudaSearch\cuda_search.h" />
<ClInclude Include="..\..\src\NeighborlyCore\util.h" />
<ClInclude Include="..\..\src\NeighborlyCore\wide_real.h" />
@@ -33,6 +33,9 @@
<ClCompile Include="..\..\src\TrainingArchive\TrainingArchive.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\TransformerRanker\TransformerRanker.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\src\CudaSearch\cuda_search_stub.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -59,6 +62,9 @@
<ClInclude Include="..\..\src\TrainingArchive\TrainingArchive.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\TransformerRanker\TransformerRanker.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\src\CudaSearch\cuda_search.h">
<Filter>Header Files</Filter>
</ClInclude>
+490 -50
View File
@@ -6,6 +6,7 @@
#include "SearchArchive.h"
#include "OnlineSurrogate.h"
#include "TrainingArchive.h"
#include "TransformerRanker.h"
#include "cuda_search.h"
#include <iostream>
#include <sstream>
@@ -57,6 +58,7 @@ constexpr std::uint32_t MIN_COMPATIBLE_ARCHIVE_OBJECTIVE_VERSION = 3;
constexpr int CUDA_SESSION_CACHE_LIMIT = NUM_TOPOLOGIES;
constexpr std::uint64_t TRAINING_CACHE_LIMIT_BYTES = 200000000000ULL;
constexpr std::uint64_t NEURAL_MODEL_BUDGET_PER_TOPOLOGY = 4ULL * 1024ULL * 1024ULL;
constexpr std::uint64_t TRANSFORMER_MODEL_BUDGET = 8ULL * 1024ULL * 1024ULL;
constexpr std::uint32_t TRAINING_ARCHIVE_SCHEMA_VERSION = 3;
struct StudyOptions {
@@ -2238,6 +2240,8 @@ struct GlobalMetrics {
double g_global_degeneracy_weight = 0.01;
std::string g_search_device_id = "CPU";
bool g_transformer_active = false;
std::string g_transformer_model_id = "none";
std::uint64_t topology_fingerprint() {
std::uint64_t hash = 1469598103934665603ULL;
@@ -2343,6 +2347,10 @@ struct GlobalTopologyState {
std::uint64_t neural_samples_added = 0;
std::uint64_t neural_training_steps = 0;
std::uint64_t neural_seed_count = 0;
std::uint64_t transformer_candidates_scored = 0;
std::uint64_t transformer_candidates_selected = 0;
std::uint64_t transformer_control_seeds = 0;
std::uint64_t transformer_invalid_predictions = 0;
std::uint64_t training_verified_records = 0;
std::uint64_t training_fp32_records = 0;
std::uint64_t training_seed_records = 0;
@@ -2897,7 +2905,8 @@ szilassi::training::CommonContext make_training_context(
context.topology_fingerprint = topology_fingerprint();
context.algorithm_fingerprint = training_hash_text(
options.use_cuda
? "hybrid-quality-diversity-neural-v3-cuda-fp32"
? "hybrid-quality-diversity-neural-v3-transformer-v1-cuda-fp32:" +
g_transformer_model_id
: "cpu-parallel-simulated-annealing-double");
static const std::uint64_t build_fingerprint = training_hash_text(
std::string(__DATE__) + " " + __TIME__);
@@ -3036,14 +3045,44 @@ std::filesystem::path neural_model_relative_path(int topology) {
"_s" + std::to_string(GLOBAL_NEURAL_SCHEMA_VERSION) + ".szonn");
}
std::filesystem::path transformer_model_relative_path() {
return std::filesystem::path("neural") / "transformer" / "current.sztf";
}
std::string transformer_digest_hex(
const std::array<std::uint8_t, szilassi::transformer::kTrainingDigestBytes>& digest
) {
static constexpr char digits[] = "0123456789abcdef";
std::string result;
result.reserve(digest.size() * 2);
for (const std::uint8_t value : digest) {
result.push_back(digits[value >> 4]);
result.push_back(digits[value & 0x0fU]);
}
return result;
}
std::string transformer_model_id(
const szilassi::transformer::Metadata& metadata
) {
std::ostringstream out;
out << transformer_digest_hex(metadata.training_digest)
<< "-s" << metadata.training_seed
<< "-p" << std::hex << std::setw(8) << std::setfill('0')
<< metadata.payload_crc32;
return out.str();
}
bool scan_neural_cache_bytes(
const std::filesystem::path& root,
std::uint64_t& bytes,
std::array<std::uint64_t, NUM_TOPOLOGIES>& final_snapshot_bytes,
std::uint64_t& transformer_snapshot_bytes,
std::string& error
) {
bytes = 0;
final_snapshot_bytes.fill(0);
transformer_snapshot_bytes = 0;
std::error_code exists_error;
const bool exists = std::filesystem::exists(root, exists_error);
if (exists_error) {
@@ -3088,6 +3127,9 @@ bool scan_neural_cache_bytes(
break;
}
}
if (relative == transformer_model_relative_path()) {
transformer_snapshot_bytes = size;
}
}
iterator.increment(iterator_error);
}
@@ -4328,6 +4370,9 @@ struct HybridSeedPool {
std::vector<GlobalMetrics> injected_metrics;
std::vector<NeuralPrediction> behavior_predictions;
std::vector<std::uint8_t> neural_guided;
// 0 = ordinary/MLP-only, 1 = selected by transformer rank, 2 =
// score-independent control retained beside transformer-ranked seeds.
std::vector<std::uint8_t> transformer_selection_mode;
std::vector<int> plane_actions;
std::vector<int> move_actions;
std::vector<int> scale_actions;
@@ -4340,6 +4385,10 @@ struct HybridSeedPool {
move_sampling_probabilities;
std::vector<std::array<float, szilassi::surrogate::kScaleCount>>
scale_sampling_probabilities;
std::uint64_t transformer_candidates_scored = 0;
std::uint64_t transformer_candidates_selected = 0;
std::uint64_t transformer_control_seeds = 0;
std::uint64_t transformer_invalid_predictions = 0;
};
NeuralInput make_neural_input(
@@ -4466,7 +4515,9 @@ void apply_neural_repair_move(
HybridSeedPool make_hybrid_seed_pool(
GlobalTopologyState& topology_state,
std::uint64_t seed,
int maximum_states
int maximum_states,
const szilassi::transformer::TransformerRanker* transformer_ranker = nullptr,
const szilassi::transformer::SearchBudget& transformer_budget = {}
) {
HybridSeedPool pool;
if (maximum_states <= 0) {
@@ -4479,6 +4530,7 @@ HybridSeedPool make_hybrid_seed_pool(
pool.injected_metrics.reserve(static_cast<std::size_t>(maximum_states));
pool.behavior_predictions.reserve(static_cast<std::size_t>(maximum_states));
pool.neural_guided.reserve(static_cast<std::size_t>(maximum_states));
pool.transformer_selection_mode.reserve(static_cast<std::size_t>(maximum_states));
pool.plane_actions.reserve(static_cast<std::size_t>(maximum_states));
pool.move_actions.reserve(static_cast<std::size_t>(maximum_states));
pool.scale_actions.reserve(static_cast<std::size_t>(maximum_states));
@@ -4489,7 +4541,9 @@ HybridSeedPool make_hybrid_seed_pool(
pool.move_sampling_probabilities.reserve(static_cast<std::size_t>(maximum_states));
pool.scale_sampling_probabilities.reserve(static_cast<std::size_t>(maximum_states));
auto append = [&](const VectorXd& injected_x,
auto append_to = [&](HybridSeedPool& destination,
int destination_limit,
const VectorXd& injected_x,
const GlobalMetrics& injected_metrics,
const VectorXd& training_x,
const NeuralInput& training_input,
@@ -4507,8 +4561,9 @@ HybridSeedPool make_hybrid_seed_pool(
const std::array<float, szilassi::surrogate::kMoveCount>&
move_sampling_probabilities,
const std::array<float, szilassi::surrogate::kScaleCount>&
scale_sampling_probabilities) {
if (static_cast<int>(pool.states.size()) >= maximum_states ||
scale_sampling_probabilities,
std::uint8_t transformer_selection_mode) {
if (static_cast<int>(destination.states.size()) >= destination_limit ||
injected_x.size() != GLOBAL_PLANE_VALUE_COUNT ||
training_x.size() != GLOBAL_PLANE_VALUE_COUNT ||
!injected_x.allFinite() || !std::isfinite(injected_metrics.energy) ||
@@ -4523,22 +4578,23 @@ HybridSeedPool make_hybrid_seed_pool(
training_value.values[static_cast<std::size_t>(component)] =
static_cast<float>(training_x[component]);
}
pool.states.push_back(value);
pool.training_states.push_back(training_value);
pool.inputs.push_back(training_input);
pool.metrics.push_back(training_metrics);
pool.injected_metrics.push_back(injected_metrics);
pool.behavior_predictions.push_back(behavior_prediction);
pool.neural_guided.push_back(guided ? 1u : 0u);
pool.plane_actions.push_back(plane_action);
pool.move_actions.push_back(move_action);
pool.scale_actions.push_back(scale_action);
pool.plane_propensities.push_back(static_cast<float>(plane_propensity));
pool.move_propensities.push_back(static_cast<float>(move_propensity));
pool.scale_propensities.push_back(static_cast<float>(scale_propensity));
pool.plane_sampling_probabilities.push_back(plane_sampling_probabilities);
pool.move_sampling_probabilities.push_back(move_sampling_probabilities);
pool.scale_sampling_probabilities.push_back(scale_sampling_probabilities);
destination.states.push_back(value);
destination.training_states.push_back(training_value);
destination.inputs.push_back(training_input);
destination.metrics.push_back(training_metrics);
destination.injected_metrics.push_back(injected_metrics);
destination.behavior_predictions.push_back(behavior_prediction);
destination.neural_guided.push_back(guided ? 1u : 0u);
destination.transformer_selection_mode.push_back(transformer_selection_mode);
destination.plane_actions.push_back(plane_action);
destination.move_actions.push_back(move_action);
destination.scale_actions.push_back(scale_action);
destination.plane_propensities.push_back(static_cast<float>(plane_propensity));
destination.move_propensities.push_back(static_cast<float>(move_propensity));
destination.scale_propensities.push_back(static_cast<float>(scale_propensity));
destination.plane_sampling_probabilities.push_back(plane_sampling_probabilities);
destination.move_sampling_probabilities.push_back(move_sampling_probabilities);
destination.scale_sampling_probabilities.push_back(scale_sampling_probabilities);
};
// Base seeds do not sample an action. A zero distribution records that
@@ -4565,7 +4621,9 @@ HybridSeedPool make_hybrid_seed_pool(
x[component] = static_cast<double>(value.values[static_cast<std::size_t>(component)]);
}
const GlobalMetrics metrics = evaluate_global_state(x, true, scratch);
append(
append_to(
pool,
maximum_states,
x,
metrics,
x,
@@ -4581,7 +4639,8 @@ HybridSeedPool make_hybrid_seed_pool(
1.0,
no_plane_sampling_distribution,
no_move_sampling_distribution,
no_scale_sampling_distribution);
no_scale_sampling_distribution,
0);
}
if (guided_target == 0) {
@@ -4593,6 +4652,9 @@ HybridSeedPool make_hybrid_seed_pool(
GlobalMetrics metrics;
NeuralInput input{};
NeuralPrediction prediction{};
NeuralPrediction control_prediction{};
bool learned_prediction = false;
bool control_learned_prediction = false;
double acquisition = 0.0;
};
std::vector<Anchor> anchors;
@@ -4619,27 +4681,117 @@ HybridSeedPool make_hybrid_seed_pool(
0.0});
}
const bool model_ready = topology_state.neural != nullptr &&
const bool online_model_ready = topology_state.neural != nullptr &&
topology_state.neural->replay_size() >= 128;
const bool transformer_ready = transformer_ranker != nullptr &&
transformer_ranker->ready();
auto default_prediction = []() {
NeuralPrediction prediction;
prediction.plane_probabilities.fill(
1.0f / static_cast<float>(cuda_search::kPlaneCount));
prediction.move_probabilities.fill(
1.0f / static_cast<float>(szilassi::surrogate::kMoveCount));
prediction.scale_probabilities = {{0.25f, 0.55f, 0.20f}};
// This is a valid neutral policy/value fallback, not a failed model
// inference. Keeping it finite also prevents transformer value heads
// trained on injected proposals from leaking into anchor telemetry.
prediction.finite = true;
return prediction;
};
auto transformer_as_neural = [](const szilassi::transformer::Prediction& source) {
NeuralPrediction result;
result.improvement_logit = source.improvement_logit;
result.improvement_probability = source.improvement_probability;
result.expected_defect_gain = source.expected_defect_gain;
result.uncertainty = source.improvement_probability_variance;
result.plane_probabilities = source.plane_probabilities;
result.move_probabilities = source.move_probabilities;
result.scale_probabilities = source.scale_probabilities;
result.finite = source.finite;
return result;
};
auto blend_predictions = [](const NeuralPrediction& local,
const NeuralPrediction& global) {
if (!global.finite) return local;
if (!local.finite) return global;
NeuralPrediction blended;
constexpr float global_weight = 0.65f;
constexpr float local_weight = 1.0f - global_weight;
// Transformer value heads are trained on injected proposals. Anchor
// inference is used only for its policy heads; keep the local online
// value estimate instead of evaluating the global value head OOD.
blended.improvement_logit = local.improvement_logit;
blended.improvement_probability = local.improvement_probability;
blended.expected_defect_gain = local.expected_defect_gain;
blended.uncertainty = local.uncertainty;
for (std::size_t i = 0; i < blended.plane_probabilities.size(); ++i) {
blended.plane_probabilities[i] =
local_weight * local.plane_probabilities[i] +
global_weight * global.plane_probabilities[i];
}
for (std::size_t i = 0; i < blended.move_probabilities.size(); ++i) {
blended.move_probabilities[i] =
local_weight * local.move_probabilities[i] +
global_weight * global.move_probabilities[i];
}
for (std::size_t i = 0; i < blended.scale_probabilities.size(); ++i) {
blended.scale_probabilities[i] =
local_weight * local.scale_probabilities[i] +
global_weight * global.scale_probabilities[i];
}
blended.finite = true;
return blended;
};
auto make_transformer_features = [&](const VectorXd& x,
const GlobalMetrics& metrics,
bool guided,
szilassi::transformer::Features& features) {
szilassi::transformer::FeatureInput input;
input.state = training_plane_state(x);
input.metrics = training_metrics(metrics);
input.budget = transformer_budget;
input.topology = static_cast<std::uint32_t>(topology_state.topology);
input.guided_context = guided;
return szilassi::transformer::make_features(input, features, nullptr);
};
std::uint64_t transformer_anchor_invalid_predictions = 0;
for (Anchor& anchor : anchors) {
if (model_ready) {
if (online_model_ready) {
anchor.prediction = topology_state.neural->predict(anchor.input);
}
if (!anchor.prediction.finite) {
anchor.prediction.plane_probabilities.fill(
1.0f / static_cast<float>(cuda_search::kPlaneCount));
anchor.prediction.move_probabilities.fill(
1.0f / static_cast<float>(szilassi::surrogate::kMoveCount));
anchor.prediction.scale_probabilities = {{0.25f, 0.55f, 0.20f}};
anchor.prediction = default_prediction();
}
anchor.control_prediction = anchor.prediction;
anchor.control_learned_prediction = online_model_ready &&
anchor.prediction.finite;
const double uncertainty = std::sqrt(std::max(
0.0,
static_cast<double>(anchor.prediction.uncertainty)));
anchor.acquisition = model_ready
anchor.acquisition = online_model_ready
? static_cast<double>(anchor.prediction.expected_defect_gain) +
0.75 * uncertainty +
0.10 * static_cast<double>(anchor.prediction.improvement_probability)
: 0.0;
if (transformer_ready) {
szilassi::transformer::Features features;
if (make_transformer_features(anchor.x, anchor.metrics, true, features)) {
const NeuralPrediction global_prediction = transformer_as_neural(
transformer_ranker->predict(features));
if (global_prediction.finite) {
anchor.prediction = blend_predictions(
anchor.prediction,
global_prediction);
anchor.learned_prediction = true;
} else {
++transformer_anchor_invalid_predictions;
}
} else {
++transformer_anchor_invalid_predictions;
}
}
anchor.learned_prediction = anchor.learned_prediction ||
(online_model_ready && anchor.prediction.finite);
}
std::stable_sort(anchors.begin(), anchors.end(), [](const Anchor& left, const Anchor& right) {
if (left.acquisition != right.acquisition) {
@@ -4650,9 +4802,18 @@ HybridSeedPool make_hybrid_seed_pool(
RNG rng(static_cast<RNG::result_type>(seed));
std::normal_distribution<double> normal(0.0, 1.0);
HybridSeedPool proposals;
proposals.transformer_invalid_predictions =
transformer_anchor_invalid_predictions;
const int proposal_target = transformer_ready
? std::max(guided_target, guided_target * 4)
: guided_target;
const int control_target = transformer_ready
? std::min(4, guided_target)
: 0;
int attempts = 0;
while (std::count(pool.neural_guided.begin(), pool.neural_guided.end(), 1u) < guided_target &&
attempts < guided_target * 12) {
while (static_cast<int>(proposals.states.size()) < proposal_target &&
attempts < proposal_target * 16) {
++attempts;
std::size_t anchor_index = 0;
if (attempts % 4 == 0) {
@@ -4663,6 +4824,14 @@ HybridSeedPool make_hybrid_seed_pool(
std::min<std::size_t>(8, anchors.size());
}
const Anchor& anchor = anchors[anchor_index];
const bool score_independent_control =
static_cast<int>(proposals.states.size()) < control_target;
const NeuralPrediction& action_prediction = score_independent_control
? anchor.control_prediction
: anchor.prediction;
const bool learned_action_prediction = score_independent_control
? anchor.control_learned_prediction
: anchor.learned_prediction;
std::array<double, szilassi::surrogate::kPlaneCount> plane_bias{};
const std::uint16_t failure_mask = static_cast<std::uint16_t>(
anchor.metrics.crossing_face_mask | anchor.metrics.intersection_face_mask);
@@ -4681,23 +4850,23 @@ HybridSeedPool make_hybrid_seed_pool(
std::array<float, szilassi::surrogate::kScaleCount>
scale_sampling_probabilities{};
const int plane = sample_neural_action(
anchor.prediction.plane_probabilities,
model_ready ? 0.25 : 0.65,
action_prediction.plane_probabilities,
learned_action_prediction ? 0.25 : 0.65,
rng,
&plane_propensity,
&plane_bias,
&plane_sampling_probabilities);
const int move = sample_neural_action(
anchor.prediction.move_probabilities,
model_ready ? 0.25 : 0.65,
action_prediction.move_probabilities,
learned_action_prediction ? 0.25 : 0.65,
rng,
&move_propensity,
static_cast<const std::array<
double, szilassi::surrogate::kMoveCount>*>(nullptr),
&move_sampling_probabilities);
const int scale = sample_neural_action(
anchor.prediction.scale_probabilities,
model_ready ? 0.25 : 0.50,
action_prediction.scale_probabilities,
learned_action_prediction ? 0.25 : 0.50,
rng,
&scale_propensity,
static_cast<const std::array<
@@ -4709,13 +4878,15 @@ HybridSeedPool make_hybrid_seed_pool(
if (!round_trip_global_state_to_fp32(candidate, candidate_metrics)) {
continue;
}
append(
append_to(
proposals,
proposal_target,
candidate,
candidate_metrics,
anchor.x,
anchor.input,
anchor.metrics,
anchor.prediction,
action_prediction,
true,
plane,
move,
@@ -4725,7 +4896,141 @@ HybridSeedPool make_hybrid_seed_pool(
scale_propensity,
plane_sampling_probabilities,
move_sampling_probabilities,
scale_sampling_probabilities);
scale_sampling_probabilities,
0);
}
auto copy_proposal = [&](std::size_t index, std::uint8_t selection_mode) {
if (index >= proposals.states.size()) return;
VectorXd injected_x(GLOBAL_PLANE_VALUE_COUNT);
VectorXd training_x(GLOBAL_PLANE_VALUE_COUNT);
for (int component = 0; component < GLOBAL_PLANE_VALUE_COUNT; ++component) {
injected_x[component] = static_cast<double>(
proposals.states[index].values[static_cast<std::size_t>(component)]);
training_x[component] = static_cast<double>(
proposals.training_states[index].values[static_cast<std::size_t>(component)]);
}
append_to(
pool,
maximum_states,
injected_x,
proposals.injected_metrics[index],
training_x,
proposals.inputs[index],
proposals.metrics[index],
proposals.behavior_predictions[index],
true,
proposals.plane_actions[index],
proposals.move_actions[index],
proposals.scale_actions[index],
proposals.plane_propensities[index],
proposals.move_propensities[index],
proposals.scale_propensities[index],
proposals.plane_sampling_probabilities[index],
proposals.move_sampling_probabilities[index],
proposals.scale_sampling_probabilities[index],
selection_mode);
};
if (!transformer_ready || proposals.states.size() <=
static_cast<std::size_t>(guided_target)) {
for (std::size_t index = 0;
index < proposals.states.size() &&
index < static_cast<std::size_t>(guided_target);
++index) {
copy_proposal(index, 0);
}
return pool;
}
const std::size_t controls = std::min<std::size_t>(
static_cast<std::size_t>(control_target),
proposals.states.size());
for (std::size_t index = 0; index < controls; ++index) {
copy_proposal(index, 2);
++pool.transformer_control_seeds;
}
struct RankedProposal {
std::size_t index = 0;
double acquisition = -std::numeric_limits<double>::infinity();
};
std::vector<szilassi::transformer::Features> rank_features;
std::vector<std::size_t> rank_indices;
rank_features.reserve(proposals.states.size() - controls);
rank_indices.reserve(proposals.states.size() - controls);
for (std::size_t index = controls; index < proposals.states.size(); ++index) {
VectorXd candidate(GLOBAL_PLANE_VALUE_COUNT);
for (int component = 0; component < GLOBAL_PLANE_VALUE_COUNT; ++component) {
candidate[component] = static_cast<double>(
proposals.states[index].values[static_cast<std::size_t>(component)]);
}
szilassi::transformer::Features features;
if (make_transformer_features(
candidate,
proposals.injected_metrics[index],
true,
features)) {
rank_features.push_back(features);
rank_indices.push_back(index);
} else {
++pool.transformer_invalid_predictions;
}
}
const std::vector<szilassi::transformer::Prediction> rank_predictions =
transformer_ranker->predict_batch(rank_features);
std::vector<RankedProposal> ranked;
ranked.reserve(rank_predictions.size());
for (std::size_t item = 0;
item < rank_predictions.size() && item < rank_indices.size();
++item) {
const szilassi::transformer::Prediction& prediction = rank_predictions[item];
if (!prediction.finite) {
++pool.transformer_invalid_predictions;
continue;
}
++pool.transformer_candidates_scored;
const double probability = std::clamp(
static_cast<double>(prediction.improvement_probability),
0.0,
1.0);
const double expected_gain = std::clamp(
static_cast<double>(prediction.expected_defect_gain),
-8.0,
8.0);
const double uncertainty = std::sqrt(std::max(
0.0,
static_cast<double>(prediction.improvement_probability_variance)));
ranked.push_back(RankedProposal{
rank_indices[item],
probability * (1.0 + std::max(0.0, expected_gain)) +
0.15 * uncertainty});
}
std::stable_sort(ranked.begin(), ranked.end(), [](const auto& left, const auto& right) {
if (left.acquisition != right.acquisition) {
return left.acquisition > right.acquisition;
}
return left.index < right.index;
});
const std::size_t ranked_target = static_cast<std::size_t>(guided_target) - controls;
std::unordered_set<std::size_t> selected_indices;
std::size_t filled_ranked_slots = 0;
for (std::size_t item = 0;
item < ranked.size() && item < ranked_target;
++item) {
copy_proposal(ranked[item].index, 1);
selected_indices.insert(ranked[item].index);
++filled_ranked_slots;
++pool.transformer_candidates_selected;
}
for (std::size_t index = controls;
filled_ranked_slots < ranked_target &&
index < proposals.states.size();
++index) {
if (selected_indices.insert(index).second) {
copy_proposal(index, 0);
++filled_ranked_slots;
}
}
return pool;
}
@@ -4738,6 +5043,7 @@ bool complete_hybrid_seed(const HybridSeedPool& pool, std::size_t index) {
index < pool.injected_metrics.size() &&
index < pool.behavior_predictions.size() &&
index < pool.neural_guided.size() &&
index < pool.transformer_selection_mode.size() &&
index < pool.plane_actions.size() &&
index < pool.move_actions.size() &&
index < pool.scale_actions.size() &&
@@ -4833,6 +5139,11 @@ std::uint64_t archive_hybrid_seed_pool(
if (pool.neural_guided[index] != 0) {
record.flags |= szilassi::training::SeedProposalNeuralGuided;
}
if (pool.transformer_selection_mode[index] == 1) {
record.flags |= szilassi::training::SeedProposalTransformerRanked;
} else if (pool.transformer_selection_mode[index] == 2) {
record.flags |= szilassi::training::SeedProposalTransformerControl;
}
if (!append_training_record(archive, record)) {
if (archive->fatal_error || !archive->writer->collection_enabled()) {
break;
@@ -5687,7 +5998,7 @@ bool write_run_manifest(
<< "cuda_session_cache\t" << effective_cuda_session_cache << "\n"
<< "algorithm\t"
<< (options.use_cuda
? "hybrid-quality-diversity-neural-v3"
? "hybrid-quality-diversity-neural-v3-transformer-ranker-v1"
: "cpu-parallel-simulated-annealing") << "\n"
<< "control_baseline_min_fraction\t0.25\n"
<< "strategy_weights\tadaptive-total:16,floors:baseline4/replica1/adaptive1/pbt1/injected3,max6\n"
@@ -5715,6 +6026,11 @@ bool write_run_manifest(
? "per-topology-ensemble:5,residual-mlp:45-128-128-128,value-policy,online-adamw,replay:4096,recent:25%"
: "disabled") << "\n"
<< "neural_safety\tbaseline-floor:25%,policy-exploration:25%,exact-cuda-plus-cpu-dd-authoritative\n"
<< "transformer_enabled\t" << (g_transformer_active ? 1 : 0) << "\n"
<< "transformer_model_id\t" << g_transformer_model_id << "\n"
<< "transformer\tglobal-ensemble:3,set-transformer:12-faces-plus-cls,width:64,heads:4,layers:3,ff:256,host-fp32-ranker\n"
<< "transformer_scope\tguided-seeds-only,ranked:12/64,score-independent-controls:4/64,map-cem-random:48/64\n"
<< "transformer_failure_mode\tmissing-invalid-nonfinite:fallback-to-online-mlp\n"
<< "training_archive_schema\t" << TRAINING_ARCHIVE_SCHEMA_VERSION << "\n"
<< "training_archive_format\t" << szilassi::training::kTrainingShardFormatVersion << "\n"
<< "training_archive_layout\trun-uuid/immutable-64MiB-crc-shards-plus-durable-wal\n"
@@ -5900,7 +6216,8 @@ bool run_global_topology_round_cuda(
int round_seed,
int effective_chain_count,
cuda_search::BatchSession& session,
TrainingArchiveSession* training_archive
TrainingArchiveSession* training_archive,
const szilassi::transformer::TransformerRanker* transformer_ranker
) {
const auto round_started_at = std::chrono::steady_clock::now();
state.last_round = {};
@@ -5951,6 +6268,7 @@ bool run_global_topology_round_cuda(
initial_pool_for_archive.injected_metrics.push_back(state.best);
initial_pool_for_archive.behavior_predictions.emplace_back();
initial_pool_for_archive.neural_guided.push_back(0);
initial_pool_for_archive.transformer_selection_mode.push_back(0);
initial_pool_for_archive.plane_actions.push_back(-1);
initial_pool_for_archive.move_actions.push_back(-1);
initial_pool_for_archive.scale_actions.push_back(-1);
@@ -6001,17 +6319,37 @@ bool run_global_topology_round_cuda(
std::max(0.002, options.step * (depth ? 0.025 : 0.05)));
run_config.injected_state_jitter = static_cast<float>(
std::max(0.002, options.step * (depth ? 0.018 : 0.04)));
const int estimated_injected_chains = std::max(
1,
(effective_chain_count * std::max(0, strategy_weights[4]) + 15) / 16);
const int estimated_chains_per_seed = std::max(
1,
(estimated_injected_chains + 63) / 64);
HybridSeedPool injected_pool = make_hybrid_seed_pool(
state,
static_cast<std::uint64_t>(static_cast<std::uint32_t>(round_seed)) +
static_cast<std::uint64_t>(batch + 1) * 0x9e3779b97f4a7c15ULL,
64);
64,
transformer_ranker,
szilassi::transformer::SearchBudget{
static_cast<std::uint64_t>(std::max(0, options.cuda_iterations)),
static_cast<std::uint32_t>(estimated_chains_per_seed),
static_cast<std::uint32_t>(estimated_chains_per_seed),
64U});
run_config.injected_states = injected_pool.states;
state.last_round.neural_seed_count += static_cast<std::uint64_t>(
std::count(
injected_pool.neural_guided.begin(),
injected_pool.neural_guided.end(),
static_cast<std::uint8_t>(1)));
state.last_round.transformer_candidates_scored +=
injected_pool.transformer_candidates_scored;
state.last_round.transformer_candidates_selected +=
injected_pool.transformer_candidates_selected;
state.last_round.transformer_control_seeds +=
injected_pool.transformer_control_seeds;
state.last_round.transformer_invalid_predictions +=
injected_pool.transformer_invalid_predictions;
if (batch == 0 && (previous_has_state || session_was_initialized)) {
run_config.fresh_numerator = depth ? 1 : 7;
run_config.fresh_denominator = depth ? 4 : 8;
@@ -6433,6 +6771,7 @@ bool run_global_topology_round_cuda(
seed_index >= injected_pool.injected_metrics.size() ||
seed_index >= injected_pool.behavior_predictions.size() ||
seed_index >= injected_pool.neural_guided.size() ||
seed_index >= injected_pool.transformer_selection_mode.size() ||
seed_index >= injected_pool.plane_actions.size() ||
seed_index >= injected_pool.move_actions.size() ||
seed_index >= injected_pool.scale_actions.size() ||
@@ -6488,7 +6827,9 @@ bool run_global_topology_round_cuda(
record.replica_count = rollout.replica_count;
// Best CPU-verified descendant among all chains assigned to
// this proposal in the batch.
record.selection_rule = 1;
record.selection_rule = 1U |
(static_cast<std::uint32_t>(
injected_pool.transformer_selection_mode[seed_index]) << 8U);
record.plane_action = injected_pool.plane_actions[seed_index];
record.move_action = injected_pool.move_actions[seed_index];
record.scale_action = injected_pool.scale_actions[seed_index];
@@ -6508,6 +6849,11 @@ bool run_global_topology_round_cuda(
if (injected_pool.neural_guided[seed_index] != 0) {
record.flags |= szilassi::training::TrajectoryNeuralGuided;
}
if (injected_pool.transformer_selection_mode[seed_index] == 1) {
record.flags |= szilassi::training::TrajectoryTransformerRanked;
} else if (injected_pool.transformer_selection_mode[seed_index] == 2) {
record.flags |= szilassi::training::TrajectoryTransformerControl;
}
if (injected_pool.behavior_predictions[seed_index].finite) {
record.flags |= szilassi::training::TrajectoryPredictionSupplied;
}
@@ -6784,7 +7130,8 @@ bool run_global_topology_round(
int round_seed,
int effective_cuda_chains,
cuda_search::BatchSession* cuda_session,
TrainingArchiveSession* training_archive
TrainingArchiveSession* training_archive,
const szilassi::transformer::TransformerRanker* transformer_ranker
) {
if (options.use_cuda) {
if (cuda_session == nullptr) {
@@ -6798,7 +7145,8 @@ bool run_global_topology_round(
round_seed,
effective_cuda_chains,
*cuda_session,
training_archive);
training_archive,
transformer_ranker);
}
const auto round_started_at = std::chrono::steady_clock::now();
state.last_round = {};
@@ -7008,6 +7356,8 @@ int global_search_all(const LocalRepairOptions& options) {
}
const std::filesystem::path root(options.global_dir);
std::filesystem::create_directories(root);
g_transformer_active = false;
g_transformer_model_id = "none";
const szilassi::checkpoint::RunIdentity run_identity =
szilassi::checkpoint::make_run_identity();
g_global_degeneracy_weight = options.degeneracy_weight;
@@ -7042,6 +7392,35 @@ int global_search_all(const LocalRepairOptions& options) {
<< std::endl;
}
szilassi::transformer::TransformerRanker transformer_ranker;
if (options.use_cuda) {
const std::filesystem::path model_path =
root / transformer_model_relative_path();
if (std::filesystem::exists(model_path)) {
std::string transformer_error;
if (transformer_ranker.load(model_path, &transformer_error) &&
transformer_ranker.ready()) {
const szilassi::transformer::Metadata metadata =
transformer_ranker.metadata();
g_transformer_active = true;
g_transformer_model_id = transformer_model_id(metadata);
std::cout << "Transformer ranker: " << g_transformer_model_id
<< ", validation AP " << metadata.validation.average_precision
<< ", top-k gain " << metadata.validation.top_k_gain
<< std::endl;
} else {
std::cerr << "Ignoring invalid transformer ranker: "
<< transformer_error
<< "; online MLP and ordinary search remain active."
<< std::endl;
}
} else {
std::cout << "Transformer ranker: no approved model yet; "
"online MLP and ordinary search remain active."
<< std::endl;
}
}
const unsigned int hardware_threads = std::thread::hardware_concurrency();
const int worker_count = std::max(
1,
@@ -7093,11 +7472,13 @@ int global_search_all(const LocalRepairOptions& options) {
std::uint64_t existing_neural_bytes = 0;
std::array<std::uint64_t, NUM_TOPOLOGIES>
existing_final_neural_snapshot_bytes{};
std::uint64_t existing_transformer_snapshot_bytes = 0;
std::string training_setup_error;
if (!scan_neural_cache_bytes(
root,
existing_neural_bytes,
existing_final_neural_snapshot_bytes,
existing_transformer_snapshot_bytes,
training_setup_error)) {
std::cerr << "Cannot account existing neural cache: "
<< training_setup_error << std::endl;
@@ -7119,6 +7500,17 @@ int global_search_all(const LocalRepairOptions& options) {
watcher.join();
return 2;
}
const std::uint64_t maximum_transformer_snapshot =
szilassi::transformer::maximum_model_bytes();
if (maximum_transformer_snapshot > TRANSFORMER_MODEL_BUDGET) {
std::cerr << "Configured transformer snapshot no longer fits its exact cache budget; "
<< "revise the program before collecting more training data."
<< std::endl;
stop_requested.store(true, std::memory_order_relaxed);
watcher_done.store(true, std::memory_order_relaxed);
watcher.join();
return 2;
}
// Count every byte that is already present, then reserve the missing
// capacity of each canonical final-model slot independently. Taking the
// maximum of aggregate existing/reserved bytes would undercount orphan or
@@ -7142,6 +7534,19 @@ int global_search_all(const LocalRepairOptions& options) {
}
accounted_neural_files += missing_slot_bytes;
}
if (existing_transformer_snapshot_bytes < TRANSFORMER_MODEL_BUDGET) {
const std::uint64_t missing_transformer_slot =
TRANSFORMER_MODEL_BUDGET - existing_transformer_snapshot_bytes;
if (missing_transformer_slot >
std::numeric_limits<std::uint64_t>::max() - accounted_neural_files) {
std::cerr << "Transformer cache byte-count overflow." << std::endl;
stop_requested.store(true, std::memory_order_relaxed);
watcher_done.store(true, std::memory_order_relaxed);
watcher.join();
return 2;
}
accounted_neural_files += missing_transformer_slot;
}
if (maximum_neural_snapshot >
std::numeric_limits<std::uint64_t>::max() - accounted_neural_files) {
std::cerr << "Neural cache byte-count overflow." << std::endl;
@@ -7152,8 +7557,24 @@ int global_search_all(const LocalRepairOptions& options) {
}
// Atomic save temporarily keeps the previous final snapshot and one full
// replacement. Existing orphan/foreign files remain separately accounted.
// Transformer publication retains the current file while creating one
// immutable generation and one atomic replacement temporary.
const std::uint64_t transformer_publication_reserve =
maximum_transformer_snapshot * 2ULL;
if (maximum_neural_snapshot >
std::numeric_limits<std::uint64_t>::max() - accounted_neural_files ||
transformer_publication_reserve >
std::numeric_limits<std::uint64_t>::max() -
(accounted_neural_files + maximum_neural_snapshot)) {
std::cerr << "Neural cache byte-count overflow." << std::endl;
stop_requested.store(true, std::memory_order_relaxed);
watcher_done.store(true, std::memory_order_relaxed);
watcher.join();
return 2;
}
const std::uint64_t neural_model_reserve =
accounted_neural_files + maximum_neural_snapshot;
accounted_neural_files + maximum_neural_snapshot +
transformer_publication_reserve;
szilassi::training::RecoveryReport training_recovery;
const std::uint64_t training_archive_allocation =
neural_model_reserve < TRAINING_CACHE_LIMIT_BYTES
@@ -7279,6 +7700,7 @@ int global_search_all(const LocalRepairOptions& options) {
<< "\trex_attempts\trex_accepts\tspsa_attempted\tspsa_accepted"
<< "\tbest_crossing_loss\tbest_intersection_loss"
<< "\tneural_replay\tneural_positive\tneural_samples\tneural_train_steps\tneural_seeds"
<< "\ttransformer_scored\ttransformer_selected\ttransformer_controls\ttransformer_invalid"
<< "\ttraining_verified\ttraining_fp32\ttraining_seeds"
<< "\ttraining_rollouts\ttraining_refinements"
<< "\ttraining_cache_accounted_bytes\ttraining_wal_bytes"
@@ -7331,6 +7753,9 @@ int global_search_all(const LocalRepairOptions& options) {
<< std::endl;
if (options.use_cuda) {
std::cout << "Guidance : smooth-I repair + per-topology online neural ensemble"
<< (g_transformer_active
? " + global transformer ranker"
: " + transformer fallback inactive")
<< std::endl;
}
std::cout << "Training data: CPU-verified states, sampled chain bests and injected trajectories, "
@@ -7533,7 +7958,8 @@ int global_search_all(const LocalRepairOptions& options) {
round_seed,
effective_cuda_chains,
cuda_session,
&training_archive);
&training_archive,
g_transformer_active ? &transformer_ranker : nullptr);
if (training_archive.fatal_error) {
fatal_search_error = true;
stop_requested.store(true, std::memory_order_relaxed);
@@ -7584,6 +8010,16 @@ int global_search_all(const LocalRepairOptions& options) {
<< (state.neural != nullptr
? ", neural replay " + std::to_string(state.neural->replay_size())
: std::string{})
<< (g_transformer_active
? ", transformer " +
std::to_string(
state.last_round.transformer_candidates_selected) +
"/" + std::to_string(
state.last_round.transformer_candidates_scored) +
" + " + std::to_string(
state.last_round.transformer_control_seeds) +
" control"
: std::string{})
<< ", bandit " << std::fixed << std::setprecision(3)
<< selection_score
<< ", elapsed " << std::fixed << std::setprecision(1)
@@ -7624,6 +8060,10 @@ int global_search_all(const LocalRepairOptions& options) {
<< state.last_round.neural_samples_added << "\t"
<< state.last_round.neural_training_steps << "\t"
<< state.last_round.neural_seed_count << "\t"
<< state.last_round.transformer_candidates_scored << "\t"
<< state.last_round.transformer_candidates_selected << "\t"
<< state.last_round.transformer_control_seeds << "\t"
<< state.last_round.transformer_invalid_predictions << "\t"
<< state.last_round.training_verified_records << "\t"
<< state.last_round.training_fp32_records << "\t"
<< state.last_round.training_seed_records << "\t"