Files
wifi-densepose/examples/prime-radiant/benches/category_bench.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

810 lines
24 KiB
Rust

//! Category Theory Benchmarks for Prime-Radiant
//!
//! Benchmarks for category-theoretic operations including:
//! - Functor application
//! - Morphism composition chains
//! - Topos operations (pullback, pushforward, exponential)
//! - Natural transformation computation
//!
//! Target metrics:
//! - Functor application: < 100us per object
//! - Composition chain (100 morphisms): < 1ms
//! - Topos pullback: < 500us
use criterion::{
black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
};
use std::collections::HashMap;
// ============================================================================
// CATEGORY THEORY TYPES
// ============================================================================
/// Object identifier
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct ObjectId(u64);
/// Morphism identifier
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct MorphismId(u64);
/// A morphism in a category
#[derive(Clone, Debug)]
struct Morphism {
id: MorphismId,
source: ObjectId,
target: ObjectId,
/// Linear transformation matrix (for VectorCategory)
matrix: Option<Vec<Vec<f64>>>,
}
/// Category structure
struct Category {
objects: HashMap<ObjectId, Object>,
morphisms: HashMap<MorphismId, Morphism>,
/// Composition table: (f, g) -> f . g
compositions: HashMap<(MorphismId, MorphismId), MorphismId>,
/// Identity morphisms
identities: HashMap<ObjectId, MorphismId>,
next_id: u64,
}
/// Object with associated data
#[derive(Clone, Debug)]
struct Object {
id: ObjectId,
dimension: usize,
data: Vec<f64>,
}
impl Category {
fn new() -> Self {
Self {
objects: HashMap::new(),
morphisms: HashMap::new(),
compositions: HashMap::new(),
identities: HashMap::new(),
next_id: 0,
}
}
fn add_object(&mut self, dimension: usize) -> ObjectId {
let id = ObjectId(self.next_id);
self.next_id += 1;
let obj = Object {
id,
dimension,
data: vec![0.0; dimension],
};
self.objects.insert(id, obj);
// Add identity morphism
let mor_id = MorphismId(self.next_id);
self.next_id += 1;
let identity_matrix = (0..dimension)
.map(|i| {
let mut row = vec![0.0; dimension];
row[i] = 1.0;
row
})
.collect();
let identity = Morphism {
id: mor_id,
source: id,
target: id,
matrix: Some(identity_matrix),
};
self.morphisms.insert(mor_id, identity);
self.identities.insert(id, mor_id);
id
}
fn add_morphism(&mut self, source: ObjectId, target: ObjectId, matrix: Vec<Vec<f64>>) -> MorphismId {
let id = MorphismId(self.next_id);
self.next_id += 1;
let morphism = Morphism {
id,
source,
target,
matrix: Some(matrix),
};
self.morphisms.insert(id, morphism);
id
}
fn compose(&mut self, f: MorphismId, g: MorphismId) -> Option<MorphismId> {
// Check if already composed
if let Some(&result) = self.compositions.get(&(f, g)) {
return Some(result);
}
let mor_f = self.morphisms.get(&f)?;
let mor_g = self.morphisms.get(&g)?;
// Check composability: target(g) = source(f)
if mor_g.target != mor_f.source {
return None;
}
// Compose matrices
let mat_f = mor_f.matrix.as_ref()?;
let mat_g = mor_g.matrix.as_ref()?;
let composed_matrix = matrix_multiply(mat_f, mat_g);
let new_id = self.add_morphism(mor_g.source, mor_f.target, composed_matrix);
self.compositions.insert((f, g), new_id);
Some(new_id)
}
fn compose_chain(&mut self, morphisms: &[MorphismId]) -> Option<MorphismId> {
if morphisms.is_empty() {
return None;
}
let mut result = morphisms[0];
for &mor in morphisms.iter().skip(1) {
result = self.compose(result, mor)?;
}
Some(result)
}
}
/// Matrix multiplication
fn matrix_multiply(a: &[Vec<f64>], b: &[Vec<f64>]) -> Vec<Vec<f64>> {
let m = a.len();
let n = if b.is_empty() { 0 } else { b[0].len() };
let k = b.len();
let mut result = vec![vec![0.0; n]; m];
for i in 0..m {
for j in 0..n {
let mut sum = 0.0;
for l in 0..k {
sum += a[i][l] * b[l][j];
}
result[i][j] = sum;
}
}
result
}
// ============================================================================
// FUNCTOR IMPLEMENTATION
// ============================================================================
/// A functor between categories
struct Functor {
/// Object mapping (encoded as transformation)
object_map: Box<dyn Fn(&Object) -> Object + Send + Sync>,
/// Morphism mapping
morphism_map: Box<dyn Fn(&Morphism) -> Morphism + Send + Sync>,
}
impl Functor {
/// Embedding functor: embeds into higher dimension
fn embedding(target_dim: usize) -> Self {
Self {
object_map: Box::new(move |obj| {
let mut data = obj.data.clone();
data.resize(target_dim, 0.0);
Object {
id: obj.id,
dimension: target_dim,
data,
}
}),
morphism_map: Box::new(move |mor| {
let matrix = mor.matrix.as_ref().map(|m| {
let old_dim = m.len();
let mut new_matrix = vec![vec![0.0; target_dim]; target_dim];
// Copy old matrix into top-left corner
for i in 0..old_dim {
for j in 0..m[i].len().min(target_dim) {
new_matrix[i][j] = m[i][j];
}
}
// Extend with identity
for i in old_dim..target_dim {
new_matrix[i][i] = 1.0;
}
new_matrix
});
Morphism {
id: mor.id,
source: mor.source,
target: mor.target,
matrix,
}
}),
}
}
/// Projection functor: projects to lower dimension
fn projection(target_dim: usize) -> Self {
Self {
object_map: Box::new(move |obj| {
let data: Vec<f64> = obj.data.iter().take(target_dim).copied().collect();
Object {
id: obj.id,
dimension: target_dim,
data,
}
}),
morphism_map: Box::new(move |mor| {
let matrix = mor.matrix.as_ref().map(|m| {
m.iter()
.take(target_dim)
.map(|row| row.iter().take(target_dim).copied().collect())
.collect()
});
Morphism {
id: mor.id,
source: mor.source,
target: mor.target,
matrix,
}
}),
}
}
fn apply_object(&self, obj: &Object) -> Object {
(self.object_map)(obj)
}
fn apply_morphism(&self, mor: &Morphism) -> Morphism {
(self.morphism_map)(mor)
}
}
// ============================================================================
// TOPOS OPERATIONS
// ============================================================================
/// Topos structure with subobject classifier
struct Topos {
base_category: Category,
/// Subobject classifier: true/false
omega: ObjectId,
/// Terminal object
terminal: ObjectId,
}
impl Topos {
fn new() -> Self {
let mut cat = Category::new();
// Add terminal object (1-dimensional)
let terminal = cat.add_object(1);
// Add subobject classifier (2-dimensional for true/false)
let omega = cat.add_object(2);
Self {
base_category: cat,
omega,
terminal,
}
}
/// Compute pullback of f: A -> C and g: B -> C
fn pullback(&mut self, f: MorphismId, g: MorphismId) -> Option<(ObjectId, MorphismId, MorphismId)> {
let mor_f = self.base_category.morphisms.get(&f)?;
let mor_g = self.base_category.morphisms.get(&g)?;
// Check that codomain matches
if mor_f.target != mor_g.target {
return None;
}
let obj_a = self.base_category.objects.get(&mor_f.source)?;
let obj_b = self.base_category.objects.get(&mor_g.source)?;
// Pullback object dimension is sum of source dimensions
let pullback_dim = obj_a.dimension + obj_b.dimension;
let pullback_obj = self.base_category.add_object(pullback_dim);
// Create projection morphisms
// p1: A x_C B -> A (projection to first factor)
let p1_matrix: Vec<Vec<f64>> = (0..obj_a.dimension)
.map(|i| {
let mut row = vec![0.0; pullback_dim];
row[i] = 1.0;
row
})
.collect();
// p2: A x_C B -> B (projection to second factor)
let p2_matrix: Vec<Vec<f64>> = (0..obj_b.dimension)
.map(|i| {
let mut row = vec![0.0; pullback_dim];
row[obj_a.dimension + i] = 1.0;
row
})
.collect();
let p1 = self.base_category.add_morphism(pullback_obj, mor_f.source, p1_matrix);
let p2 = self.base_category.add_morphism(pullback_obj, mor_g.source, p2_matrix);
Some((pullback_obj, p1, p2))
}
/// Compute exponential object B^A
fn exponential(&mut self, a: ObjectId, b: ObjectId) -> Option<ObjectId> {
let obj_a = self.base_category.objects.get(&a)?;
let obj_b = self.base_category.objects.get(&b)?;
// Exponential dimension is dim(B)^dim(A) (approximated as product)
let exp_dim = obj_a.dimension * obj_b.dimension;
let exp_obj = self.base_category.add_object(exp_dim);
Some(exp_obj)
}
/// Compute pushout of f: C -> A and g: C -> B
fn pushout(&mut self, f: MorphismId, g: MorphismId) -> Option<(ObjectId, MorphismId, MorphismId)> {
let mor_f = self.base_category.morphisms.get(&f)?;
let mor_g = self.base_category.morphisms.get(&g)?;
// Check that domain matches
if mor_f.source != mor_g.source {
return None;
}
let obj_a = self.base_category.objects.get(&mor_f.target)?;
let obj_b = self.base_category.objects.get(&mor_g.target)?;
// Pushout dimension
let pushout_dim = obj_a.dimension + obj_b.dimension;
let pushout_obj = self.base_category.add_object(pushout_dim);
// Create injection morphisms
let i1_matrix: Vec<Vec<f64>> = (0..pushout_dim)
.map(|i| {
if i < obj_a.dimension {
let mut row = vec![0.0; obj_a.dimension];
row[i] = 1.0;
row
} else {
vec![0.0; obj_a.dimension]
}
})
.collect();
let i2_matrix: Vec<Vec<f64>> = (0..pushout_dim)
.map(|i| {
if i >= obj_a.dimension {
let mut row = vec![0.0; obj_b.dimension];
row[i - obj_a.dimension] = 1.0;
row
} else {
vec![0.0; obj_b.dimension]
}
})
.collect();
let i1 = self.base_category.add_morphism(mor_f.target, pushout_obj, i1_matrix);
let i2 = self.base_category.add_morphism(mor_g.target, pushout_obj, i2_matrix);
Some((pushout_obj, i1, i2))
}
}
// ============================================================================
// NATURAL TRANSFORMATION
// ============================================================================
/// Natural transformation between functors
struct NaturalTransformation {
/// Component morphisms for each object
components: HashMap<ObjectId, Vec<Vec<f64>>>,
}
impl NaturalTransformation {
fn new() -> Self {
Self {
components: HashMap::new(),
}
}
fn add_component(&mut self, obj: ObjectId, matrix: Vec<Vec<f64>>) {
self.components.insert(obj, matrix);
}
fn apply_at(&self, obj: ObjectId, data: &[f64]) -> Option<Vec<f64>> {
let matrix = self.components.get(&obj)?;
Some(matvec(matrix, data))
}
/// Check naturality square for a morphism f: A -> B
fn check_naturality(&self, f: &Morphism, f_prime: &Morphism) -> bool {
// Check: F(f) . eta_A = eta_B . G(f)
let eta_a = match self.components.get(&f.source) {
Some(m) => m,
None => return false,
};
let eta_b = match self.components.get(&f.target) {
Some(m) => m,
None => return false,
};
let mat_f = match &f.matrix {
Some(m) => m,
None => return false,
};
let mat_f_prime = match &f_prime.matrix {
Some(m) => m,
None => return false,
};
// Left side: F(f) . eta_A
let left = matrix_multiply(mat_f_prime, eta_a);
// Right side: eta_B . G(f)
let right = matrix_multiply(eta_b, mat_f);
// Check equality (within tolerance)
matrices_equal(&left, &right, 1e-10)
}
}
fn matvec(matrix: &[Vec<f64>], vec: &[f64]) -> Vec<f64> {
matrix
.iter()
.map(|row| row.iter().zip(vec.iter()).map(|(a, b)| a * b).sum())
.collect()
}
fn matrices_equal(a: &[Vec<f64>], b: &[Vec<f64>], tol: f64) -> bool {
if a.len() != b.len() {
return false;
}
for (row_a, row_b) in a.iter().zip(b.iter()) {
if row_a.len() != row_b.len() {
return false;
}
for (va, vb) in row_a.iter().zip(row_b.iter()) {
if (va - vb).abs() > tol {
return false;
}
}
}
true
}
// ============================================================================
// BENCHMARK DATA GENERATORS
// ============================================================================
fn generate_random_matrix(rows: usize, cols: usize, seed: u64) -> Vec<Vec<f64>> {
let mut rng_state = seed;
(0..rows)
.map(|_| {
(0..cols)
.map(|_| {
rng_state = rng_state.wrapping_mul(6364136223846793005).wrapping_add(1);
((rng_state >> 33) as f64 / (u32::MAX as f64)) * 2.0 - 1.0
})
.collect()
})
.collect()
}
fn setup_category_with_chain(dimension: usize, chain_length: usize) -> (Category, Vec<MorphismId>) {
let mut cat = Category::new();
let mut objects = Vec::new();
let mut morphisms = Vec::new();
// Create chain of objects
for _ in 0..=chain_length {
objects.push(cat.add_object(dimension));
}
// Create chain of morphisms
for i in 0..chain_length {
let matrix = generate_random_matrix(dimension, dimension, (i as u64) * 42 + 1);
let mor = cat.add_morphism(objects[i], objects[i + 1], matrix);
morphisms.push(mor);
}
(cat, morphisms)
}
// ============================================================================
// BENCHMARKS
// ============================================================================
fn bench_functor_application(c: &mut Criterion) {
let mut group = c.benchmark_group("category/functor");
group.sample_size(100);
for &dim in &[16, 64, 128, 256] {
let target_dim = dim * 2;
let embedding = Functor::embedding(target_dim);
let projection = Functor::projection(dim / 2);
let obj = Object {
id: ObjectId(0),
dimension: dim,
data: (0..dim).map(|i| (i as f64).sin()).collect(),
};
let mor = Morphism {
id: MorphismId(0),
source: ObjectId(0),
target: ObjectId(1),
matrix: Some(generate_random_matrix(dim, dim, 42)),
};
group.throughput(Throughput::Elements(dim as u64));
group.bench_with_input(
BenchmarkId::new("embedding_object", dim),
&(&embedding, &obj),
|b, (functor, obj)| {
b.iter(|| black_box(functor.apply_object(black_box(obj))))
},
);
group.bench_with_input(
BenchmarkId::new("embedding_morphism", dim),
&(&embedding, &mor),
|b, (functor, mor)| {
b.iter(|| black_box(functor.apply_morphism(black_box(mor))))
},
);
group.bench_with_input(
BenchmarkId::new("projection_object", dim),
&(&projection, &obj),
|b, (functor, obj)| {
b.iter(|| black_box(functor.apply_object(black_box(obj))))
},
);
}
group.finish();
}
fn bench_composition_chains(c: &mut Criterion) {
let mut group = c.benchmark_group("category/composition");
group.sample_size(50);
for &chain_length in &[10, 50, 100, 200] {
let dim = 32;
let (mut cat, morphisms) = setup_category_with_chain(dim, chain_length);
group.throughput(Throughput::Elements(chain_length as u64));
group.bench_with_input(
BenchmarkId::new("sequential", chain_length),
&morphisms,
|b, morphisms| {
b.iter_batched(
|| {
let (cat, _) = setup_category_with_chain(dim, chain_length);
cat
},
|mut cat| {
let mut result = morphisms[0];
for &mor in morphisms.iter().skip(1) {
result = cat.compose(result, mor).unwrap();
}
black_box(result)
},
criterion::BatchSize::SmallInput,
)
},
);
group.bench_with_input(
BenchmarkId::new("chain_compose", chain_length),
&morphisms,
|b, morphisms| {
b.iter_batched(
|| {
let (cat, _) = setup_category_with_chain(dim, chain_length);
cat
},
|mut cat| black_box(cat.compose_chain(morphisms)),
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();
}
fn bench_topos_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("category/topos");
group.sample_size(50);
for &dim in &[8, 16, 32, 64] {
group.throughput(Throughput::Elements(dim as u64));
// Setup for pullback
group.bench_with_input(
BenchmarkId::new("pullback", dim),
&dim,
|b, &dim| {
b.iter_batched(
|| {
let mut topos = Topos::new();
let a = topos.base_category.add_object(dim);
let b = topos.base_category.add_object(dim);
let c = topos.base_category.add_object(dim);
let mat_f = generate_random_matrix(dim, dim, 42);
let mat_g = generate_random_matrix(dim, dim, 43);
let f = topos.base_category.add_morphism(a, c, mat_f);
let g = topos.base_category.add_morphism(b, c, mat_g);
(topos, f, g)
},
|(mut topos, f, g)| black_box(topos.pullback(f, g)),
criterion::BatchSize::SmallInput,
)
},
);
// Pushout
group.bench_with_input(
BenchmarkId::new("pushout", dim),
&dim,
|b, &dim| {
b.iter_batched(
|| {
let mut topos = Topos::new();
let c = topos.base_category.add_object(dim);
let a = topos.base_category.add_object(dim);
let b = topos.base_category.add_object(dim);
let mat_f = generate_random_matrix(dim, dim, 44);
let mat_g = generate_random_matrix(dim, dim, 45);
let f = topos.base_category.add_morphism(c, a, mat_f);
let g = topos.base_category.add_morphism(c, b, mat_g);
(topos, f, g)
},
|(mut topos, f, g)| black_box(topos.pushout(f, g)),
criterion::BatchSize::SmallInput,
)
},
);
// Exponential
group.bench_with_input(
BenchmarkId::new("exponential", dim),
&dim,
|b, &dim| {
b.iter_batched(
|| {
let mut topos = Topos::new();
let a = topos.base_category.add_object(dim);
let b = topos.base_category.add_object(dim);
(topos, a, b)
},
|(mut topos, a, b)| black_box(topos.exponential(a, b)),
criterion::BatchSize::SmallInput,
)
},
);
}
group.finish();
}
fn bench_natural_transformation(c: &mut Criterion) {
let mut group = c.benchmark_group("category/natural_transformation");
group.sample_size(50);
for &dim in &[16, 32, 64, 128] {
let mut nat_trans = NaturalTransformation::new();
// Add components for multiple objects
for i in 0..10 {
let matrix = generate_random_matrix(dim, dim, i * 42);
nat_trans.add_component(ObjectId(i), matrix);
}
let data: Vec<f64> = (0..dim).map(|i| (i as f64).sin()).collect();
group.throughput(Throughput::Elements(dim as u64));
group.bench_with_input(
BenchmarkId::new("apply_component", dim),
&(&nat_trans, ObjectId(0), &data),
|b, (nat_trans, obj, data)| {
b.iter(|| black_box(nat_trans.apply_at(*obj, black_box(data))))
},
);
// Setup naturality check
let f = Morphism {
id: MorphismId(0),
source: ObjectId(0),
target: ObjectId(1),
matrix: Some(generate_random_matrix(dim, dim, 100)),
};
let f_prime = Morphism {
id: MorphismId(1),
source: ObjectId(0),
target: ObjectId(1),
matrix: Some(generate_random_matrix(dim, dim, 101)),
};
group.bench_with_input(
BenchmarkId::new("check_naturality", dim),
&(&nat_trans, &f, &f_prime),
|b, (nat_trans, f, f_prime)| {
b.iter(|| black_box(nat_trans.check_naturality(black_box(f), black_box(f_prime))))
},
);
}
group.finish();
}
fn bench_matrix_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("category/matrix");
group.sample_size(50);
for &dim in &[32, 64, 128, 256] {
let a = generate_random_matrix(dim, dim, 42);
let b = generate_random_matrix(dim, dim, 43);
let v: Vec<f64> = (0..dim).map(|i| (i as f64).sin()).collect();
group.throughput(Throughput::Elements((dim * dim) as u64));
group.bench_with_input(
BenchmarkId::new("multiply", dim),
&(&a, &b),
|b, (a, b_mat)| {
b.iter(|| black_box(matrix_multiply(black_box(a), black_box(b_mat))))
},
);
group.bench_with_input(
BenchmarkId::new("matvec", dim),
&(&a, &v),
|b, (a, v)| {
b.iter(|| black_box(matvec(black_box(a), black_box(v))))
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_functor_application,
bench_composition_chains,
bench_topos_operations,
bench_natural_transformation,
bench_matrix_operations,
);
criterion_main!(benches);