AGISystem2 uses a strategy pattern for HDC operations. This allows swapping the underlying vector representation without changing the reasoning layer. This guide explains how to create a new strategy.
Every strategy must satisfy these mathematical properties:
| bind(a, a) | Produces "zero" effect for XOR-based strategies (strategy-dependent) |
| bind(bind(a, b), b) | ≈ a (reversibility) |
| similarity(v, v) | = 1.0 (reflexive) |
| similarity(a, b) | = similarity(b, a) (symmetric) |
| similarity(random, random) | ≈ 0.5 ± 0.05 (quasi-orthogonal) |
| bundle([a,b,c]).similarity(a) | > 0.5 for small n (retrievable) |
Create src/hdc/strategies/your-strategy.mjs:
/**
* AGISystem2 - Your Custom HDC Strategy
* @module hdc/strategies/your-strategy
*/
// ============================================================================
// VECTOR CLASS (Internal)
// ============================================================================
class YourVector {
constructor(geometry, data = null) {
this.geometry = geometry;
this.strategyId = 'your-strategy';
// Initialize your data structure
this.data = data || this.createEmpty(geometry);
}
createEmpty(geometry) {
// Return your empty data structure
}
// Required instance methods for backward compat:
// getBit(index), setBit(index, value), popcount(), density()
// clone(), equals(other), serialize()
// xorInPlace(other), andInPlace(other), orInPlace(other), notInPlace()
// zero(), ones()
// Required static methods:
// static random(geometry, randomFn)
// static zeros(geometry)
// static ones(geometry)
// static deserialize(obj)
}
// ============================================================================
// STRATEGY PROPERTIES
// ============================================================================
const properties = {
id: 'your-strategy',
displayName: 'Your Strategy Name',
recommendedBundleCapacity: 7, // Optimal vectors to bundle
maxBundleCapacity: 100, // Before accuracy drops
bytesPerVector: (geo) => geo / 8, // Memory estimate
bindComplexity: 'O(n)', // Big-O notation
sparseOptimized: false, // Benefits from sparse data?
description: 'Description of your strategy'
};
// ============================================================================
// FACTORY FUNCTIONS
// ============================================================================
function createZero(geometry) {
return new YourVector(geometry);
}
function createRandom(geometry, seed = null) {
// Create vector with ~50% density
const v = new YourVector(geometry);
// Fill with random data...
return v;
}
function createFromName(name, geometry) {
// CRITICAL: Same (name, geometry) must always produce same vector!
// Use deterministic hash-based PRNG
const seed = hashFunction(name);
return createRandom(geometry, seed);
}
function deserialize(serialized) {
if (serialized.strategyId !== 'your-strategy') {
throw new Error('Wrong strategy');
}
const v = new YourVector(serialized.geometry);
// Restore data from serialized.data
return v;
}
// ============================================================================
// CORE OPERATIONS
// ============================================================================
function bind(a, b) {
// Must be: associative, commutative (cancellation is strategy-dependent)
// For binary: XOR
// For polynomial: multiply mod irreducible
if (a.geometry !== b.geometry) {
throw new Error('Geometry mismatch');
}
const result = clone(a);
// Apply binding operation...
return result;
}
function bindAll(...vectors) {
if (vectors.length === 0) throw new Error('Need at least one vector');
let result = clone(vectors[0]);
for (let i = 1; i < vectors.length; i++) {
result = bind(result, vectors[i]);
}
return result;
}
function bundle(vectors, tieBreaker = null) {
// Superposition: result similar to ALL inputs
// For binary: majority vote per bit
// For polynomial: weighted sum
if (vectors.length === 0) throw new Error('Need at least one vector');
if (vectors.length === 1) return clone(vectors[0]);
const geometry = vectors[0].geometry;
const result = new YourVector(geometry);
// Apply bundling operation...
return result;
}
function similarity(a, b) {
// Range: [0, 1], reflexive, symmetric
// For binary: 1 - (hamming distance / geometry)
// For polynomial: cosine similarity
if (a.geometry !== b.geometry) {
throw new Error('Geometry mismatch');
}
// Calculate and return similarity...
return 0.5; // placeholder
}
function unbind(composite, component) {
// For XOR-based strategies, unbind can reuse bind
return bind(composite, component);
}
// ============================================================================
// UTILITY OPERATIONS
// ============================================================================
function clone(v) {
const result = new YourVector(v.geometry);
// Copy data...
return result;
}
function equals(a, b) {
if (a.geometry !== b.geometry) return false;
// Compare data...
return true;
}
function serialize(v) {
return {
strategyId: 'your-strategy',
geometry: v.geometry,
version: 1,
data: /* your serialization */
};
}
function topKSimilar(query, vocabulary, k = 5) {
const results = [];
const entries = vocabulary instanceof Map
? vocabulary.entries()
: Object.entries(vocabulary);
for (const [name, vec] of entries) {
results.push({ name, similarity: similarity(query, vec) });
}
results.sort((a, b) => b.similarity - a.similarity);
return results.slice(0, k);
}
function distance(a, b) {
return 1 - similarity(a, b);
}
function isOrthogonal(a, b, threshold = 0.55) {
const sim = similarity(a, b);
return sim < threshold && sim > (1 - threshold);
}
// ============================================================================
// EXPORT STRATEGY OBJECT
// ============================================================================
export const yourStrategy = {
id: 'your-strategy',
properties,
// Factory
createZero,
createRandom,
createFromName,
deserialize,
// Core operations
bind,
bindAll,
bundle,
similarity,
unbind,
// Utilities
clone,
equals,
serialize,
topKSimilar,
distance,
isOrthogonal,
// Internal class (for advanced use)
Vector: YourVector
};
export default yourStrategy;
Edit src/hdc/strategies/index.mjs:
import { denseBinaryStrategy } from './dense-binary.mjs';
import { yourStrategy } from './your-strategy.mjs';
const strategies = new Map();
// Register strategies
strategies.set('dense-binary', denseBinaryStrategy);
strategies.set('your-strategy', yourStrategy); // ADD THIS
export function getStrategy(strategyId) {
const strategy = strategies.get(strategyId);
if (!strategy) {
throw new Error(`Unknown strategy: ${strategyId}`);
}
return strategy;
}
// ... rest of file
Use the built-in validator to check your strategy:
import { validateStrategy } from './src/hdc/contract.mjs';
import { yourStrategy } from './src/hdc/strategies/your-strategy.mjs';
const result = validateStrategy(yourStrategy, 2048);
if (result.valid) {
console.log('Strategy passes contract!');
} else {
console.log('Contract violations:');
for (const error of result.errors) {
console.log(' -', error);
}
}
# Run eval suite with your strategy
SYS2_HDC_STRATEGY=your-strategy npm run eval
import { compareStrategies } from './src/hdc/facade.mjs';
const results = compareStrategies(
['dense-binary', 'your-strategy'],
8192,
{ iterations: 1000 }
);
console.log(JSON.stringify(results, null, 2));
A sparse polynomial strategy might represent vectors as polynomials over GF(2)[x]:
class SparsePolyVector {
constructor(geometry) {
this.geometry = geometry;
this.strategyId = 'sparse-polynomial';
// Store as Map<exponent, coefficient>
this.terms = new Map();
}
}
function bind(a, b) {
// Polynomial multiplication mod irreducible polynomial
const result = new SparsePolyVector(a.geometry);
for (const [expA, coefA] of a.terms) {
for (const [expB, coefB] of b.terms) {
const newExp = (expA + expB) % a.geometry;
const newCoef = coefA * coefB;
result.terms.set(newExp,
(result.terms.get(newExp) || 0) + newCoef
);
}
}
return result;
}
function similarity(a, b) {
// Cosine similarity in polynomial coefficient space
let dotProduct = 0, normA = 0, normB = 0;
// ... calculate ...
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
| Memory | Sparse strategies use less memory for low-density vectors |
| Speed | Dense strategies are faster for dense vectors (SIMD-friendly) |
| Capacity | How many vectors can be bundled before accuracy drops? |
| Noise | How does noise accumulate through operations? |
The SPHDC strategy implements sparse HDC using Integer-XOR binding with Min-Hash sampling.
| Property | Dense Binary | Sparse Polynomial | Metric-Affine | Metric-Affine Elastic | EXACT |
| Dimensionality | Fixed (2048-4096 bits) | Infinite (k=4-8 exponents) | Fixed (32 byte channels) | Elastic (32+ byte channels) | Elastic (session-local atom indices) |
| Binding Operation | XOR (bitwise) | Symmetric difference | Affine transformation | Affine transformation | OR-product (bitset-polynomial) |
| Binding Speed | Fast (O(n/32)) | Slower (O(k²)) | Fastest (O(m)) | Fast (O(m) + chunk scan) | Depends on term counts (often fast for monomial facts; worst-case O(|A||B|)) |
| Memory per Vector | 256-512 bytes | 32 bytes (k=4 default) | 32 bytes | 32B+ (chunked bundles) | Variable (lossless set of BigInt monomials) |
| Similarity Metric | Hamming distance | Jaccard index | Channel overlap | Channel overlap (max over chunks) | Jaccard over monomials (plus witness-based decode/cleanup) |
| Performance (Core Theory) | 516ms (symbolic) | 523ms (symbolic) | 225ms (symbolic) ⚡ 2.3x faster | Similar to Metric-Affine (small KBs) | Varies; used as an upper bound in saturation evaluation |
| XOR cancellation | Perfect | Approximate | Approximate | Approximate | Not required (UNBIND is quotient-like) |
| Commutative | Perfect | Perfect | Perfect | Perfect | Perfect |
| Best For | Research, standard HDC | Large KBs, memory-constrained | Production systems, speed-critical apps | Large KB superpositions, stable bundling | Research: lossless encoding + decoding/cleanup studies |
See the SPHDC Comparison Guide for detailed benchmarks and recommendations. For EMA internals, read Metric-Affine Elastic.
unbind() and may require decoding/cleanup.