Files

119 lines
5.5 KiB
C++

// Emit current production tensor kernels for an installed engine's real Gpgpu.
// This validates GPU mathematics, not current module boot/registration.
#include <fstream>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include "agent/Learning.h"
#include "agent/tensor/GpuGraph.h"
#include "tensor/KernelGen.h"
#include "tensor/Optimizer.h"
using namespace eve;
static std::string quote(const std::string& text) {
std::string out = "\"";
for (char c : text) {
if (c == '\n')
out += "\\n";
else if (c == '\r')
out += "\\r";
else {
if (c == '\\' || c == '\"') out += '\\';
out += c;
}
}
return out + "\"";
}
static void emit(std::ostream& out, agent::Policy p, agent::Observation o, bool training, int test) {
auto recipe = agent::detail::makePolicyGraph(p.featureCount, p.hiddenWidth, p.actionCount, training);
auto opt = tensor::optimizeGraph(recipe.graph, recipe.output);
std::vector<float> weights;
weights.reserve(p.weights.size());
for (double weight : p.weights) weights.push_back(static_cast<float>(weight));
std::vector<std::vector<float>> feeds{std::move(weights),
o.features,
std::vector<float>(p.actionCount, -1e38f),
std::vector<float>(p.actionCount, 0),
{0.03f}};
for (auto a : o.legalActions) feeds[2][a] = 0;
feeds[3][o.legalActions.back()] = 1;
auto expected = agent::detail::forward(p, o);
if (training) {
agent::detail::train(p, o, o.legalActions.back(), 0.03);
expected = p.weights;
}
out << "{ local b = []; local shaders = [];\n";
for (auto size : opt.slotSize) out << "b.append(gpgpu.newBuffer(" << size * 4 << ",\"storage\"));\n";
for (int id = 0; id < recipe.graph.nodeCount(); ++id) {
auto& node = recipe.graph.node(id);
int slot = opt.nodeSlot[id];
if (slot < 0) continue;
const std::vector<float>* values = nullptr;
if (node.type == tensor::OpType::Placeholder) values = &feeds[node.placeholderSlot];
if (node.type == tensor::OpType::Const) values = &node.constData;
if (values)
for (std::size_t i = 0; i < values->size(); ++i)
out << "b[" << slot << "].writeFloat32(" << i << "," << (*values)[i] << ");\n";
}
for (int gi : opt.groupOrder) {
auto& group = opt.groups[gi];
if (group.kind == tensor::GroupKind::Alias) continue;
tensor::KernelSpec spec;
if (!tensor::generateKernel(recipe.graph, group, spec)) throw std::runtime_error("Unsupported GPU graph group");
out << "{ local stats = [];\n";
for (int i = 0; i < spec.statsCount; ++i)
out << "stats.append(gpgpu.newBuffer(" << spec.statsSize * 4 << ",\"storage\"));\n";
auto pass = [&](const std::string& source, bool first) {
out << "{ local s = gpgpu.newShader(" << quote(source) << "); shaders.append(s);\n";
int inputs = first ? spec.inputsReadPass1 : spec.inputCount;
for (int i = 0; i < inputs; ++i)
out << "s.bindBuffer(" << i << ",b[" << opt.nodeSlot[group.inputs[i]] << "]);\n";
for (int i = 0; i < spec.statsCount; ++i) out << "s.bindBuffer(" << inputs + i << ",stats[" << i << "]);\n";
if (!first)
out << "s.bindBuffer(" << inputs + spec.statsCount << ",b[" << opt.nodeSlot[group.outputNode]
<< "]);\n";
out << "gpgpu.dispatch(s," << (first ? spec.groupsX1 : spec.groupsX2) << ","
<< (first ? spec.groupsY1 : spec.groupsY2) << "," << (first ? spec.groupsZ1 : spec.groupsZ2)
<< "); }\n";
};
if (spec.twoPass) pass(spec.pass1, true);
pass(spec.pass2, false);
out << "}\n";
}
out << "local maxError = 0.0;\n";
for (std::size_t i = 0; i < expected.size(); ++i) {
out << "{ local actual = b[" << opt.nodeSlot[recipe.output] << "].readFloat32(" << i
<< "); local error = fabs(actual-(" << expected[i]
<< ")); if (actual != actual || error > 0.00003) throw \"GPU parity failed case " << test << " index " << i
<< " actual=\"+actual+\" error=\"+error; if (error > maxError) maxError=error; }\n";
}
out << "print(\"AGENT_GPU_PASS case=" << test << " training=" << training << " maxError=\"+maxError+\"\\n\"); }\n";
}
int main(int argc, char** argv) {
if (argc != 2) return 2;
try {
std::ofstream out(argv[1]);
if (!out) return 2;
out << std::scientific << std::setprecision(9);
out << "eve_init = function() { if(!gpgpu.isAvailable()) throw \"GPU unavailable\";\n";
int test = 0;
for (auto shape : {4, 16, 64}) {
agent::Config c;
c.featureCount = 3;
c.hiddenWidth = shape;
c.actionCount = 5;
auto p = agent::detail::makePolicy(c);
agent::Observation o;
o.features = {0.4f, -0.2f, 0.8f};
o.legalActions = {0, 2, 4};
emit(out, p, o, false, ++test);
emit(out, p, o, true, ++test);
}
out << "print(\"AGENT_GPU_ALL_PASS\\n\"); };\n";
std::cout << "Generated 6 GPU numerical checks\n";
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return 1;
}
}