Skip to main content

Cross-Language Compatibility

One of the core design principles of odin-prompt-toolkit is bit-perfect cross-language compatibility: all three implementations (Rust, Python, TypeScript) produce identical signatures from identical inputs.

Guarantees

Bit-Identical LSH Signatures ✅

Given the same:

  • Normalized embedding vector
  • LSH configuration (families, bits, bands)
  • Family seed

All three implementations produce exactly the same hex signatures.

Example:

Input: [0.5, 0.5, 0.5, 0.5] (4-dimensional unit vector)
Family: 0
Bits: 256

Rust output: 8d000000ac854dae7f3b9c1e...
Python output: 8d000000ac854dae7f3b9c1e...
TypeScript output: 8d000000ac854dae7f3b9c1e...
✅ Identical (64 hex characters = 256 bits)

SHA256 Hash Consistency ✅

All implementations use the same canonical JSON format for embedding hashing:

{"embedding":[0.5,0.5,0.5,0.5]}

Format Rules:

  • No spaces
  • Lowercase key names
  • Array notation with square brackets
  • Consistent float precision (no trailing zeros)

Result: Identical SHA256 hashes across all languages for the same embedding.


Validation Methodology

Test Vector Approach

The Rust implementation serves as the canonical reference. Test vectors are generated from Rust and validated against Python and TypeScript:

┌──────────────┐
│ Rust (Canon) │ Generate test vectors
└──────┬───────┘

├─────────────┬─────────────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌─────────┐ ┌──────────┐
│ Python │ │TypeScript│ │ Rust │
│ Tests │ │ Tests │ │ Tests │
└────┬───┘ └────┬─────┘ └────┬─────┘
│ │ │
└──────┬──────┴───────┬───────┘
│ │
▼ ▼
✅ Compare ✅ Validate

Test Vector Files

Located in spec/test-vectors/, 8 JSON files with 124 test cases:

FileCasesWhat's Tested
splitmix64.json7PRNG determinism
sign_for.json72Hyperplane sign generation
simhash.json5Complete LSH pipeline
hamming.json10Bit distance computation
cosine.json8Similarity estimation
sha256.json7Embedding hash format
signature_format.json7String parsing
cm_lsh.json8CM-LSH dual hashing

Total: 384 passing tests across 3 languages (69 Rust + 183 Python + 132 TypeScript)


Deterministic Algorithm

The LSH algorithm is completely deterministic with no sources of randomness:

1. Deterministic PRNG (SplitMix64)

Hyperplanes are generated using SplitMix64 PRNG with a deterministic seed:

seed = (family_index << 32) | bit_index

Properties:

  • Same seed → same random number sequence
  • Platform-independent (no system RNG)
  • Bit-identical across languages

Validation: All 7 SplitMix64 test cases pass across all languages.

2. Sign-Only Projections

LSH uses only the sign of dot products, not their magnitude:

hash_bit = if dot_product > 0.0 { 1 } else { 0 }

Why this matters:

  • Floating-point precision differences don't affect results
  • Python uses f64, Rust uses f32, TypeScript uses f64
  • As long as the sign agrees, the bit is identical

Edge Case: Zero projections (dot == 0.0) are treated consistently as 0 bit across all implementations.

3. Canonical Normalization

Vector normalization uses consistent L2 norm calculation:

norm = sqrt(sum(x_i^2))
normalized[i] = x[i] / norm

Precision handling:

  • All implementations use standard IEEE 754 floating point
  • Normalization happens before LSH (precision converges)
  • Sign bits are robust to minor float differences

Precision Considerations

Core LSH: Exact Match ✅

Standard LSH signatures are bit-identical across all languages because:

  • Sign-only projections (no magnitude threshold)
  • Deterministic hyperplane generation
  • Consistent normalization

CM-LSH: Minor Variance (~5%) ⚠️

CM-LSH has slightly different results across languages due to:

  1. Confidence thresholding: Uses magnitude comparisons

    confident = (abs(projection) > threshold)
    • Float precision affects threshold calculation
    • Python/TypeScript (f64) vs Rust (f32)
  2. Percentile computation: 45th percentile calculation

    • Small sorting differences due to float precision
    • Typically affects 2-3% of bits

Impact: ~5% bit difference in hashB (confidence bits)

  • hashA (direction bits) remains identical
  • Similarity estimates differ by <0.01 (negligible)

Conclusion: CM-LSH variance is acceptable for production use. The confidence bits are advisory, not critical.


Cross-Language Test Execution

Running Tests

All three languages have test suites that validate against canonical test vectors:

cd packages/rust
cargo test

# Expected output:
# running 69 tests
# test result: ok. 69 passed; 0 failed

Continuous Integration

GitHub Actions runs all test suites on every commit:

# .github/workflows/test.yml
- Rust tests (cargo test)
- Python tests (pytest)
- TypeScript tests (jest)
- Cross-validation check (all vectors match)

Badge Status: Tests


Verification Examples

Verifying Identical Signatures

use odin_prompt_toolkit::{simhash_lsh_multi, normalize_vector, LshConfig};

let vector = vec![0.5, 0.5, 0.5, 0.5];
let normalized = normalize_vector(&vector);

let config = LshConfig::default();
let families = simhash_lsh_multi(&normalized, &config);

println!("Family 0: {}", families[0].signature);
// Output: 8d000000ac854dae7f3b9c1e...

All three outputs are identical!

Verifying SHA256 Hashes

from odin_prompt_toolkit import normalize_vector, compute_embedding_sha256

vector = [0.5, 0.5, 0.5, 0.5]
normalized = normalize_vector(vector)
sha256 = compute_embedding_sha256(normalized)

print(f"SHA256: {sha256}")
# Output: a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a

SHA256 hash is identical across all languages for the same normalized embedding.


Compatibility Matrix

Version Compatibility

VersionRustPythonTypeScriptCross-Compatible
V0 (OpenAI)✅ Bit-identical
V1 (ONNX)✅ Bit-identical
CM-LSH⚠️ ~95% identical

Feature Parity

FeatureRustPythonTypeScript
Core LSH
CM-LSH
OpenAI Provider
ONNX Provider
Native AccelerationN/A✅ (Rust ext)N/A
Hasher Abstraction
Error Types

Float Precision Notes

Standard LSH: No Impact

Float precision differences have zero impact on standard LSH because:

// Only the sign matters, not the magnitude
sign_bit = (dot_product > 0.0)

// Examples (all produce bit = 1):
// Rust (f32): 0.123456789
// Python (f64): 0.123456789012345
// Result: Both > 0 → bit = 1 ✅

CM-LSH: Minor Impact

CM-LSH uses magnitude thresholds:

# Confidence threshold at 45th percentile
threshold = percentile(abs_projections, 45)
confident = (abs(proj) > threshold)

Sources of variance:

  1. Percentile calculation (sorting float arrays)
  2. Threshold comparison (f32 vs f64 precision)

Mitigation:

  • Confidence bits are advisory (not critical)
  • Direction bits (hashA) remain identical
  • Calibration adjusts for minor differences

Measured impact: ~5% bit difference in confidence hash, <1% impact on similarity estimates.


Production Considerations

When Cross-Language Compatibility Matters

Critical:

  • Comparing signatures generated by different language implementations
  • Distributed systems with mixed language services
  • Migrating between languages (e.g., Python prototype → Rust production)

Not Critical:

  • Single-language deployment
  • All signatures generated by same implementation
  • No cross-language signature exchange

Best Practices

  1. Use the same version across all services

    • V0 signatures are comparable with V0 only
    • V1 signatures are comparable with V1 only
  2. Validate with test vectors when implementing custom logic

    • Run test suite after any LSH modifications
    • Compare against canonical vectors
  3. Document float precision if using CM-LSH in multi-language setup

    • Note that confidence bits may differ slightly
    • Direction bits remain identical
  4. Use canonical SHA256 format for embedding hashes

    • Ensures consistency for deduplication
    • Enables cross-language embedding cache

See Also