Squashed 'vendor/ruvector/' content from commit b64c2172

git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
commit d803bfe2b1
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,525 @@
# Prime-Radiant Security Audit Report
**Audit Date:** 2026-01-22
**Auditor:** V3 Security Architect
**Crate:** prime-radiant (Coherence Engine)
**Scope:** Memory safety, input validation, cryptographic concerns, WASM security, dependencies, code quality
---
## Executive Summary
The Prime-Radiant coherence engine demonstrates **strong security fundamentals** with several notable strengths:
- `#![deny(unsafe_code)]` enforced crate-wide
- Parameterized SQL queries preventing SQL injection
- Proper use of Result types throughout public APIs
- Well-defined error types with thiserror
However, **17 security issues** were identified across the following categories:
| Severity | Count | Description |
|----------|-------|-------------|
| HIGH | 3 | Input validation gaps, panic-on-invalid-input |
| MEDIUM | 8 | Numerical stability, resource exhaustion potential |
| LOW | 4 | Code quality improvements, hardening recommendations |
| INFO | 2 | Best practice recommendations |
---
## 1. Memory Safety Analysis
### 1.1 Unsafe Code Status: PASS
The crate explicitly denies unsafe code:
```rust
// /crates/prime-radiant/src/lib.rs:143
#![deny(unsafe_code)]
```
This is excellent and enforced at compile time. No unsafe blocks exist in the codebase.
### 1.2 Buffer Operations: MOSTLY SAFE
**SIMD Vector Operations** (`src/simd/vectors.rs`):
- Uses `debug_assert!` for length checks (lines 50, 196-197, 286, 369-371)
- These assertions only fire in debug mode; release builds skip validation
**FINDING [MED-1]: Release-Mode Bounds Check Missing**
```rust
// src/simd/vectors.rs:49-50
pub fn dot_product_simd(a: &[f32], b: &[f32]) -> f32 {
debug_assert_eq!(a.len(), b.len(), "Vectors must have equal length");
// In release mode, mismatched lengths cause undefined behavior
```
**Recommendation:** Replace `debug_assert!` with proper Result-returning validation for public APIs.
### 1.3 GPU Buffer Operations: SAFE
Buffer management (`src/gpu/buffer.rs`) properly validates:
- Buffer size limits (line 516): `if size > super::MAX_BUFFER_SIZE`
- Buffer size mismatches (line 182-187): Returns `GpuError::BufferSizeMismatch`
- Pool capacity limits (line 555): Enforces `max_pool_size`
---
## 2. Input Validation Analysis
### 2.1 Graph Size Limits: PARTIAL
**FINDING [HIGH-1]: No Maximum Graph Size Limit**
The `SheafGraph` (`src/substrate/graph.rs`) allows unbounded growth:
```rust
pub fn add_node(&self, node: SheafNode) -> NodeId {
// No limit on node count
self.nodes.insert(id, node);
```
**DoS Risk:** An attacker could exhaust memory by adding unlimited nodes/edges.
**Recommendation:** Add configurable limits:
```rust
pub struct GraphLimits {
pub max_nodes: usize, // Default: 1_000_000
pub max_edges: usize, // Default: 10_000_000
pub max_state_dim: usize, // Default: 65536
}
```
### 2.2 Matrix Dimension Validation: PARTIAL
**FINDING [MED-2]: Large Matrix Allocation Without Bounds**
`RestrictionMap::identity()` allocates `dim * dim` without upper bound:
```rust
// src/coherence/engine.rs:214-225
pub fn identity(dim: usize) -> Self {
let mut matrix = vec![0.0; dim * dim]; // Unbounded!
```
With `dim = 2^16`, this allocates 16GB.
**Recommendation:** Add dimension caps (suggested: 65536 for matrices).
### 2.3 File Path Validation: SAFE
PostgreSQL storage (`src/storage/postgres.rs`) uses parameterized queries:
```rust
// Line 362-377 - properly parameterized
sqlx::query("INSERT INTO node_states (node_id, state, dimension, updated_at) VALUES ($1, $2, $3, NOW())")
.bind(node_id)
.bind(state)
```
File storage (`src/storage/file.rs`) constructs paths but does not sanitize for traversal:
**FINDING [MED-3]: Potential Path Traversal in FileStorage**
```rust
// src/storage/file.rs:279-281
fn node_path(&self, node_id: &str) -> PathBuf {
let ext = if self.format == StorageFormat::Json { "json" } else { "bin" };
self.root.join("nodes").join(format!("{}.{}", node_id, ext))
}
```
If `node_id = "../../../etc/passwd"`, this creates a traversal vector.
**Recommendation:** Validate node_id contains only alphanumeric, dash, underscore characters.
### 2.4 Signal Validation: EXISTS
The `SignalValidator` (`src/signal/validation.rs`) provides:
- Maximum payload size validation (default 1MB)
- Signal type allowlisting
- Source non-empty validation
This is good but could be expanded.
---
## 3. Numerical Stability Analysis
### 3.1 NaN/Infinity Handling: INCOMPLETE
**FINDING [MED-4]: No NaN Checks on Input States**
State vectors accept NaN/Infinity without validation:
```rust
// src/substrate/node.rs
pub fn update_state_from_slice(&mut self, new_state: &[f32]) {
self.state = StateVector::from_slice(new_state);
// No NaN check
```
NaN propagates through all coherence computations silently.
**Locations using special float values:**
- `src/hyperbolic/mod.rs:217`: `f32::MAX` for min_depth
- `src/mincut/metrics.rs:55`: `f64::INFINITY` for min_cut_value
- `src/attention/moe.rs:199`: `f32::NEG_INFINITY` for max logit
- `src/ruvllm_integration/confidence.rs:376-379`: NaN for error states
**Recommendation:** Add validation helper:
```rust
pub fn validate_state(state: &[f32]) -> Result<(), ValidationError> {
if state.iter().any(|x| x.is_nan() || x.is_infinite()) {
return Err(ValidationError::InvalidFloat);
}
Ok(())
}
```
### 3.2 Division Safety: PARTIAL
Cosine similarity (`src/storage/postgres.rs:861-875`) properly handles zero norms:
```rust
if norm_a == 0.0 || norm_b == 0.0 {
return 0.0;
}
```
However, other locations may divide without checking.
---
## 4. Cryptographic Analysis
### 4.1 Random Number Generation: MIXED
**Good (Deterministic Seeds):**
```rust
// src/coherence/engine.rs:248-249
use rand::{Rng, SeedableRng};
let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
```
This is appropriate for reproducible restriction maps.
**FINDING [MED-5]: Non-Cryptographic RNG for Node IDs**
```rust
// src/substrate/node.rs:48-49
use rand::Rng;
let mut rng = rand::thread_rng();
```
`thread_rng()` is not cryptographically secure. While likely used for test data, if node IDs need unpredictability, use `OsRng` or `getrandom`.
### 4.2 Hash Functions: GOOD
The crate uses `blake3` for WAL checksums (`src/storage/file.rs:51-52`):
```rust
let checksum = *blake3::hash(&op_bytes).as_bytes();
```
Blake3 is cryptographically strong and appropriate.
### 4.3 No Hardcoded Secrets: PASS
Searched codebase for hardcoded credentials, API keys, passwords - none found.
---
## 5. WASM-Specific Security
### 5.1 Memory Isolation: HANDLED BY WASM RUNTIME
The tiles module uses 256 WASM tiles. WASM provides:
- Linear memory isolation
- Control flow integrity
- Type safety at boundaries
### 5.2 Data Cleanup: NOT EXPLICITLY HANDLED
**FINDING [LOW-1]: No Explicit Memory Zeroization**
Sensitive data in WASM memory (e.g., state vectors) is not explicitly zeroed after use. While WASM memory is isolated per instance, zeroing before deallocation is defense-in-depth.
**Recommendation:** For sensitive operations, use `zeroize` crate.
### 5.3 JS Boundary Error Handling: GOOD
The GPU module returns proper `GpuResult<T>` types across all boundaries.
---
## 6. Dependency Analysis
### 6.1 Cargo.toml Dependencies
Based on `/crates/prime-radiant/Cargo.toml`:
| Dependency | Version | Known CVEs | Status |
|------------|---------|------------|--------|
| blake3 | 1.5 | None | OK |
| bytemuck | 1.21 | None | OK |
| chrono | 0.4 | None (0.4.35+) | OK |
| dashmap | 6.0 | None | OK |
| parking_lot | 0.12 | None | OK |
| rayon | 1.10 | None | OK |
| serde | 1.0 | None | OK |
| sqlx | 0.8 | None | OK |
| thiserror | 2.0 | None | OK |
| uuid | 1.10 | None | OK |
| wgpu | 22.1 | None | OK |
| wide | 0.7 | None | OK |
| bincode | 2.0.0-rc.3 | None | OK (RC) |
**FINDING [LOW-2]: Using Release Candidate Dependency**
`bincode = "2.0.0-rc.3"` is a release candidate. Consider pinning to stable when available.
### 6.2 Minimal Dependency Surface: GOOD
The crate uses feature flags to minimize attack surface:
```toml
[features]
default = []
postgres = ["sqlx/postgres"]
gpu = ["wgpu"]
simd = []
parallel = ["rayon"]
```
Only required features are compiled.
---
## 7. Code Quality Issues
### 7.1 Panic-Inducing Code
**FINDING [HIGH-2]: panic! in Library Code**
```rust
// src/distributed/adapter.rs:340
panic!("Wrong command type");
```
Library code should never panic; use Result instead.
**FINDING [HIGH-3]: unwrap() in Non-Test Code**
```rust
// src/governance/witness.rs:564
self.head.as_ref().unwrap()
```
This can panic if `head` is `None`.
**FINDING [MED-6]: expect() in Builders Without Validation**
```rust
// src/substrate/node.rs:454
let state = self.state.expect("State vector is required");
```
Builder pattern should return `Result<T, BuilderError>` instead of panicking.
### 7.2 Incomplete Error Propagation
Some locations use `.unwrap()` in test code (acceptable) but several are in production paths. Full list of production unwrap() calls:
1. `src/storage/file.rs:49` - WAL entry creation (partially justified)
2. `src/simd/vectors.rs:499` - SIMD array conversion
3. `src/simd/matrix.rs:390` - SIMD array conversion
4. `src/simd/energy.rs:523` - SIMD array conversion
5. `src/governance/witness.rs:564` - Head access
### 7.3 Timing Attack Considerations
**FINDING [MED-7]: Non-Constant-Time Comparisons**
Hash comparisons in WAL verification use standard equality:
```rust
// src/storage/file.rs:63
fn verify(&self) -> bool {
self.checksum == *blake3::hash(&bytes).as_bytes()
}
```
For security-critical hash comparisons, use constant-time comparison to prevent timing attacks:
```rust
use subtle::ConstantTimeEq;
self.checksum.ct_eq(&hash).into()
```
---
## 8. Recommendations Summary
### Critical (Address Immediately)
| ID | Issue | File | Line | Fix |
|----|-------|------|------|-----|
| HIGH-1 | No graph size limits | substrate/graph.rs | 312 | Add `GraphLimits` config |
| HIGH-2 | panic! in library | distributed/adapter.rs | 340 | Return Result |
| HIGH-3 | unwrap() on Option | governance/witness.rs | 564 | Return Result |
### High Priority (Address in Phase 1)
| ID | Issue | File | Fix |
|----|-------|------|-----|
| MED-1 | Release-mode bounds | simd/vectors.rs | Add runtime validation |
| MED-2 | Unbounded matrix allocation | coherence/engine.rs | Add dimension cap |
| MED-3 | Path traversal potential | storage/file.rs | Validate node_id |
| MED-4 | No NaN/Inf validation | substrate/node.rs | Add float validation |
### Medium Priority (Address in Phase 2)
| ID | Issue | File | Fix |
|----|-------|------|-----|
| MED-5 | Non-crypto RNG | substrate/node.rs | Document or use OsRng |
| MED-6 | expect() in builders | substrate/*.rs | Return Result |
| MED-7 | Timing attacks | storage/file.rs | Use constant-time |
### Low Priority (Best Practices)
| ID | Issue | Fix |
|----|-------|-----|
| LOW-1 | No memory zeroization | Use `zeroize` for sensitive data |
| LOW-2 | RC dependency | Pin bincode to stable when available |
---
## 9. Production Deployment Recommendations
### 9.1 Resource Limits
Configure these limits before production deployment:
```rust
let config = CoherenceConfig {
max_nodes: 1_000_000,
max_edges: 10_000_000,
max_state_dimension: 4096,
max_matrix_dimension: 8192,
max_payload_size: 10 * 1024 * 1024, // 10MB
max_concurrent_computations: 100,
};
```
### 9.2 Input Validation Layer
Add a validation middleware for all external inputs:
```rust
pub struct SecureInputValidator {
pub max_state_dim: usize,
pub max_node_id_len: usize,
pub allowed_id_chars: Regex,
}
impl SecureInputValidator {
pub fn validate_node_id(&self, id: &str) -> Result<(), ValidationError> {
if id.len() > self.max_node_id_len {
return Err(ValidationError::IdTooLong);
}
if !self.allowed_id_chars.is_match(id) {
return Err(ValidationError::InvalidIdChars);
}
Ok(())
}
pub fn validate_state(&self, state: &[f32]) -> Result<(), ValidationError> {
if state.len() > self.max_state_dim {
return Err(ValidationError::StateTooLarge);
}
if state.iter().any(|x| x.is_nan() || x.is_infinite()) {
return Err(ValidationError::InvalidFloat);
}
Ok(())
}
}
```
### 9.3 Monitoring
Add these security-relevant metrics:
- Graph size (nodes, edges)
- Failed validation attempts
- Memory usage per operation
- Unusual pattern detection (rapid adds, large states)
### 9.4 Rate Limiting
Implement rate limiting for:
- Node/edge additions per client
- Energy computation requests
- File storage operations
---
## 10. Compliance Notes
### 10.1 Rust Security Best Practices
| Practice | Status |
|----------|--------|
| No unsafe code | PASS |
| Proper error types | PASS |
| Result over panic | PARTIAL |
| Input validation | PARTIAL |
| Dependency management | PASS |
### 10.2 OWASP Considerations
| Risk | Mitigation Status |
|------|-------------------|
| Injection | PASS (parameterized SQL) |
| Broken Auth | N/A (no auth in crate) |
| Sensitive Data | PARTIAL (no zeroization) |
| XXE | N/A (no XML) |
| Access Control | N/A (application layer) |
| Misconfig | PARTIAL (needs limits) |
| XSS | N/A (no web output) |
| Deserialization | PASS (serde/bincode safe) |
| Logging | PARTIAL (needs audit logs) |
| SSRF | N/A |
---
## Appendix A: Files Audited
```
src/
├── lib.rs
├── error.rs
├── coherence/engine.rs
├── distributed/adapter.rs
├── governance/
│ ├── mod.rs
│ ├── witness.rs
│ ├── lineage.rs
│ └── repository.rs
├── gpu/
│ ├── mod.rs
│ └── buffer.rs
├── hyperbolic/
│ ├── mod.rs
│ ├── adapter.rs
│ └── energy.rs
├── simd/
│ ├── mod.rs
│ ├── vectors.rs
│ ├── matrix.rs
│ └── energy.rs
├── signal/
│ ├── mod.rs
│ ├── validation.rs
│ └── ingestion.rs
├── storage/
│ ├── mod.rs
│ ├── file.rs
│ └── postgres.rs
├── substrate/
│ ├── graph.rs
│ ├── node.rs
│ ├── edge.rs
│ └── restriction.rs
└── tiles/
├── mod.rs
├── adapter.rs
└── coordinator.rs
```
---
**Report Generated:** 2026-01-22
**Next Audit Recommended:** 2026-04-22 (quarterly)

View File

@@ -0,0 +1,333 @@
# ADR-001: Sheaf Cohomology for AI Coherence
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
Large Language Models and AI agents frequently produce outputs that are locally plausible but globally inconsistent. Traditional approaches to detecting such "hallucinations" rely on:
1. **Confidence scores**: Unreliable due to overconfidence on out-of-distribution inputs
2. **Retrieval augmentation**: Helps but doesn't verify consistency across retrieved facts
3. **Chain-of-thought verification**: Manual and prone to same failures as original reasoning
4. **Ensemble methods**: Expensive and still vulnerable to correlated errors
We need a mathematical framework that can:
- Detect **local-to-global consistency** failures systematically
- Provide **quantitative measures** of coherence
- Support **incremental updates** as new information arrives
- Work across **multiple domains** with the same underlying math
### Why Sheaf Theory?
Sheaf theory was developed in algebraic geometry and topology precisely to handle local-to-global problems. A sheaf assigns data to open sets in a way that:
1. **Locality**: Information at a point is determined by nearby information
2. **Gluing**: Locally consistent data can be assembled into global data
3. **Restriction**: Global data determines local data uniquely
These properties exactly match our coherence requirements:
- AI claims are local (about specific facts)
- Coherent knowledge should glue together globally
- Contradictions appear when local data fails to extend globally
---
## Decision
We implement **cellular sheaf cohomology** on graphs as the mathematical foundation for Prime-Radiant's coherence engine.
### Mathematical Foundation
#### Definition: Sheaf on a Graph
A **cellular sheaf** F on a graph G = (V, E) assigns:
1. To each vertex v, a vector space F(v) (the **stalk** at v)
2. To each edge e = (u,v), a vector space F(e)
3. For each vertex v incident to edge e, a linear map (the **restriction map**):
```
rho_{v,e}: F(v) -> F(e)
```
#### Definition: Residual
For an edge e = (u,v) with vertex states x_u in F(u) and x_v in F(v), the **residual** is:
```
r_e = rho_{u,e}(x_u) - rho_{v,e}(x_v)
```
The residual measures local inconsistency: if states agree through their restriction maps, r_e = 0.
#### Definition: Sheaf Laplacian
The **sheaf Laplacian** L is the block matrix:
```
L = D^T W D
```
where:
- D is the coboundary map (encodes graph topology and restriction maps)
- W is a diagonal weight matrix for edges
The quadratic form x^T L x = sum_e w_e ||r_e||^2 computes total coherence energy.
#### Definition: Cohomology Groups
The **first cohomology group** H^1(G, F) measures obstruction to finding a global section:
```
H^1(G, F) = ker(delta_1) / im(delta_0)
```
where delta_i are coboundary maps. If H^1 is non-trivial, the sheaf admits no global section (global inconsistency exists).
### Implementation Architecture
```rust
/// A sheaf on a graph with fixed-dimensional stalks
pub struct SheafGraph {
/// Node stalks: state vectors at each vertex
nodes: HashMap<NodeId, StateVector>,
/// Edge stalks and restriction maps
edges: HashMap<EdgeId, SheafEdge>,
/// Cached Laplacian blocks for incremental updates
laplacian_cache: LaplacianCache,
}
/// A restriction map implemented as a matrix
pub struct RestrictionMap {
/// The linear map as a matrix (output_dim x input_dim)
matrix: Array2<f32>,
/// Input dimension (node stalk dimension)
input_dim: usize,
/// Output dimension (edge stalk dimension)
output_dim: usize,
}
impl RestrictionMap {
/// Apply the restriction map: rho(x)
pub fn apply(&self, x: &[f32]) -> Vec<f32> {
self.matrix.dot(&ArrayView1::from(x)).to_vec()
}
/// Identity restriction (node stalk = edge stalk)
pub fn identity(dim: usize) -> Self {
Self {
matrix: Array2::eye(dim),
input_dim: dim,
output_dim: dim,
}
}
/// Projection restriction (edge stalk is subset of node stalk)
pub fn projection(input_dim: usize, output_dim: usize) -> Self {
let mut matrix = Array2::zeros((output_dim, input_dim));
for i in 0..output_dim.min(input_dim) {
matrix[[i, i]] = 1.0;
}
Self { matrix, input_dim, output_dim }
}
}
```
### Cohomology Computation
```rust
/// Compute the first cohomology dimension
pub fn cohomology_dimension(&self) -> usize {
// Build coboundary matrix D
let d = self.build_coboundary_matrix();
// Compute rank using SVD
let svd = d.svd(true, true).unwrap();
let rank = svd.singular_values
.iter()
.filter(|&s| *s > 1e-10)
.count();
// dim H^1 = dim(edge stalks) - rank(D)
let edge_dim: usize = self.edges.values()
.map(|e| e.stalk_dim)
.sum();
edge_dim.saturating_sub(rank)
}
/// Check if sheaf admits a global section
pub fn has_global_section(&self) -> bool {
self.cohomology_dimension() == 0
}
```
### Energy Computation
The total coherence energy is:
```rust
/// Compute total coherence energy: E = sum_e w_e ||r_e||^2
pub fn coherence_energy(&self) -> f32 {
self.edges.values()
.map(|edge| {
let source = &self.nodes[&edge.source];
let target = &self.nodes[&edge.target];
// Apply restriction maps
let rho_s = edge.source_restriction.apply(&source.state);
let rho_t = edge.target_restriction.apply(&target.state);
// Compute residual
let residual: Vec<f32> = rho_s.iter()
.zip(rho_t.iter())
.map(|(a, b)| a - b)
.collect();
// Weighted squared norm
let norm_sq: f32 = residual.iter().map(|r| r * r).sum();
edge.weight * norm_sq
})
.sum()
}
```
### Incremental Updates
For efficiency, we maintain a **residual cache** and update incrementally:
```rust
/// Update a single node and recompute affected energies
pub fn update_node(&mut self, node_id: NodeId, new_state: Vec<f32>) {
// Store old state for delta computation
let old_state = self.nodes.insert(node_id, new_state.clone());
// Only recompute residuals for edges incident to this node
for edge_id in self.edges_incident_to(node_id) {
self.recompute_residual(edge_id);
}
// Update fingerprint
self.update_fingerprint(node_id, &old_state, &new_state);
}
```
---
## Consequences
### Positive
1. **Mathematically Grounded**: Sheaf cohomology provides rigorous foundations for coherence
2. **Domain Agnostic**: Same math applies to facts, financial signals, medical data, etc.
3. **Local-to-Global Detection**: Naturally captures the essence of hallucination (local OK, global wrong)
4. **Incremental Computation**: Residual caching enables real-time updates
5. **Spectral Analysis**: Sheaf Laplacian eigenvalues provide drift detection
6. **Quantitative Measure**: Energy gives a continuous coherence score, not just binary
### Negative
1. **Computational Cost**: Full cohomology computation is O(n^3) for n nodes
2. **Restriction Map Design**: Choosing appropriate rho requires domain knowledge
3. **Curse of Dimensionality**: High-dimensional stalks increase memory and compute
4. **Learning Complexity**: Non-trivial to learn restriction maps from data
### Mitigations
1. **Incremental Updates**: Avoid full recomputation for small changes
2. **Learned rho**: GNN-based restriction map learning (see `learned-rho` feature)
3. **Dimensional Reduction**: Use projection restriction maps to reduce edge stalk dimension
4. **Subpolynomial MinCut**: Use for approximation when full computation is infeasible
---
## Mathematical Properties
### Theorem: Energy Minimization
If the sheaf Laplacian L has full column rank, the minimum energy configuration is unique:
```
x* = argmin_x ||Dx||^2_W = L^+ b
```
where L^+ is the pseudoinverse and b encodes boundary conditions.
### Theorem: Cheeger Inequality
The spectral gap (second smallest eigenvalue) of L relates to graph cuts:
```
lambda_2 / 2 <= h(G) <= sqrt(2 * lambda_2)
```
where h(G) is the Cheeger constant. This enables **cut prediction** from spectral analysis.
### Theorem: Hodge Decomposition
The space of edge states decomposes:
```
C^1(G, F) = im(delta_0) + ker(delta_1) + H^1(G, F)
```
This separates gradient flows (consistent), harmonic forms (neutral), and cohomology (obstructions).
---
## Related Decisions
- [ADR-004: Spectral Invariants](ADR-004-spectral-invariants.md) - Uses sheaf Laplacian eigenvalues
- [ADR-002: Category Theory](ADR-002-category-topos.md) - Sheaves are presheaves satisfying gluing
- [ADR-003: Homotopy Type Theory](ADR-003-homotopy-type-theory.md) - Higher sheaves and stacks
---
## References
1. Hansen, J., & Ghrist, R. (2019). "Toward a spectral theory of cellular sheaves." Journal of Applied and Computational Topology.
2. Curry, J. (2014). "Sheaves, Cosheaves and Applications." PhD thesis, University of Pennsylvania.
3. Robinson, M. (2014). "Topological Signal Processing." Springer.
4. Bodnar, C., et al. (2022). "Neural Sheaf Diffusion: A Topological Perspective on Heterophily and Oversmoothing in GNNs." NeurIPS.
5. Ghrist, R. (2014). "Elementary Applied Topology." Createspace.
---
## Appendix: Worked Example
Consider a knowledge graph with three facts:
- F1: "Paris is the capital of France" (state: [1, 0, 0, 1])
- F2: "France is in Europe" (state: [0, 1, 1, 0])
- F3: "Paris is not in Europe" (state: [1, 0, 0, -1]) -- HALLUCINATION
Edges with identity restriction maps:
- E1: F1 -> F2 (France connection)
- E2: F1 -> F3 (Paris connection)
- E3: F2 -> F3 (Europe connection)
Residuals:
- r_{E1} = [1,0,0,1] - [0,1,1,0] = [1,-1,-1,1], ||r||^2 = 4
- r_{E2} = [1,0,0,1] - [1,0,0,-1] = [0,0,0,2], ||r||^2 = 4
- r_{E3} = [0,1,1,0] - [1,0,0,-1] = [-1,1,1,1], ||r||^2 = 4
Total energy = 4 + 4 + 4 = 12 (HIGH -- indicates hallucination)
If F3 were corrected to "Paris is in Europe" (state: [1,0,1,1]):
- r_{E3} = [0,1,1,0] - [1,0,1,1] = [-1,1,0,-1], ||r||^2 = 3
Energy decreases, indicating better coherence.

View File

@@ -0,0 +1,492 @@
# ADR-002: Category Theory and Topos-Theoretic Belief Models
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
While sheaf cohomology (ADR-001) provides the foundation for coherence measurement, we need higher-level abstractions for:
1. **Functorial Retrieval**: Structure-preserving access to knowledge across different representations
2. **Belief Dynamics**: Modeling how beliefs change under new evidence
3. **Higher Coherence Laws**: Ensuring consistency not just of facts, but of relationships between facts
4. **Intuitionistic Logic**: Handling partial or uncertain knowledge appropriately
Category theory provides the language for these abstractions, and topos theory extends this to handle logic and set-like constructions in coherent ways.
### Why Category Theory?
Category theory is the mathematics of structure and structure-preserving maps. It provides:
1. **Functors**: Maps between categories that preserve structure
2. **Natural Transformations**: Maps between functors that preserve relationships
3. **Limits and Colimits**: Universal constructions for combining and decomposing data
4. **Adjunctions**: Fundamental optimization principles
### Why Topos Theory?
A topos is a category that behaves like the category of sets but with a different internal logic. Topoi enable:
1. **Intuitionistic Logic**: Handle "not provably true" vs "provably false"
2. **Subobject Classifiers**: Generalized truth values beyond {true, false}
3. **Internal Languages**: Reason about objects using logical syntax
4. **Sheaf Semantics**: Interpret sheaves as generalized sets
---
## Decision
We implement a **functorial retrieval system** with topos-theoretic belief models for coherence management.
### Mathematical Foundation
#### Definition: Category of Knowledge Graphs
Let **KGraph** be the category where:
- Objects are knowledge graphs G = (V, E, F) with sheaf structure F
- Morphisms are graph homomorphisms that preserve sheaf structure:
```
phi: G -> G' such that phi_*(F) -> F'
```
#### Definition: Retrieval Functor
A **retrieval functor** R: Query -> KGraph assigns:
- To each query q, a subgraph R(q) of the knowledge base
- To each query refinement q -> q', a graph inclusion R(q) -> R(q')
Functoriality ensures that refining a query gives a consistent subgraph.
#### Definition: Belief Topos
The **belief topos** B(G) over a knowledge graph G is the category:
- Objects: Belief states (assignments of credences to nodes/edges)
- Morphisms: Belief updates under new evidence
- Subobject classifier: Omega = [0, 1] (credence values)
The internal logic is intuitionistic: for a proposition P,
- "P is true" means credence(P) = 1
- "P is false" means credence(P) = 0
- Otherwise, P has partial truth value
### Implementation Architecture
#### Functorial Retrieval
```rust
/// A category of knowledge representations
pub trait Category {
type Object;
type Morphism;
fn identity(obj: &Self::Object) -> Self::Morphism;
fn compose(f: &Self::Morphism, g: &Self::Morphism) -> Self::Morphism;
}
/// A functor between categories
pub trait Functor<C: Category, D: Category> {
fn map_object(&self, obj: &C::Object) -> D::Object;
fn map_morphism(&self, mor: &C::Morphism) -> D::Morphism;
// Functoriality laws (ensured by implementation)
// F(id_A) = id_{F(A)}
// F(g . f) = F(g) . F(f)
}
/// Query category: queries with refinement morphisms
pub struct QueryCategory;
impl Category for QueryCategory {
type Object = Query;
type Morphism = QueryRefinement;
fn identity(q: &Query) -> QueryRefinement {
QueryRefinement::identity(q.clone())
}
fn compose(f: &QueryRefinement, g: &QueryRefinement) -> QueryRefinement {
QueryRefinement::compose(f, g)
}
}
/// Retrieval functor from queries to knowledge subgraphs
pub struct RetrievalFunctor {
knowledge_base: Arc<SheafGraph>,
index: VectorIndex,
}
impl Functor<QueryCategory, KGraphCategory> for RetrievalFunctor {
fn map_object(&self, query: &Query) -> SheafSubgraph {
// Retrieve relevant subgraph for query
let node_ids = self.index.search(&query.embedding, query.k);
self.knowledge_base.extract_subgraph(&node_ids, query.hops)
}
fn map_morphism(&self, refinement: &QueryRefinement) -> SubgraphInclusion {
// Refinement yields inclusion of subgraphs
let source = self.map_object(&refinement.source);
let target = self.map_object(&refinement.target);
SubgraphInclusion::compute(&source, &target)
}
}
```
#### Natural Transformations
```rust
/// A natural transformation between functors
pub trait NaturalTransformation<C, D, F, G>
where
C: Category,
D: Category,
F: Functor<C, D>,
G: Functor<C, D>,
{
/// Component at object A: eta_A: F(A) -> G(A)
fn component(&self, obj: &C::Object) -> D::Morphism;
// Naturality: for f: A -> B,
// G(f) . eta_A = eta_B . F(f)
}
/// Coherence preservation transformation
pub struct CoherencePreservation {
source_functor: RetrievalFunctor,
target_functor: CoherenceAwareFunctor,
}
impl NaturalTransformation<QueryCategory, KGraphCategory,
RetrievalFunctor, CoherenceAwareFunctor>
for CoherencePreservation {
fn component(&self, query: &Query) -> SubgraphMap {
// Transform retrieval into coherence-filtered retrieval
let raw_subgraph = self.source_functor.map_object(query);
let filtered = self.filter_incoherent_edges(&raw_subgraph);
SubgraphMap::new(raw_subgraph, filtered)
}
}
```
#### Topos-Theoretic Belief Model
```rust
/// A topos of belief states over a knowledge graph
pub struct BeliefTopos {
graph: Arc<SheafGraph>,
/// Credence assignments: node/edge -> [0, 1]
credences: HashMap<EntityId, f32>,
/// Update history for rollback
history: Vec<BeliefUpdate>,
}
/// The subobject classifier Omega
pub struct TruthValue(f32);
impl TruthValue {
pub const TRUE: TruthValue = TruthValue(1.0);
pub const FALSE: TruthValue = TruthValue(0.0);
pub const UNKNOWN: TruthValue = TruthValue(0.5);
/// Intuitionistic negation: not(p) = p -> FALSE
pub fn not(&self) -> TruthValue {
if self.0 == 0.0 {
TruthValue::TRUE
} else {
TruthValue::FALSE
}
}
/// Intuitionistic conjunction
pub fn and(&self, other: &TruthValue) -> TruthValue {
TruthValue(self.0.min(other.0))
}
/// Intuitionistic disjunction
pub fn or(&self, other: &TruthValue) -> TruthValue {
TruthValue(self.0.max(other.0))
}
/// Intuitionistic implication
pub fn implies(&self, other: &TruthValue) -> TruthValue {
if self.0 <= other.0 {
TruthValue::TRUE
} else {
other.clone()
}
}
}
impl BeliefTopos {
/// Bayesian update under new evidence
pub fn update(&mut self, evidence: Evidence) -> BeliefUpdate {
let prior = self.credence(evidence.entity);
// Compute likelihood based on coherence
let likelihood = self.compute_likelihood(&evidence);
// Bayesian update (simplified)
let posterior = (prior * likelihood) /
(prior * likelihood + (1.0 - prior) * (1.0 - likelihood));
let update = BeliefUpdate {
entity: evidence.entity,
prior,
posterior,
evidence: evidence.clone(),
};
self.credences.insert(evidence.entity, posterior);
self.history.push(update.clone());
update
}
/// Compute likelihood based on coherence with existing beliefs
fn compute_likelihood(&self, evidence: &Evidence) -> f32 {
// High coherence with existing beliefs -> high likelihood
let subgraph = self.graph.neighborhood(evidence.entity, 2);
let energy = subgraph.compute_energy();
// Convert energy to probability (lower energy = higher likelihood)
(-energy / self.temperature()).exp()
}
/// Check if proposition holds in current belief state
pub fn holds(&self, prop: &Proposition) -> TruthValue {
match prop {
Proposition::Atom(entity) => {
TruthValue(self.credence(*entity))
}
Proposition::And(p, q) => {
self.holds(p).and(&self.holds(q))
}
Proposition::Or(p, q) => {
self.holds(p).or(&self.holds(q))
}
Proposition::Implies(p, q) => {
self.holds(p).implies(&self.holds(q))
}
Proposition::Not(p) => {
self.holds(p).not()
}
Proposition::Coherent(region) => {
// Region is coherent if energy below threshold
let energy = self.graph.region_energy(region);
if energy < COHERENCE_THRESHOLD {
TruthValue::TRUE
} else if energy > INCOHERENCE_THRESHOLD {
TruthValue::FALSE
} else {
TruthValue(1.0 - energy / INCOHERENCE_THRESHOLD)
}
}
}
}
}
```
### Higher Category Structure
For advanced applications, we model **2-morphisms** (relationships between relationships):
```rust
/// A 2-category with objects, 1-morphisms, and 2-morphisms
pub trait TwoCategory {
type Object;
type Morphism1;
type Morphism2;
fn id_1(obj: &Self::Object) -> Self::Morphism1;
fn id_2(mor: &Self::Morphism1) -> Self::Morphism2;
fn compose_1(f: &Self::Morphism1, g: &Self::Morphism1) -> Self::Morphism1;
fn compose_2_vertical(
alpha: &Self::Morphism2,
beta: &Self::Morphism2
) -> Self::Morphism2;
fn compose_2_horizontal(
alpha: &Self::Morphism2,
beta: &Self::Morphism2
) -> Self::Morphism2;
}
/// Coherence laws form 2-morphisms in the belief 2-category
pub struct CoherenceLaw {
/// Source belief update sequence
source: Vec<BeliefUpdate>,
/// Target belief update sequence
target: Vec<BeliefUpdate>,
/// Witness that they're equivalent
witness: CoherenceWitness,
}
impl CoherenceLaw {
/// Associativity: (f . g) . h = f . (g . h)
pub fn associativity(f: BeliefUpdate, g: BeliefUpdate, h: BeliefUpdate) -> Self {
CoherenceLaw {
source: vec![f.clone(), g.clone(), h.clone()], // Left-associated
target: vec![f, g, h], // Right-associated
witness: CoherenceWitness::Associativity,
}
}
/// Unit law: id . f = f = f . id
pub fn left_unit(f: BeliefUpdate) -> Self {
CoherenceLaw {
source: vec![BeliefUpdate::identity(), f.clone()],
target: vec![f],
witness: CoherenceWitness::LeftUnit,
}
}
}
```
---
## Consequences
### Positive
1. **Structure Preservation**: Functors ensure retrieval respects knowledge structure
2. **Intuitionistic Reasoning**: Handles partial/uncertain knowledge properly
3. **Compositionality**: Complex operations built from simple primitives
4. **Higher Coherence**: 2-morphisms capture meta-level consistency
5. **Belief Dynamics**: Topos semantics enable principled belief update
### Negative
1. **Abstraction Overhead**: Category theory requires learning curve
2. **Performance Cost**: Functor laws verification has runtime cost
3. **Complexity**: 2-categorical structures can be overwhelming
4. **Implementation Fidelity**: Ensuring Rust code matches category theory is subtle
### Mitigations
1. **Gradual Adoption**: Use basic functors first, add higher structures as needed
2. **Type-Level Enforcement**: Use Rust's type system to enforce laws statically
3. **Documentation**: Extensive examples linking code to mathematical concepts
4. **Testing**: Property-based tests for categorical laws
---
## Mathematical Properties
### Theorem: Yoneda Lemma
For a functor F: C -> Set and object A in C:
```
Nat(Hom(A, -), F) ≅ F(A)
```
Natural transformations from a representable functor to F are determined by elements of F(A).
**Application**: This allows us to reconstruct knowledge graph structure from query patterns.
### Theorem: Subobject Classifier in Presheaves
In the topos of presheaves Set^{C^op}:
```
Omega(c) = {sieves on c}
```
The truth values for an object c are sieves (downward-closed collections of morphisms into c).
**Application**: Partial truth values are determined by how much of the knowledge graph supports a proposition.
### Theorem: Adjoint Functors Preserve Limits
If F ⊣ G (F left adjoint to G), then:
- F preserves colimits
- G preserves limits
**Application**: Retrieval (right adjoint) preserves finite products of query results.
---
## Integration with Sheaf Cohomology
The belief topos connects to sheaf cohomology:
```rust
/// Coherence as a global section
pub fn coherent_section(&self) -> Option<GlobalSection> {
// Check if current beliefs form a global section
let cohomology_dim = self.graph.cohomology_dimension();
if cohomology_dim == 0 {
Some(self.construct_global_section())
} else {
None // Obstruction exists
}
}
/// Credence from cohomology class
pub fn credence_from_cohomology(&self, node: NodeId) -> f32 {
// Higher cohomology -> lower credence
let local_cohomology = self.graph.local_cohomology(node);
1.0 / (1.0 + local_cohomology as f32)
}
```
---
## Related Decisions
- [ADR-001: Sheaf Cohomology](ADR-001-sheaf-cohomology.md) - Mathematical foundation
- [ADR-003: Homotopy Type Theory](ADR-003-homotopy-type-theory.md) - Higher categories and paths
- [ADR-005: Causal Abstraction](ADR-005-causal-abstraction.md) - Causal categories
---
## References
1. Mac Lane, S. (1978). "Categories for the Working Mathematician." Springer.
2. Lawvere, F.W. & Schanuel, S. (2009). "Conceptual Mathematics." Cambridge University Press.
3. Goldblatt, R. (1984). "Topoi: The Categorical Analysis of Logic." North-Holland.
4. Awodey, S. (2010). "Category Theory." Oxford University Press.
5. Johnstone, P.T. (2002). "Sketches of an Elephant: A Topos Theory Compendium." Oxford University Press.
6. Spivak, D.I. (2014). "Category Theory for the Sciences." MIT Press.
---
## Appendix: Category Theory Primer
### Objects and Morphisms
A category C consists of:
- A collection ob(C) of **objects**
- For each pair of objects A, B, a collection Hom(A, B) of **morphisms**
- For each object A, an **identity morphism** id_A: A -> A
- **Composition**: For f: A -> B and g: B -> C, g . f: A -> C
Subject to:
- Associativity: (h . g) . f = h . (g . f)
- Identity: f . id_A = f = id_B . f
### Functors
A functor F: C -> D consists of:
- An object map: A |-> F(A)
- A morphism map: f |-> F(f)
Subject to:
- F(id_A) = id_{F(A)}
- F(g . f) = F(g) . F(f)
### Natural Transformations
A natural transformation eta: F => G between functors F, G: C -> D consists of:
- For each object A in C, a morphism eta_A: F(A) -> G(A)
Subject to naturality: For f: A -> B,
- G(f) . eta_A = eta_B . F(f)

View File

@@ -0,0 +1,539 @@
# ADR-003: Homotopy Type Theory for Verified Reasoning
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
AI systems need to reason about equivalences between different representations of knowledge. Traditional approaches struggle with:
1. **Representation Independence**: Different encodings of the same knowledge should be interchangeable
2. **Proof Transfer**: A proof about one structure should apply to equivalent structures
3. **Higher Equalities**: Not just equality of objects, but equality of proofs of equality
4. **Constructive Reasoning**: Proofs should be computationally meaningful
Homotopy Type Theory (HoTT) provides a foundation where:
- Types are spaces
- Terms are points
- Equalities are paths
- Higher equalities are higher-dimensional paths (homotopies)
This geometric intuition enables **proof transport**: any property of a structure transfers automatically to equivalent structures.
### Why HoTT?
The **Univalence Axiom** in HoTT states:
```
(A ≃ B) ≃ (A = B)
```
Equivalence of types is equivalent to identity of types. This means:
- If two knowledge representations are equivalent, they are the same for all purposes
- Proofs about one representation apply to the other
- Refactoring doesn't break correctness guarantees
---
## Decision
We implement a **HoTT-inspired reasoning layer** for verified coherence operations with proof transport.
### Mathematical Foundation
#### Definition: Path (Identity Type)
For a type A and terms a, b : A, the **path type** a =_A b represents proofs that a and b are equal.
A term p : a =_A b is a **path** from a to b.
#### Definition: Path Induction (J Eliminator)
Given:
- Type family C : (x : A) -> (y : A) -> (x = y) -> Type
- Base case c : (x : A) -> C(x, x, refl_x)
We can construct:
- J(C, c) : (x : A) -> (y : A) -> (p : x = y) -> C(x, y, p)
This means: to prove something about all paths, it suffices to prove it for reflexivity.
#### Definition: Univalence
For types A and B, there is an equivalence:
```
ua : (A ≃ B) -> (A = B)
```
with inverse:
```
idtoeqv : (A = B) -> (A ≃ B)
```
such that ua . idtoeqv = id and idtoeqv . ua = id.
#### Definition: Transport
Given a path p : a = b and a type family P : A -> Type, we get:
```
transport_P(p) : P(a) -> P(b)
```
This "transports" data along the path.
### Implementation Architecture
#### Path Types
```rust
/// A path (proof of equality) between terms
pub struct Path<A> {
source: A,
target: A,
/// The actual proof witness (for computational paths)
witness: PathWitness,
}
/// Witness types for different kinds of paths
pub enum PathWitness {
/// Reflexivity: a = a
Refl,
/// Path from equivalence via univalence
Univalence(EquivalenceWitness),
/// Composed path: transitivity
Compose(Box<PathWitness>, Box<PathWitness>),
/// Inverted path: symmetry
Inverse(Box<PathWitness>),
/// Applied function: ap
Ap {
function: String,
base_path: Box<PathWitness>,
},
/// Transport witness
Transport {
family: String,
base_path: Box<PathWitness>,
},
}
impl<A: Clone + PartialEq> Path<A> {
/// Reflexivity path
pub fn refl(x: A) -> Self {
Path {
source: x.clone(),
target: x,
witness: PathWitness::Refl,
}
}
/// Symmetry: p : a = b implies p^-1 : b = a
pub fn inverse(&self) -> Path<A> {
Path {
source: self.target.clone(),
target: self.source.clone(),
witness: PathWitness::Inverse(Box::new(self.witness.clone())),
}
}
/// Transitivity: p : a = b and q : b = c implies q . p : a = c
pub fn compose(&self, other: &Path<A>) -> Option<Path<A>> {
if self.target != other.source {
return None;
}
Some(Path {
source: self.source.clone(),
target: other.target.clone(),
witness: PathWitness::Compose(
Box::new(self.witness.clone()),
Box::new(other.witness.clone()),
),
})
}
}
```
#### Type Families and Transport
```rust
/// A type family (dependent type)
pub trait TypeFamily<A> {
type Fiber;
fn fiber(&self, x: &A) -> Self::Fiber;
}
/// Transport along a path
pub struct Transport<P: TypeFamily<A>, A> {
family: P,
_marker: PhantomData<A>,
}
impl<P: TypeFamily<A>, A: Clone> Transport<P, A> {
/// Transport data along a path
pub fn transport(
&self,
path: &Path<A>,
data: P::Fiber,
) -> P::Fiber
where
P::Fiber: Clone,
{
match &path.witness {
PathWitness::Refl => data,
PathWitness::Univalence(equiv) => {
// Apply the equivalence map
self.apply_equivalence(equiv, data)
}
PathWitness::Compose(p, q) => {
// Transport along p, then along q
let mid = self.transport_along_witness(p, data);
self.transport_along_witness(q, mid)
}
PathWitness::Inverse(p) => {
// Use inverse of equivalence
self.transport_inverse(p, data)
}
_ => data, // Conservative: identity if unknown
}
}
}
```
#### Equivalences
```rust
/// An equivalence between types A and B
pub struct Equivalence<A, B> {
/// Forward map
pub to: Box<dyn Fn(A) -> B>,
/// Backward map
pub from: Box<dyn Fn(B) -> A>,
/// Witness that from . to ~ id_A
pub left_inverse: Homotopy<A>,
/// Witness that to . from ~ id_B
pub right_inverse: Homotopy<B>,
}
/// A homotopy between functions
pub struct Homotopy<A> {
/// For each x, a path from f(x) to g(x)
component: Box<dyn Fn(A) -> PathWitness>,
}
impl<A: Clone, B: Clone> Equivalence<A, B> {
/// Convert to path via univalence
pub fn to_path(&self) -> Path<TypeId> {
Path {
source: TypeId::of::<A>(),
target: TypeId::of::<B>(),
witness: PathWitness::Univalence(
EquivalenceWitness::from_equivalence(self)
),
}
}
}
/// Univalence axiom: (A ≃ B) ≃ (A = B)
pub fn univalence<A: 'static, B: 'static>(
equiv: Equivalence<A, B>
) -> Path<TypeId> {
equiv.to_path()
}
/// Inverse of univalence: (A = B) -> (A ≃ B)
pub fn idtoeqv<A: Clone, B: Clone>(
path: Path<TypeId>
) -> Option<Equivalence<A, B>> {
match path.witness {
PathWitness::Refl => {
Some(Equivalence::identity())
}
PathWitness::Univalence(equiv) => {
equiv.to_equivalence()
}
_ => None,
}
}
```
#### Higher Paths
```rust
/// A 2-path (homotopy between paths)
pub struct Path2<A> {
source: Path<A>,
target: Path<A>,
witness: Path2Witness,
}
/// A 3-path (homotopy between homotopies)
pub struct Path3<A> {
source: Path2<A>,
target: Path2<A>,
witness: Path3Witness,
}
impl<A: Clone + PartialEq> Path2<A> {
/// Identity 2-path
pub fn refl(p: Path<A>) -> Self {
Path2 {
source: p.clone(),
target: p,
witness: Path2Witness::Refl,
}
}
/// Associativity coherence: (p . q) . r = p . (q . r)
pub fn associativity(
p: &Path<A>,
q: &Path<A>,
r: &Path<A>,
) -> Option<Path2<A>> {
let left = p.compose(q)?.compose(r)?; // (p . q) . r
let right = q.compose(r).and_then(|qr| p.compose(&qr))?; // p . (q . r)
Some(Path2 {
source: left,
target: right,
witness: Path2Witness::Associativity,
})
}
/// Unit coherence: refl . p = p = p . refl
pub fn left_unit(p: &Path<A>) -> Path2<A> {
let refl_composed = Path::refl(p.source.clone()).compose(p).unwrap();
Path2 {
source: refl_composed,
target: p.clone(),
witness: Path2Witness::LeftUnit,
}
}
}
```
### Application to Coherence
```rust
/// Coherence property as a type family
pub struct CoherenceFamily {
threshold: f32,
}
impl TypeFamily<SheafGraph> for CoherenceFamily {
type Fiber = CoherenceProof;
fn fiber(&self, graph: &SheafGraph) -> Self::Fiber {
let energy = graph.coherence_energy();
if energy < self.threshold {
CoherenceProof::Coherent(energy)
} else {
CoherenceProof::Incoherent(energy)
}
}
}
/// Proof that coherence transports along equivalences
pub fn coherence_transport<A, B>(
equiv: &Equivalence<A, B>,
coherence_a: CoherenceProof,
) -> CoherenceProof
where
A: IntoSheafGraph,
B: IntoSheafGraph,
{
// Use univalence to get path
let path = equiv.to_path();
// Transport coherence along path
let transport = Transport::new(CoherenceFamily::default());
transport.transport(&path, coherence_a)
}
/// Verified refactoring: if A ≃ B and A is coherent, B is coherent
pub fn verified_refactor<A, B>(
source: A,
target: B,
equiv: Equivalence<A, B>,
proof: CoherenceProof,
) -> Result<(B, CoherenceProof), RefactorError>
where
A: IntoSheafGraph,
B: IntoSheafGraph,
{
// Verify equivalence
if !equiv.verify() {
return Err(RefactorError::InvalidEquivalence);
}
// Transport proof
let transported_proof = coherence_transport(&equiv, proof);
Ok((target, transported_proof))
}
```
### Higher Inductive Types
```rust
/// A circle: base point with a loop
pub struct Circle {
// Type has one point constructor and one path constructor
}
impl Circle {
pub const BASE: Circle = Circle {};
/// The loop: base = base
pub fn loop_path() -> Path<Circle> {
Path {
source: Circle::BASE,
target: Circle::BASE,
witness: PathWitness::Loop,
}
}
}
/// Recursion principle for circle
pub fn circle_rec<X>(
base_case: X,
loop_case: Path<X>,
) -> impl Fn(Circle) -> X {
move |_c: Circle| base_case.clone()
}
/// Induction principle for circle
pub fn circle_ind<P: TypeFamily<Circle>>(
base_case: P::Fiber,
loop_case: Path<P::Fiber>,
) -> impl Fn(Circle) -> P::Fiber
where
P::Fiber: Clone,
{
move |_c: Circle| base_case.clone()
}
```
---
## Consequences
### Positive
1. **Proof Transport**: Coherence properties transfer across equivalent representations
2. **Representation Independence**: Different encodings are provably equivalent
3. **Higher Coherence**: 2-paths and 3-paths capture meta-level consistency
4. **Constructive**: All proofs are computationally meaningful
5. **Verified Refactoring**: Transform code while preserving correctness
### Negative
1. **Complexity**: HoTT concepts require significant learning investment
2. **Performance**: Path manipulation has runtime overhead
3. **Incompleteness**: Not all equivalences are decidable
4. **Engineering Challenge**: Implementing univalence faithfully is hard
### Mitigations
1. **Progressive Disclosure**: Use simple paths first, add complexity as needed
2. **Lazy Evaluation**: Compute path witnesses on demand
3. **Conservative Transport**: Fall back to identity for unknown paths
4. **Extensive Testing**: Property tests verify transport correctness
---
## Mathematical Properties
### Theorem: Transport is Functorial
For paths p : a = b and q : b = c:
```
transport_P(q . p) = transport_P(q) . transport_P(p)
```
### Theorem: Ap Commutes with Composition
For f : A -> B and paths p : a = a', q : a' = a'':
```
ap_f(q . p) = ap_f(q) . ap_f(p)
```
### Theorem: Function Extensionality
For functions f, g : A -> B:
```
(f = g) ≃ ((x : A) -> f(x) = g(x))
```
Two functions are equal iff they're pointwise equal.
### Theorem: Univalence Implies Function Extensionality
Univalence implies the above, making it a "master" axiom for equality.
---
## Related Decisions
- [ADR-001: Sheaf Cohomology](ADR-001-sheaf-cohomology.md) - Cohomology as path obstructions
- [ADR-002: Category Theory](ADR-002-category-topos.md) - Categories as infinity-groupoids
---
## References
1. Univalent Foundations Program. (2013). "Homotopy Type Theory: Univalent Foundations of Mathematics." Institute for Advanced Study.
2. Voevodsky, V. (2010). "Univalent Foundations." Talk at IAS.
3. Awodey, S., & Warren, M. (2009). "Homotopy Theoretic Models of Identity Types." Mathematical Proceedings of the Cambridge Philosophical Society.
4. Shulman, M. (2015). "Brouwer's Fixed-Point Theorem in Real-Cohesive Homotopy Type Theory."
5. Rijke, E. (2022). "Introduction to Homotopy Type Theory." arXiv.
---
## Appendix: HoTT Computation Rules
### Beta Rule for Path Induction
```
J(C, c, a, a, refl_a) = c(a)
```
Path induction on reflexivity returns the base case.
### Computation for Transport
```
transport_P(refl_a, x) = x
```
Transporting along reflexivity is identity.
### Computation for Ap
```
ap_f(refl_a) = refl_{f(a)}
```
Applying a function to reflexivity gives reflexivity.
### Univalence Computation
```
transport_{P}(ua(e), x) = e.to(x)
```
Transporting along a univalence path applies the equivalence.

View File

@@ -0,0 +1,320 @@
# ADR-004: Spectral Invariants for Representation Analysis
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
Neural network representations form high-dimensional vector spaces where geometric and spectral properties encode semantic meaning. Understanding these representations requires mathematical tools that can:
1. **Extract invariant features**: Properties preserved under transformations
2. **Detect representation quality**: Distinguish good embeddings from degenerate ones
3. **Track representation evolution**: Monitor how representations change during training
4. **Compare representations**: Measure similarity between different models
Traditional approaches focus on:
- Cosine similarity (ignores global structure)
- t-SNE/UMAP (non-linear, non-invertible projections)
- Probing classifiers (task-specific, not general)
We need invariants that are mathematically well-defined and computationally tractable.
---
## Decision
We implement **spectral invariants** based on eigenvalue analysis of representation matrices, covariance structures, and graph Laplacians.
### Core Spectral Invariants
#### 1. Eigenvalue Spectrum
For a representation matrix X (n samples x d dimensions):
```rust
/// Compute eigenvalue spectrum of covariance matrix
pub struct EigenvalueSpectrum {
/// Eigenvalues in descending order
pub eigenvalues: Vec<f64>,
/// Cumulative explained variance
pub cumulative_variance: Vec<f64>,
/// Effective dimensionality
pub effective_dim: f64,
}
impl EigenvalueSpectrum {
pub fn from_covariance(cov: &DMatrix<f64>) -> Result<Self> {
let eigen = cov.symmetric_eigenvalues();
let mut eigenvalues: Vec<f64> = eigen.iter().cloned().collect();
eigenvalues.sort_by(|a, b| b.partial_cmp(a).unwrap());
let total: f64 = eigenvalues.iter().sum();
let cumulative_variance: Vec<f64> = eigenvalues.iter()
.scan(0.0, |acc, &x| {
*acc += x / total;
Some(*acc)
})
.collect();
// Effective dimensionality via participation ratio
let sum_sq: f64 = eigenvalues.iter().map(|x| x * x).sum();
let effective_dim = (total * total) / sum_sq;
Ok(Self { eigenvalues, cumulative_variance, effective_dim })
}
}
```
#### 2. Spectral Gap
The spectral gap measures separation between clusters:
```rust
/// Spectral gap analysis
pub struct SpectralGap {
/// Gap between first and second eigenvalues
pub primary_gap: f64,
/// Normalized gap (invariant to scale)
pub normalized_gap: f64,
/// Location of largest gap in spectrum
pub largest_gap_index: usize,
}
impl SpectralGap {
pub fn from_eigenvalues(eigenvalues: &[f64]) -> Self {
let gaps: Vec<f64> = eigenvalues.windows(2)
.map(|w| w[0] - w[1])
.collect();
let largest_gap_index = gaps.iter()
.enumerate()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.map(|(i, _)| i)
.unwrap_or(0);
let primary_gap = gaps.first().copied().unwrap_or(0.0);
let normalized_gap = primary_gap / eigenvalues[0].max(1e-10);
Self { primary_gap, normalized_gap, largest_gap_index }
}
}
```
#### 3. Condition Number
Measures numerical stability of representations:
```rust
/// Condition number for representation stability
pub fn condition_number(eigenvalues: &[f64]) -> f64 {
let max_eig = eigenvalues.first().copied().unwrap_or(1.0);
let min_eig = eigenvalues.last().copied().unwrap_or(1e-10).max(1e-10);
max_eig / min_eig
}
```
### Graph Laplacian Spectrum
For representation similarity graphs:
```rust
/// Laplacian spectral analysis
pub struct LaplacianSpectrum {
/// Number of connected components (multiplicity of 0 eigenvalue)
pub num_components: usize,
/// Fiedler value (second smallest eigenvalue)
pub fiedler_value: f64,
/// Cheeger constant bound
pub cheeger_bound: (f64, f64),
}
impl LaplacianSpectrum {
pub fn from_graph(adjacency: &DMatrix<f64>) -> Self {
// Compute degree matrix
let degrees = adjacency.row_sum();
let degree_matrix = DMatrix::from_diagonal(&degrees);
// Laplacian L = D - A
let laplacian = &degree_matrix - adjacency;
// Compute spectrum
let eigen = laplacian.symmetric_eigenvalues();
let mut eigenvalues: Vec<f64> = eigen.iter().cloned().collect();
eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap());
// Count zero eigenvalues (connected components)
let num_components = eigenvalues.iter()
.filter(|&&e| e.abs() < 1e-10)
.count();
let fiedler_value = eigenvalues.get(num_components)
.copied()
.unwrap_or(0.0);
// Cheeger inequality bounds
let cheeger_lower = fiedler_value / 2.0;
let cheeger_upper = (2.0 * fiedler_value).sqrt();
Self {
num_components,
fiedler_value,
cheeger_bound: (cheeger_lower, cheeger_upper),
}
}
}
```
### Invariant Fingerprints
Combine spectral invariants into a fingerprint for comparison:
```rust
/// Spectral fingerprint for representation comparison
#[derive(Debug, Clone)]
pub struct SpectralFingerprint {
/// Top k eigenvalues (normalized)
pub top_eigenvalues: Vec<f64>,
/// Effective dimensionality
pub effective_dim: f64,
/// Condition number (log scale)
pub log_condition: f64,
/// Spectral entropy
pub spectral_entropy: f64,
}
impl SpectralFingerprint {
pub fn new(spectrum: &EigenvalueSpectrum, k: usize) -> Self {
let total: f64 = spectrum.eigenvalues.iter().sum();
let top_eigenvalues: Vec<f64> = spectrum.eigenvalues.iter()
.take(k)
.map(|e| e / total)
.collect();
// Spectral entropy
let probs: Vec<f64> = spectrum.eigenvalues.iter()
.map(|e| e / total)
.filter(|&p| p > 1e-10)
.collect();
let spectral_entropy: f64 = -probs.iter()
.map(|p| p * p.ln())
.sum::<f64>();
Self {
top_eigenvalues,
effective_dim: spectrum.effective_dim,
log_condition: condition_number(&spectrum.eigenvalues).ln(),
spectral_entropy,
}
}
/// Compare two fingerprints
pub fn distance(&self, other: &Self) -> f64 {
let eigenvalue_dist: f64 = self.top_eigenvalues.iter()
.zip(other.top_eigenvalues.iter())
.map(|(a, b)| (a - b).powi(2))
.sum::<f64>()
.sqrt();
let dim_diff = (self.effective_dim - other.effective_dim).abs();
let cond_diff = (self.log_condition - other.log_condition).abs();
let entropy_diff = (self.spectral_entropy - other.spectral_entropy).abs();
// Weighted combination
eigenvalue_dist + 0.1 * dim_diff + 0.05 * cond_diff + 0.1 * entropy_diff
}
}
```
---
## Consequences
### Positive
1. **Mathematically rigorous**: Based on linear algebra with well-understood properties
2. **Computationally efficient**: SVD/eigendecomposition is O(d^3) but highly optimized
3. **Invariant to orthogonal transformations**: Eigenvalues don't change under rotation
4. **Interpretable**: Effective dimensionality, spectral gap have clear meanings
5. **Composable**: Can combine multiple invariants into fingerprints
### Negative
1. **Not invariant to non-orthogonal transforms**: Scaling changes condition number
2. **Requires full spectrum**: Approximations lose information
3. **Sensitive to outliers**: Single extreme point can dominate covariance
4. **Memory intensive**: Storing covariance matrices is O(d^2)
### Mitigations
1. **Normalization**: Pre-normalize representations to unit variance
2. **Lanczos iteration**: Compute only top-k eigenvalues for large d
3. **Robust covariance**: Use median-of-means or trimmed estimators
4. **Streaming updates**: Maintain running covariance estimates
---
## Implementation Notes
### Lanczos Algorithm for Large Matrices
```rust
/// Compute top-k eigenvalues using Lanczos iteration
pub fn lanczos_eigenvalues(
matrix: &DMatrix<f64>,
k: usize,
max_iter: usize,
) -> Vec<f64> {
let n = matrix.nrows();
let k = k.min(n);
// Initialize with random vector
let mut v = DVector::from_fn(n, |_, _| rand::random::<f64>());
v.normalize_mut();
let mut alpha = Vec::with_capacity(max_iter);
let mut beta = Vec::with_capacity(max_iter);
let mut v_prev = DVector::zeros(n);
for i in 0..max_iter {
let w = matrix * &v;
let a = v.dot(&w);
alpha.push(a);
let mut w = w - a * &v - if i > 0 { beta[i-1] * &v_prev } else { DVector::zeros(n) };
let b = w.norm();
if b < 1e-10 { break; }
beta.push(b);
v_prev = v.clone();
v = w / b;
}
// Build tridiagonal matrix and compute eigenvalues
tridiagonal_eigenvalues(&alpha, &beta, k)
}
```
---
## Related Decisions
- [ADR-001: Sheaf Cohomology](ADR-001-sheaf-cohomology.md) - Uses spectral gap for coherence
- [ADR-002: Category Theory](ADR-002-category-topos.md) - Spectral invariants as functors
- [ADR-006: Quantum Topology](ADR-006-quantum-topology.md) - Density matrix eigenvalues
---
## References
1. Belkin, M., & Niyogi, P. (2003). "Laplacian Eigenmaps for Dimensionality Reduction." Neural Computation.
2. Von Luxburg, U. (2007). "A Tutorial on Spectral Clustering." Statistics and Computing.
3. Roy, O., & Vetterli, M. (2007). "The Effective Rank: A Measure of Effective Dimensionality." EUSIPCO.
4. Kornblith, S., et al. (2019). "Similarity of Neural Network Representations Revisited." ICML.

View File

@@ -0,0 +1,343 @@
# ADR-005: Causal Abstraction for Mechanistic Interpretability
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
Understanding *why* neural networks produce their outputs requires more than correlation analysis. We need:
1. **Causal mechanisms**: Which components actually cause specific behaviors
2. **Interventional reasoning**: What happens when we modify internal states
3. **Abstraction levels**: How low-level computations relate to high-level concepts
4. **Alignment verification**: Whether learned mechanisms match intended behavior
Traditional interpretability approaches provide:
- Attention visualization (correlational, not causal)
- Gradient-based attribution (local approximations)
- Probing classifiers (detect presence, not causation)
These fail to distinguish "correlates with output" from "causes output."
### Why Causal Abstraction?
Causal abstraction theory (Geiger et al., 2021) provides a rigorous framework for:
1. **Defining interpretations**: Mapping neural computations to high-level concepts
2. **Testing interpretations**: Using interventions to verify causal structure
3. **Measuring alignment**: Quantifying how well neural mechanisms match intended algorithms
4. **Localizing circuits**: Finding minimal subnetworks that implement behaviors
---
## Decision
We implement **causal abstraction** as the foundation for mechanistic interpretability in Prime-Radiant.
### Core Concepts
#### 1. Causal Models
```rust
/// A causal model with variables and structural equations
pub struct CausalModel {
/// Variable nodes
variables: HashMap<VariableId, Variable>,
/// Directed edges (cause -> effect)
edges: HashSet<(VariableId, VariableId)>,
/// Structural equations: V = f(Pa(V), noise)
equations: HashMap<VariableId, StructuralEquation>,
/// Exogenous noise distributions
noise: HashMap<VariableId, NoiseDistribution>,
}
/// A variable in the causal model
pub struct Variable {
pub id: VariableId,
pub name: String,
pub domain: VariableDomain,
pub level: AbstractionLevel,
}
/// Structural equation defining variable's value
pub enum StructuralEquation {
/// f(inputs) -> output
Function(Box<dyn Fn(&[Value]) -> Value>),
/// Neural network component
Neural(NeuralComponent),
/// Identity (exogenous variable)
Exogenous,
}
```
#### 2. Interventions
```rust
/// An intervention on a causal model
pub enum Intervention {
/// Set variable to constant value: do(X = x)
Hard(VariableId, Value),
/// Modify value by function: do(X = f(X))
Soft(VariableId, Box<dyn Fn(Value) -> Value>),
/// Interchange values between runs
Interchange(VariableId, SourceId),
/// Activation patching
Patch(VariableId, Vec<f32>),
}
impl CausalModel {
/// Apply intervention and compute effects
pub fn intervene(&self, intervention: &Intervention) -> CausalModel {
let mut modified = self.clone();
match intervention {
Intervention::Hard(var, value) => {
// Remove all incoming edges
modified.edges.retain(|(_, target)| target != var);
// Set constant equation
modified.equations.insert(*var, StructuralEquation::constant(*value));
}
Intervention::Soft(var, f) => {
// Compose with existing equation
let old_eq = modified.equations.get(var).unwrap();
modified.equations.insert(*var, old_eq.compose(f));
}
// ...
}
modified
}
}
```
#### 3. Causal Abstraction
```rust
/// A causal abstraction between two models
pub struct CausalAbstraction {
/// Low-level (concrete) model
low: CausalModel,
/// High-level (abstract) model
high: CausalModel,
/// Variable mapping: low -> high
tau: HashMap<VariableId, VariableId>,
/// Intervention mapping
intervention_map: Box<dyn Fn(&Intervention) -> Intervention>,
}
impl CausalAbstraction {
/// Check if abstraction is valid (interventional consistency)
pub fn is_valid(&self, test_interventions: &[Intervention]) -> bool {
for intervention in test_interventions {
// Map intervention to high level
let high_intervention = (self.intervention_map)(intervention);
// Intervene on both models
let low_result = self.low.intervene(intervention);
let high_result = self.high.intervene(&high_intervention);
// Check outputs match (up to tau)
let low_output = low_result.output();
let high_output = high_result.output();
if !self.outputs_match(&low_output, &high_output) {
return false;
}
}
true
}
/// Compute interchange intervention accuracy
pub fn iia(&self,
base_inputs: &[Input],
source_inputs: &[Input],
target_var: VariableId) -> f64 {
let mut correct = 0;
let total = base_inputs.len() * source_inputs.len();
for base in base_inputs {
for source in source_inputs {
// Run high-level model with intervention
let high_base = self.high.run(base);
let high_source = self.high.run(source);
let high_interchanged = self.high.intervene(
&Intervention::Interchange(target_var, high_source.id)
).run(base);
// Run low-level model with corresponding intervention
let low_base = self.low.run(base);
let low_source = self.low.run(source);
let low_intervention = (self.intervention_map)(
&Intervention::Interchange(self.tau[&target_var], low_source.id)
);
let low_interchanged = self.low.intervene(&low_intervention).run(base);
// Check if behaviors match
if self.outputs_match(&low_interchanged, &high_interchanged) {
correct += 1;
}
}
}
correct as f64 / total as f64
}
}
```
### Activation Patching
```rust
/// Activation patching for neural network interpretability
pub struct ActivationPatcher {
/// Target layer/component
target: NeuralComponent,
/// Patch source
source: PatchSource,
}
pub enum PatchSource {
/// From another input's activation
OtherInput(InputId),
/// Fixed vector
Fixed(Vec<f32>),
/// Noise ablation
Noise(NoiseDistribution),
/// Mean ablation
Mean,
/// Zero ablation
Zero,
}
impl ActivationPatcher {
/// Measure causal effect of patching
pub fn causal_effect(
&self,
model: &NeuralNetwork,
base_input: &Input,
metric: &Metric,
) -> f64 {
// Run without patching
let base_output = model.forward(base_input);
let base_metric = metric.compute(&base_output);
// Run with patching
let patched_output = model.forward_with_patch(base_input, self);
let patched_metric = metric.compute(&patched_output);
// Causal effect is the difference
patched_metric - base_metric
}
}
```
### Circuit Discovery
```rust
/// Discover minimal circuits implementing a behavior
pub struct CircuitDiscovery {
/// Target behavior to explain
behavior: Behavior,
/// Candidate components
components: Vec<NeuralComponent>,
/// Discovered circuits
circuits: Vec<Circuit>,
}
pub struct Circuit {
/// Components in the circuit
components: Vec<NeuralComponent>,
/// Edges (data flow)
edges: Vec<(NeuralComponent, NeuralComponent)>,
/// Faithfulness score (how well circuit explains behavior)
faithfulness: f64,
/// Completeness score (how much of behavior is captured)
completeness: f64,
}
impl CircuitDiscovery {
/// Use activation patching to find important components
pub fn find_circuit(&mut self, model: &NeuralNetwork, inputs: &[Input]) -> Circuit {
let mut important = Vec::new();
// Test each component
for component in &self.components {
let patcher = ActivationPatcher {
target: component.clone(),
source: PatchSource::Zero,
};
let avg_effect: f64 = inputs.iter()
.map(|input| patcher.causal_effect(model, input, &self.behavior.metric))
.sum::<f64>() / inputs.len() as f64;
if avg_effect.abs() > IMPORTANCE_THRESHOLD {
important.push((component.clone(), avg_effect));
}
}
// Build circuit from important components
self.build_circuit(important)
}
}
```
---
## Consequences
### Positive
1. **Rigorous causality**: Distinguishes correlation from causation
2. **Multi-level analysis**: Connects low-level activations to high-level concepts
3. **Testable interpretations**: Interventions provide empirical verification
4. **Circuit localization**: Identifies minimal subnetworks for behaviors
5. **Alignment checking**: Verifies mechanisms match specifications
### Negative
1. **Combinatorial explosion**: Testing all interventions is exponential
2. **Approximation required**: Full causal analysis is computationally intractable
3. **Abstraction design**: Choosing the right high-level model requires insight
4. **Noise sensitivity**: Small variations can affect intervention outcomes
### Mitigations
1. **Importance sampling**: Focus on high-impact interventions
2. **Hierarchical search**: Use coarse-to-fine circuit discovery
3. **Learned abstractions**: Train models to find good variable mappings
4. **Robust statistics**: Use multiple samples and statistical tests
---
## Integration with Prime-Radiant
### Connection to Sheaf Cohomology
Causal structure forms a sheaf:
- Open sets: Subnetworks
- Sections: Causal mechanisms
- Restriction maps: Marginalization
- Cohomology: Obstruction to global causal explanation
### Connection to Category Theory
Causal abstraction is a functor:
- Objects: Causal models
- Morphisms: Interventional maps
- Composition: Hierarchical abstraction
---
## References
1. Geiger, A., et al. (2021). "Causal Abstractions of Neural Networks." NeurIPS.
2. Pearl, J. (2009). "Causality: Models, Reasoning, and Inference." Cambridge.
3. Conmy, A., et al. (2023). "Towards Automated Circuit Discovery." NeurIPS.
4. Wang, K., et al. (2022). "Interpretability in the Wild." ICLR.
5. Goldowsky-Dill, N., et al. (2023). "Localizing Model Behavior with Path Patching." arXiv.

View File

@@ -0,0 +1,451 @@
# ADR-006: Quantum Topology for Representation Analysis
**Status**: Accepted
**Date**: 2024-12-15
**Authors**: RuVector Team
**Supersedes**: None
---
## Context
High-dimensional neural network representations exhibit complex geometric and topological structure that classical methods struggle to capture. We need tools that can:
1. **Handle superpositions**: Representations often encode multiple concepts simultaneously
2. **Measure entanglement**: Detect non-local correlations between features
3. **Track topological invariants**: Identify persistent structural properties
4. **Model uncertainty**: Represent distributional properties of activations
Quantum-inspired methods offer advantages because:
- Superposition naturally models polysemy and context-dependence
- Entanglement captures feature interactions beyond correlation
- Density matrices provide natural uncertainty representation
- Topological quantum invariants are robust to noise
---
## Decision
We implement **quantum topology** methods for advanced representation analysis, including density matrix representations, entanglement measures, and topological invariants.
### Core Structures
#### 1. Quantum State Representation
```rust
use num_complex::Complex64;
/// A quantum state representing neural activations
pub struct QuantumState {
/// Amplitudes in computational basis
amplitudes: Vec<Complex64>,
/// Number of qubits (log2 of dimension)
num_qubits: usize,
}
impl QuantumState {
/// Create from real activation vector (amplitude encoding)
pub fn from_activations(activations: &[f64]) -> Self {
let n = activations.len();
let num_qubits = (n as f64).log2().ceil() as usize;
let dim = 1 << num_qubits;
// Normalize
let norm: f64 = activations.iter().map(|x| x * x).sum::<f64>().sqrt();
let mut amplitudes = vec![Complex64::new(0.0, 0.0); dim];
for (i, &a) in activations.iter().enumerate() {
amplitudes[i] = Complex64::new(a / norm, 0.0);
}
Self { amplitudes, num_qubits }
}
/// Inner product (fidelity for pure states)
pub fn fidelity(&self, other: &Self) -> f64 {
let inner: Complex64 = self.amplitudes.iter()
.zip(other.amplitudes.iter())
.map(|(a, b)| a.conj() * b)
.sum();
inner.norm_sqr()
}
/// Convert to density matrix
pub fn to_density_matrix(&self) -> DensityMatrix {
let dim = self.amplitudes.len();
let mut rho = vec![vec![Complex64::new(0.0, 0.0); dim]; dim];
for i in 0..dim {
for j in 0..dim {
rho[i][j] = self.amplitudes[i] * self.amplitudes[j].conj();
}
}
DensityMatrix { matrix: rho, dim }
}
}
```
#### 2. Density Matrix
```rust
/// Density matrix for mixed state representation
pub struct DensityMatrix {
/// The density matrix elements
matrix: Vec<Vec<Complex64>>,
/// Dimension
dim: usize,
}
impl DensityMatrix {
/// Create maximally mixed state
pub fn maximally_mixed(dim: usize) -> Self {
let mut matrix = vec![vec![Complex64::new(0.0, 0.0); dim]; dim];
let val = Complex64::new(1.0 / dim as f64, 0.0);
for i in 0..dim {
matrix[i][i] = val;
}
Self { matrix, dim }
}
/// From ensemble of pure states
pub fn from_ensemble(states: &[(f64, QuantumState)]) -> Self {
let dim = states[0].1.amplitudes.len();
let mut matrix = vec![vec![Complex64::new(0.0, 0.0); dim]; dim];
for (prob, state) in states {
let rho = state.to_density_matrix();
for i in 0..dim {
for j in 0..dim {
matrix[i][j] += Complex64::new(*prob, 0.0) * rho.matrix[i][j];
}
}
}
Self { matrix, dim }
}
/// Von Neumann entropy: S(rho) = -Tr(rho log rho)
pub fn entropy(&self) -> f64 {
let eigenvalues = self.eigenvalues();
-eigenvalues.iter()
.filter(|&e| *e > 1e-10)
.map(|e| e * e.ln())
.sum::<f64>()
}
/// Purity: Tr(rho^2)
pub fn purity(&self) -> f64 {
let mut trace = Complex64::new(0.0, 0.0);
for i in 0..self.dim {
for k in 0..self.dim {
trace += self.matrix[i][k] * self.matrix[k][i];
}
}
trace.re
}
/// Eigenvalues of density matrix
pub fn eigenvalues(&self) -> Vec<f64> {
// Convert to nalgebra matrix and compute eigenvalues
let mut m = DMatrix::zeros(self.dim, self.dim);
for i in 0..self.dim {
for j in 0..self.dim {
m[(i, j)] = self.matrix[i][j].re; // Hermitian, so real eigenvalues
}
}
let eigen = m.symmetric_eigenvalues();
eigen.iter().cloned().collect()
}
}
```
#### 3. Entanglement Measures
```rust
/// Entanglement analysis for bipartite systems
pub struct EntanglementAnalysis {
/// Subsystem A
subsystem_a: Vec<usize>,
/// Subsystem B
subsystem_b: Vec<usize>,
}
impl EntanglementAnalysis {
/// Compute partial trace over subsystem B
pub fn partial_trace_b(&self, rho: &DensityMatrix) -> DensityMatrix {
let dim_a = 1 << self.subsystem_a.len();
let dim_b = 1 << self.subsystem_b.len();
let mut rho_a = vec![vec![Complex64::new(0.0, 0.0); dim_a]; dim_a];
for i in 0..dim_a {
for j in 0..dim_a {
for k in 0..dim_b {
let row = i * dim_b + k;
let col = j * dim_b + k;
rho_a[i][j] += rho.matrix[row][col];
}
}
}
DensityMatrix { matrix: rho_a, dim: dim_a }
}
/// Entanglement entropy: S(rho_A)
pub fn entanglement_entropy(&self, rho: &DensityMatrix) -> f64 {
let rho_a = self.partial_trace_b(rho);
rho_a.entropy()
}
/// Mutual information: I(A:B) = S(A) + S(B) - S(AB)
pub fn mutual_information(&self, rho: &DensityMatrix) -> f64 {
let rho_a = self.partial_trace_b(rho);
let rho_b = self.partial_trace_a(rho);
rho_a.entropy() + rho_b.entropy() - rho.entropy()
}
/// Concurrence (for 2-qubit systems)
pub fn concurrence(&self, rho: &DensityMatrix) -> f64 {
if rho.dim != 4 {
return 0.0; // Only defined for 2 qubits
}
// Spin-flip matrix
let sigma_y = [[Complex64::new(0.0, 0.0), Complex64::new(0.0, -1.0)],
[Complex64::new(0.0, 1.0), Complex64::new(0.0, 0.0)]];
// rho_tilde = (sigma_y x sigma_y) rho* (sigma_y x sigma_y)
let rho_tilde = self.spin_flip_transform(rho, &sigma_y);
// R = rho * rho_tilde
let r = self.matrix_multiply(rho, &rho_tilde);
// Eigenvalues of R
let eigenvalues = r.eigenvalues();
let mut lambdas: Vec<f64> = eigenvalues.iter()
.map(|e| e.sqrt())
.collect();
lambdas.sort_by(|a, b| b.partial_cmp(a).unwrap());
// C = max(0, lambda_1 - lambda_2 - lambda_3 - lambda_4)
(lambdas[0] - lambdas[1] - lambdas[2] - lambdas[3]).max(0.0)
}
}
```
#### 4. Topological Invariants
```rust
/// Topological invariants for representation spaces
pub struct TopologicalInvariant {
/// Type of invariant
pub kind: InvariantKind,
/// Computed value
pub value: f64,
/// Confidence/precision
pub precision: f64,
}
pub enum InvariantKind {
/// Euler characteristic
EulerCharacteristic,
/// Betti numbers
BettiNumber(usize),
/// Chern number (for complex bundles)
ChernNumber,
/// Berry phase
BerryPhase,
/// Winding number
WindingNumber,
}
impl TopologicalInvariant {
/// Compute Berry phase around a loop in parameter space
pub fn berry_phase(states: &[QuantumState]) -> Self {
let n = states.len();
let mut phase = Complex64::new(1.0, 0.0);
for i in 0..n {
let next = (i + 1) % n;
let overlap: Complex64 = states[i].amplitudes.iter()
.zip(states[next].amplitudes.iter())
.map(|(a, b)| a.conj() * b)
.sum();
phase *= overlap;
}
Self {
kind: InvariantKind::BerryPhase,
value: phase.arg(),
precision: 1e-10,
}
}
/// Compute winding number from phase function
pub fn winding_number(phases: &[f64]) -> Self {
let mut total_winding = 0.0;
for i in 0..phases.len() {
let next = (i + 1) % phases.len();
let mut delta = phases[next] - phases[i];
// Wrap to [-pi, pi]
while delta > std::f64::consts::PI { delta -= 2.0 * std::f64::consts::PI; }
while delta < -std::f64::consts::PI { delta += 2.0 * std::f64::consts::PI; }
total_winding += delta;
}
Self {
kind: InvariantKind::WindingNumber,
value: (total_winding / (2.0 * std::f64::consts::PI)).round(),
precision: 1e-6,
}
}
}
```
### Simplicial Complex for TDA
```rust
/// Simplicial complex for topological data analysis
pub struct SimplicialComplex {
/// Vertices
vertices: Vec<usize>,
/// Simplices by dimension
simplices: Vec<HashSet<Vec<usize>>>,
/// Boundary matrices
boundary_maps: Vec<DMatrix<f64>>,
}
impl SimplicialComplex {
/// Build Vietoris-Rips complex from point cloud
pub fn vietoris_rips(points: &[DVector<f64>], epsilon: f64, max_dim: usize) -> Self {
let n = points.len();
let vertices: Vec<usize> = (0..n).collect();
let mut simplices = vec![HashSet::new(); max_dim + 1];
// 0-simplices (vertices)
for i in 0..n {
simplices[0].insert(vec![i]);
}
// 1-simplices (edges)
for i in 0..n {
for j in (i+1)..n {
if (&points[i] - &points[j]).norm() <= epsilon {
simplices[1].insert(vec![i, j]);
}
}
}
// Higher simplices (clique detection)
for dim in 2..=max_dim {
for simplex in &simplices[dim - 1] {
for v in 0..n {
if simplex.contains(&v) { continue; }
// Check if v is connected to all vertices in simplex
let all_connected = simplex.iter().all(|&u| {
simplices[1].contains(&vec![u.min(v), u.max(v)])
});
if all_connected {
let mut new_simplex = simplex.clone();
new_simplex.push(v);
new_simplex.sort();
simplices[dim].insert(new_simplex);
}
}
}
}
Self { vertices, simplices, boundary_maps: vec![] }
}
/// Compute Betti numbers
pub fn betti_numbers(&self) -> Vec<usize> {
self.compute_boundary_maps();
let mut betti = Vec::new();
for k in 0..self.simplices.len() {
let kernel_dim = if k < self.boundary_maps.len() {
self.kernel_dimension(&self.boundary_maps[k])
} else {
self.simplices[k].len()
};
let image_dim = if k > 0 && k <= self.boundary_maps.len() {
self.image_dimension(&self.boundary_maps[k - 1])
} else {
0
};
betti.push(kernel_dim.saturating_sub(image_dim));
}
betti
}
}
```
---
## Consequences
### Positive
1. **Rich representation**: Density matrices capture distributional information
2. **Entanglement detection**: Identifies non-local feature correlations
3. **Topological robustness**: Invariants stable under continuous deformation
4. **Quantum advantage**: Some computations exponentially faster
5. **Uncertainty modeling**: Natural probabilistic interpretation
### Negative
1. **Computational cost**: Density matrices are O(d^2) in memory
2. **Classical simulation**: Full quantum benefits require quantum hardware
3. **Interpretation complexity**: Quantum concepts less intuitive
4. **Limited applicability**: Not all problems benefit from quantum formalism
### Mitigations
1. **Low-rank approximations**: Use matrix product states for large systems
2. **Tensor networks**: Efficient classical simulation of structured states
3. **Hybrid classical-quantum**: Use quantum-inspired methods on classical hardware
4. **Domain-specific applications**: Focus on problems with natural quantum structure
---
## Integration with Prime-Radiant
### Connection to Sheaf Cohomology
Quantum states form a sheaf:
- Open sets: Subsystems
- Sections: Quantum states
- Restriction: Partial trace
- Cohomology: Entanglement obstructions
### Connection to Category Theory
Quantum mechanics as a dagger category:
- Objects: Hilbert spaces
- Morphisms: Completely positive maps
- Dagger: Adjoint
---
## References
1. Nielsen, M.A., & Chuang, I.L. (2010). "Quantum Computation and Quantum Information." Cambridge.
2. Carlsson, G. (2009). "Topology and Data." Bulletin of the AMS.
3. Coecke, B., & Kissinger, A. (2017). "Picturing Quantum Processes." Cambridge.
4. Schuld, M., & Petruccione, F. (2021). "Machine Learning with Quantum Computers." Springer.
5. Edelsbrunner, H., & Harer, J. (2010). "Computational Topology." AMS.

View File

@@ -0,0 +1,321 @@
# Prime-Radiant Domain Model
## Overview
Prime-Radiant is a mathematical framework for AI interpretability, built on rigorous foundations from algebraic topology, category theory, and quantum mechanics. This document describes the domain model using Domain-Driven Design (DDD) principles.
---
## Bounded Contexts
### 1. Cohomology Context
**Purpose**: Analyze topological structure of representations and detect coherence failures.
#### Aggregates
**Sheaf** (Aggregate Root)
- Contains: Presheaf, Sections, RestrictionMaps
- Invariants: Gluing axioms, locality conditions
- Behavior: Compute cohomology, detect obstructions
**ChainComplex**
- Contains: ChainGroups, BoundaryMaps
- Invariants: d^2 = 0 (boundary of boundary is zero)
- Behavior: Compute homology groups
#### Value Objects
- `Section`: Data over an open set
- `RestrictionMap`: Linear map between stalks
- `BettiNumbers`: Topological invariants
- `PersistenceDiagram`: Multi-scale topology
#### Domain Events
- `CoherenceViolationDetected`: When H^1 is non-trivial
- `TopologyChanged`: When underlying graph structure changes
- `SectionUpdated`: When local data is modified
---
### 2. Category Context
**Purpose**: Model compositional structure and preserve mathematical properties.
#### Aggregates
**Category** (Aggregate Root)
- Contains: Objects, Morphisms
- Invariants: Identity, associativity
- Behavior: Compose morphisms, verify laws
**Topos** (Aggregate Root)
- Contains: Category, SubobjectClassifier, Products, Exponentials
- Invariants: Finite limits, exponentials exist
- Behavior: Internal logic, subobject classification
#### Entities
- `Object`: An element of the category
- `Morphism`: A transformation between objects
- `Functor`: Structure-preserving map between categories
- `NaturalTransformation`: Morphism between functors
#### Value Objects
- `MorphismId`: Unique identifier
- `ObjectId`: Unique identifier
- `CompositionResult`: Result of morphism composition
#### Domain Events
- `MorphismAdded`: New morphism in category
- `FunctorApplied`: Functor maps between categories
- `CoherenceVerified`: Axioms confirmed
---
### 3. HoTT Context (Homotopy Type Theory)
**Purpose**: Provide type-theoretic foundations for proofs and equivalences.
#### Aggregates
**TypeUniverse** (Aggregate Root)
- Contains: Types, Terms, Judgments
- Invariants: Type formation rules
- Behavior: Type checking, univalence
**Path** (Entity)
- Properties: Start, End, Homotopy
- Invariants: Endpoints match types
- Behavior: Concatenation, inversion, transport
#### Value Objects
- `Type`: A type in the universe
- `Term`: An element of a type
- `Equivalence`: Bidirectional map with proofs
- `IdentityType`: The type of paths between terms
#### Domain Services
- `PathInduction`: J-eliminator for paths
- `Transport`: Move values along paths
- `Univalence`: Equivalence = Identity
---
### 4. Spectral Context
**Purpose**: Analyze eigenvalue structure and spectral invariants.
#### Aggregates
**SpectralDecomposition** (Aggregate Root)
- Contains: Eigenvalues, Eigenvectors
- Invariants: Orthogonality, completeness
- Behavior: Compute spectrum, effective dimension
#### Value Objects
- `Eigenspace`: Subspace for eigenvalue
- `SpectralGap`: Distance between eigenvalues
- `SpectralFingerprint`: Comparison signature
- `ConditionNumber`: Numerical stability measure
#### Domain Services
- `LanczosIteration`: Efficient eigenvalue computation
- `CheegerAnalysis`: Spectral gap and graph cuts
---
### 5. Causal Context
**Purpose**: Implement causal abstraction for mechanistic interpretability.
#### Aggregates
**CausalModel** (Aggregate Root)
- Contains: Variables, Edges, StructuralEquations
- Invariants: DAG structure (no cycles)
- Behavior: Intervention, counterfactual reasoning
**CausalAbstraction** (Aggregate Root)
- Contains: LowModel, HighModel, VariableMapping
- Invariants: Interventional consistency
- Behavior: Verify abstraction, compute IIA
#### Entities
- `Variable`: A node in the causal graph
- `Intervention`: An action on a variable
- `Circuit`: Minimal subnetwork for behavior
#### Value Objects
- `StructuralEquation`: Functional relationship
- `InterventionResult`: Outcome of intervention
- `AlignmentScore`: How well mechanisms match
#### Domain Events
- `InterventionApplied`: Variable was modified
- `CircuitDiscovered`: Minimal mechanism found
- `AbstractionViolation`: Models disagree under intervention
---
### 6. Quantum Context
**Purpose**: Apply quantum-inspired methods to representation analysis.
#### Aggregates
**QuantumState** (Aggregate Root)
- Contains: Amplitudes
- Invariants: Normalization
- Behavior: Measure, evolve, entangle
**DensityMatrix** (Aggregate Root)
- Contains: Matrix elements
- Invariants: Positive semi-definite, trace 1
- Behavior: Entropy, purity, partial trace
#### Value Objects
- `Entanglement`: Correlation measure
- `TopologicalInvariant`: Robust property
- `BerryPhase`: Geometric phase
#### Domain Services
- `EntanglementAnalysis`: Compute entanglement measures
- `TDAService`: Topological data analysis
---
## Cross-Cutting Concerns
### Error Handling
All contexts use a unified error type hierarchy:
```rust
pub enum PrimeRadiantError {
Cohomology(CohomologyError),
Category(CategoryError),
HoTT(HoTTError),
Spectral(SpectralError),
Causal(CausalError),
Quantum(QuantumError),
}
```
### Numerical Precision
- Default epsilon: 1e-10
- Configurable per computation
- Automatic condition number checking
### Serialization
All value objects and aggregates implement:
- `serde::Serialize` and `serde::Deserialize`
- Custom formats for mathematical objects
---
## Context Map
```
┌─────────────────────────────────────────────────────────────────┐
│ Prime-Radiant Core │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Cohomology │────▶│ Category │────▶│ HoTT │ │
│ │ Context │ │ Context │ │ Context │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Spectral │────▶│ Causal │────▶│ Quantum │ │
│ │ Context │ │ Context │ │ Context │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Relationships:
─────────────
Cohomology ──[U]──▶ Category : Sheaves are presheaves + gluing (Upstream/Downstream)
Category ──[U]──▶ HoTT : Categories model type theory
Spectral ──[S]──▶ Cohomology: Laplacian eigenvalues for cohomology (Shared Kernel)
Causal ──[C]──▶ Category : Causal abstraction as functors (Conformist)
Quantum ──[P]──▶ Category : Quantum channels as morphisms (Partnership)
```
---
## Ubiquitous Language
| Term | Definition |
|------|------------|
| **Sheaf** | Assignment of data to open sets satisfying gluing axioms |
| **Cohomology** | Measure of obstruction to extending local sections globally |
| **Morphism** | Structure-preserving map between objects |
| **Functor** | Structure-preserving map between categories |
| **Path** | Continuous map from interval, proof of equality in HoTT |
| **Equivalence** | Bidirectional map with inverse proofs |
| **Spectral Gap** | Difference between consecutive eigenvalues |
| **Intervention** | Fixing a variable to a value (do-operator) |
| **Entanglement** | Non-local correlation in quantum states |
| **Betti Number** | Dimension of homology group |
---
## Implementation Guidelines
### Aggregate Design
1. Keep aggregates small and focused
2. Use value objects for immutable data
3. Enforce invariants in aggregate root
4. Emit domain events for state changes
### Repository Pattern
Each aggregate root has a repository:
```rust
pub trait SheafRepository {
fn find_by_id(&self, id: SheafId) -> Option<Sheaf>;
fn save(&mut self, sheaf: Sheaf) -> Result<(), Error>;
fn find_by_topology(&self, graph: &Graph) -> Vec<Sheaf>;
}
```
### Factory Pattern
Complex aggregates use factories:
```rust
pub struct SheafFactory {
pub fn from_neural_network(network: &NeuralNetwork) -> Sheaf;
pub fn from_knowledge_graph(kg: &KnowledgeGraph) -> Sheaf;
}
```
### Domain Services
Cross-aggregate operations use services:
```rust
pub struct CoherenceService {
pub fn check_global_consistency(sheaf: &Sheaf) -> CoherenceReport;
pub fn optimize_sections(sheaf: &mut Sheaf) -> OptimizationResult;
}
```