* fix(graph): handle qualified C++ symbols Signed-off-by: trvon <me@trevon.dev> * refactor(vector)!: remove dead transition APIs Split Simeon vector and retrieval linkage so consumers inherit only the runtime surface they use. BREAKING CHANGE: remove unused async/model/config helpers and the undefined vector-backend factory from public headers. Signed-off-by: trvon <me@trevon.dev> * fix(vector): harden PQ snapshot lifecycle Validate and atomically install persisted generations, revalidate after races, and fall back to exact search when a snapshot is stale. Add the exact-scan reference lane and distinguish observed work counters from unavailable backend metrics. Signed-off-by: trvon <me@trevon.dev> * feat(topology): expose route coordinates Persist chart distortion and local-dimension evidence, retain pre-scalarization route axes, and emit detailed trace rows without treating unavailable vector work as zero. Signed-off-by: trvon <me@trevon.dev> * feat(xplan): add vector decision gates Track binary and corpus identity, preserve missing-work semantics, and add repeated PQ routing, crossover, and rerank plans with quality and cost summaries. Signed-off-by: trvon <me@trevon.dev> * feat(topology): formalize coordinate routing Separate chart geometry from route-risk admission and prove candidate-stage recall and strict-focus boundaries. Record the measured atlas limitations and literature basis for the next construction experiment. Signed-off-by: trvon <me@trevon.dev> * fix(search): serialize SymSpell readers SQLite-backed lookups reuse mutable prepared statements, so shared-reader locking is unsafe. Advance the owned SymSpell revision for batched delete insertion and cover concurrent root lookups under TSAN. Signed-off-by: trvon <me@trevon.dev> * fix(vector): make mutations atomic Preserve stored vectors on replacement and commit failures, reject cross-thread writes during explicit transactions, and restore exact reranking for quantized-primary PQ rows. * fix(search)!: surface SymSpell write errors Propagate persistent dictionary failures through metadata ingestion, roll back failed batches, and make clear remove stored terms. BREAKING CHANGE: SymSpellSearch mutations now return Result values. * feat(topology): formalize candidate expansion * feat(vector): add coordinate-aware embeddings * feat(topology): preserve protected relation cover * feat(search): add graph candidate rescue * feat(daemon): expose retrieval hydration telemetry * feat(xplan): add rescue and hydration gates * refactor(content): dedupe media handlers Reuse shared graph presenter helpers and centralize repeated audio/video/archive buffer processing, validation, lifecycle, and stats helper logic. * feat(lifecycle): prune stale corpus data Keep cleanup preview-first while retention stays opt-in and long prune work no longer holds IPC responses open. * feat(search): add topology candidate rescue Preserve high-value routed candidates across vector backends while keeping exact rerank diagnostics explicit. * feat(search): expose evidence paths * docs: define mobile host contract * formal: model bounded candidate rescue * fix(prune): inspect extensionless signatures * fix(prune): tolerate stale extensions --------- Signed-off-by: trvon <me@trevon.dev>
241 lines
8.1 KiB
C++
241 lines
8.1 KiB
C++
#pragma once
|
|
|
|
#include "tests/benchmarks/beir_loader.h"
|
|
|
|
#include <yams/vector/vector_types.h>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <iosfwd>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <random>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
namespace yams::bench {
|
|
|
|
bool benchmarkSearchIndexReusable(yams::vector::VectorSearchEngine engine,
|
|
bool backendPersistedReusable);
|
|
bool benchmarkSearchIndexQueryReady(yams::vector::VectorSearchEngine engine,
|
|
bool persistedIndexReusable, bool preparedThisRun);
|
|
void writeTopologyDisabledEmbeddingSelection(std::ostream& out, bool topologyDisabled);
|
|
|
|
std::optional<double> processPeakRssMb() noexcept;
|
|
|
|
struct TestQuery {
|
|
std::string query;
|
|
std::set<std::string> relevantFiles;
|
|
std::set<std::string> relevantDocIds;
|
|
std::map<std::string, int> relevanceGrades;
|
|
bool useDocIds = false;
|
|
};
|
|
|
|
enum class SyntheticCorpusMode { Generic, CommunityGraph, HardLexical };
|
|
|
|
struct CorpusGenerator {
|
|
std::vector<std::string> topics = {"authentication", "database", "network", "parsing",
|
|
"encryption", "testing", "logging", "storage"};
|
|
std::vector<std::string> terms = {"user", "password", "token", "session",
|
|
"connection", "request", "response", "cache",
|
|
"error", "file", "data"};
|
|
std::filesystem::path corpusDir;
|
|
std::vector<std::string> createdFiles;
|
|
std::mt19937 rng{42};
|
|
SyntheticCorpusMode mode = SyntheticCorpusMode::Generic;
|
|
|
|
explicit CorpusGenerator(const std::filesystem::path& dir,
|
|
SyntheticCorpusMode corpusMode = SyntheticCorpusMode::Generic);
|
|
|
|
void generateDocuments(int count);
|
|
std::vector<TestQuery> generateQueries(int numQueries);
|
|
};
|
|
|
|
struct LexicalTopologyDocument {
|
|
std::string documentHash;
|
|
std::string text;
|
|
};
|
|
|
|
struct LexicalTopologyNeighbor {
|
|
std::string sourceHash;
|
|
std::string targetHash;
|
|
float score{0.0F};
|
|
};
|
|
|
|
struct GraphTopologyFeature {
|
|
std::int64_t nodeId = 0;
|
|
float weight = 0.0F;
|
|
};
|
|
|
|
struct GraphTopologyDocument {
|
|
std::string documentHash;
|
|
std::vector<GraphTopologyFeature> relatedNodes;
|
|
};
|
|
|
|
struct KeyphraseTopologyDocument {
|
|
std::string documentHash;
|
|
std::string text;
|
|
};
|
|
|
|
enum class BenchmarkTopologySignalKind {
|
|
LexicalCohesion,
|
|
LexicalDisruption,
|
|
Keyphrase,
|
|
Entity,
|
|
Ontology,
|
|
Structure,
|
|
Embedding,
|
|
};
|
|
|
|
std::string topologySignalKindName(BenchmarkTopologySignalKind kind);
|
|
|
|
struct BenchmarkTopologyGateConfig {
|
|
float minSignalWeight = 1.0F;
|
|
std::size_t maxSignalDocumentFrequency = 64;
|
|
float minNeighborScore = 0.0F;
|
|
std::size_t maxComponentDocs = 64;
|
|
};
|
|
|
|
struct BenchmarkTopologySignal {
|
|
std::string documentHash;
|
|
std::string featureId;
|
|
BenchmarkTopologySignalKind kind = BenchmarkTopologySignalKind::LexicalCohesion;
|
|
float weight = 0.0F;
|
|
std::size_t documentFrequency = 0;
|
|
};
|
|
|
|
struct BenchmarkTopologyNeighborObservation {
|
|
std::string sourceHash;
|
|
std::string targetHash;
|
|
std::string sourceSegmentId;
|
|
std::string targetSegmentId;
|
|
float score = 0.0F;
|
|
};
|
|
|
|
struct BenchmarkTopologyEdgeWitness {
|
|
std::string sourceHash;
|
|
std::string targetHash;
|
|
std::string sourceSegmentId;
|
|
std::string targetSegmentId;
|
|
float score = 0.0F;
|
|
std::string featureId;
|
|
BenchmarkTopologySignalKind kind = BenchmarkTopologySignalKind::LexicalCohesion;
|
|
float sourceWeight = 0.0F;
|
|
float targetWeight = 0.0F;
|
|
std::size_t documentFrequency = 0;
|
|
bool reciprocal = false;
|
|
bool signalGatePassed = false;
|
|
};
|
|
|
|
struct BenchmarkTopologyComponentSummary {
|
|
std::string rootHash;
|
|
std::vector<std::string> memberHashes;
|
|
};
|
|
|
|
struct BenchmarkTopologyConstructionCertificate {
|
|
std::string sourceName;
|
|
BenchmarkTopologyGateConfig gates;
|
|
std::vector<BenchmarkTopologySignal> signals;
|
|
std::vector<BenchmarkTopologyNeighborObservation> observations;
|
|
std::vector<BenchmarkTopologyEdgeWitness> edgeWitnesses;
|
|
std::vector<BenchmarkTopologyComponentSummary> components;
|
|
};
|
|
|
|
struct BenchmarkTopologyValidationResult {
|
|
bool ok = true;
|
|
std::vector<std::string> errors;
|
|
};
|
|
|
|
BenchmarkTopologyValidationResult validateTopologyConstructionCertificate(
|
|
const BenchmarkTopologyConstructionCertificate& certificate);
|
|
nlohmann::json
|
|
topologyConstructionCertificateToJson(const BenchmarkTopologyConstructionCertificate& certificate,
|
|
const BenchmarkTopologyValidationResult& validation = {});
|
|
|
|
std::vector<LexicalTopologyNeighbor>
|
|
buildLexicalTopologyNeighbors(const std::vector<LexicalTopologyDocument>& documents,
|
|
std::size_t topK, float minScore, bool reciprocalOnly = true,
|
|
BenchmarkTopologyConstructionCertificate* certificate = nullptr);
|
|
|
|
std::vector<LexicalTopologyNeighbor>
|
|
buildGraphTopologyNeighbors(const std::vector<GraphTopologyDocument>& documents, std::size_t topK,
|
|
float minScore, bool reciprocalOnly = true,
|
|
BenchmarkTopologyConstructionCertificate* certificate = nullptr);
|
|
|
|
std::vector<LexicalTopologyNeighbor> buildKeyphraseTopologyNeighbors(
|
|
const std::vector<KeyphraseTopologyDocument>& documents, std::size_t topK, float minScore,
|
|
std::size_t maxFeaturesPerDocument = 16, std::size_t maxFeatureDocumentFrequency = 64,
|
|
bool reciprocalOnly = true, BenchmarkTopologyConstructionCertificate* certificate = nullptr);
|
|
|
|
std::vector<LexicalTopologyNeighbor> buildSegmentKeyphraseTopologyNeighbors(
|
|
const std::vector<KeyphraseTopologyDocument>& documents, std::size_t topK, float minScore,
|
|
std::size_t segmentTokenBudget = 192, std::size_t segmentTokenOverlap = 32,
|
|
std::size_t maxFeaturesPerSegment = 12, std::size_t maxFeatureDocumentFrequency = 64,
|
|
std::size_t maxComponentDocs = 64, std::size_t maxEdgesPerFeature = 64,
|
|
bool reciprocalOnly = true, BenchmarkTopologyConstructionCertificate* certificate = nullptr);
|
|
|
|
struct BEIRCorpusLoader {
|
|
BEIRDataset dataset;
|
|
std::filesystem::path corpusDir;
|
|
std::map<std::string, std::string> docIdToHash;
|
|
|
|
BEIRCorpusLoader(const BEIRDataset& ds, const std::filesystem::path& dir);
|
|
|
|
void writeDocumentsAsFiles();
|
|
std::vector<TestQuery> generateTestQueries();
|
|
std::vector<std::string> getDocumentPaths() const;
|
|
};
|
|
|
|
yams::Result<BEIRDataset> loadBenchmarkManifestDataset(const std::string& datasetName,
|
|
const std::filesystem::path& manifestRoot);
|
|
|
|
struct RetrievalMetrics {
|
|
double mrr = 0.0;
|
|
double recallAtK = 0.0;
|
|
double precisionAtK = 0.0;
|
|
double ndcgAtK = 0.0;
|
|
double map = 0.0;
|
|
double duplicateRateAtK = 0.0;
|
|
int numQueries = 0;
|
|
};
|
|
|
|
struct StageMetricAccumulator {
|
|
double totalMRR = 0.0;
|
|
double totalRecall = 0.0;
|
|
double totalPrecision = 0.0;
|
|
double totalNDCG = 0.0;
|
|
double totalMAP = 0.0;
|
|
std::uint64_t queryCount = 0;
|
|
};
|
|
|
|
struct QueryScoreSample {
|
|
int firstRelevantRank = -1;
|
|
int numRelevantInTopK = 0;
|
|
int numRelevantSeen = 0;
|
|
bool hadDuplicateInTopK = false;
|
|
double reciprocalRank = 0.0;
|
|
double recallAtK = 0.0;
|
|
double precisionAtK = 0.0;
|
|
double averagePrecision = 0.0;
|
|
double ndcgAtK = 0.0;
|
|
std::vector<int> retrievedGrades;
|
|
std::vector<std::string> duplicateDocIds;
|
|
};
|
|
|
|
std::string canonicalResultDocId(const TestQuery& query, const std::string& fileName);
|
|
bool isRelevantDocForQuery(const TestQuery& query, const std::string& docId);
|
|
int relevanceGradeForQuery(const TestQuery& query, const std::string& docId);
|
|
|
|
QueryScoreSample scoreRankedDocIds(const TestQuery& query,
|
|
const std::vector<std::string>& rankedDocIds, int k);
|
|
void addStageMetricSample(StageMetricAccumulator& acc, const std::vector<std::string>& rankedDocIds,
|
|
const TestQuery& query, int k);
|
|
RetrievalMetrics finalizeStageMetrics(const StageMetricAccumulator& acc);
|
|
nlohmann::json
|
|
stageRetrievalMetricsToJson(const std::map<std::string, StageMetricAccumulator>& stageMetrics);
|
|
|
|
} // namespace yams::bench
|