Thank you for your interest in contributing to the Ternary Engine library! This document provides guidelines for contributing code, documentation, and other improvements.
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Testing Requirements
- Documentation
- Pull Request Process
- Performance Guidelines
- Adding New Operations
This project follows the principle of technical accuracy and professional objectivity:
- Focus on facts and problem-solving
- Provide constructive, objective feedback
- Respect diverse approaches and perspectives
- Prioritize code quality and maintainability
-
Development Environment:
- C++17-capable compiler (GCC 7+, Clang 5+, MSVC 2017+)
- Python 3.7+
- AVX2-capable CPU (Intel Haswell 2013+ or AMD Excavator 2015+)
-
Dependencies:
pip install pybind11 numpy
-
Build the Library:
python build/build.py
-
Run Tests:
# Unified runner (recommended -- runs all wired suites, 15 as of 2026-08) python tests/run_tests.py # Or individually python tests/python/test_phase0.py python tests/python/test_omp.py
ternary-engine/
├── src/ # Centralized source code
│ ├── core/ # Production kernel (algebra, SIMD, FFI)
│ └── engine/ # Python bindings and libraries
├── docs/ # Documentation (organized by category)
├── tests/ # Test suite (python/, cpp/, run_tests.py)
├── benchmarks/ # Performance benchmarks
├── build/ # Build scripts (flat, no build/scripts/ subdirectory)
├── models/ # LLM/Neural Network integration (TritNet, etc.)
├── research/ # Ternary semantic hypothesis falsification framework
├── reports/ # Dated session/analysis reports
└── local-reports/ # Development notes (not in git)
git clone https://github.com/YOUR_USERNAME/ternary-engine.git
cd ternary-enginegit checkout -b feature/my-new-feature
# or
git checkout -b bugfix/issue-123- Source code: Edit files under
src/core/(production kernel) orsrc/engine/(Python bindings) -- not at repo root - Documentation: Update files in
docs/directory - Tests: Add or modify tests in
tests/python/ortests/cpp/ - Build scripts: Modify files directly in
build/(noscripts/subdirectory)
# Build
python build/build.py
# Test correctness (unified runner, recommended)
python tests/run_tests.py
# Test performance
python benchmarks/python-with-interpreter-overhead/bench_simd_core_ops.pygit add .
git commit -m "Brief description of changes
Detailed explanation if needed:
- What was changed
- Why it was changed
- Performance impact (if applicable)"git push origin feature/my-new-featureThen create a Pull Request on GitHub.
- Source files: Kernel code (
.h) goes undersrc/core/(algebra/,simd/,ffi/, etc.), organized by subsystem, not at repo root; Python bindings (.cpp) go undersrc/engine/ - Header guards: Use
#ifndef HEADER_NAME_Hformat - Includes: Group system headers, then library headers, then local headers
// Types: CamelCase
struct TernaryVector { ... };
enum class SIMDLevel { ... };
// Functions: snake_case
trit tadd(trit a, trit b);
__m256i tadd_simd(const uint8_t* a, const uint8_t* b);
// Constants: UPPER_CASE
constexpr int OMP_THRESHOLD = 100000;
constexpr int PREFETCH_DIST = 512;
// Variables: snake_case
size_t array_size = 1000;
__m256i vec_result;Use OPT-XXX tags to track optimizations:
// OPT-PHASE3-01: Adaptive OMP threshold
static const ssize_t OMP_THRESHOLD = 32768 * std::thread::hardware_concurrency();
// OPT-AUTO-LUT: Constexpr compile-time LUT generation
constexpr auto TADD_LUT = make_binary_lut(tadd_logic);// Good: Explains WHY
// Use adaptive threshold to scale with CPU core count
static const ssize_t OMP_THRESHOLD = 32768 * std::thread::hardware_concurrency();
// Bad: Explains WHAT (obvious from code)
// Set threshold to 32768 times hardware concurrency
static const ssize_t OMP_THRESHOLD = 32768 * std::thread::hardware_concurrency();Follow PEP 8 with these additions:
# Imports: standard, third-party, local
import sys
import numpy as np
import ternary_simd_engine as tc
# Type hints (Python 3.7+)
def process_array(data: np.ndarray, size: int) -> np.ndarray:
...
# Docstrings
def int_to_trit(value: int) -> int:
"""Convert integer (-1, 0, +1) to trit encoding (0b00, 0b01, 0b10).
Args:
value: Integer in range [-1, 0, 1]
Returns:
Trit encoding (0b00, 0b01, or 0b10)
"""All Python scripts that import project modules should use this standard pattern:
from pathlib import Path
import sys
PROJECT_ROOT = Path(__file__).parent.parent.resolve() # adjust .parent count to match depth -- see below
sys.path.insert(0, str(PROJECT_ROOT))Why this pattern:
- Consistent across the entire codebase
- Readable and maintainable
- Platform-independent (works on Windows, Linux, macOS)
- Explicit about what's being added to sys.path
- Uses
.resolve()to get absolute paths
Anti-patterns to avoid:
# DON'T: Chained os.path calls (hard to read)
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# DON'T: Relative string paths (fragile)
sys.path.insert(0, '..')
# DON'T: Multiple sys.path additions (causes import confusion)
sys.path.insert(0, str(ROOT_DIR))
sys.path.insert(0, str(ROOT_DIR / "models" / "tritnet" / "src"))Getting the .parent count right (verified 2026-08-16 against the real
files below; this is the single most common bug found across this whole
project's docs and scripts -- always count subdirectories between the file
and the repo root, then use exactly that many .parent calls):
build/build.py-- 1 subdirectory deep ->Path(__file__).parent.parent.resolve()(2.parents)tests/python/test_phase0.py-- 2 subdirectories deep ->Path(__file__).parent.parent.parent.resolve()(3.parents)models/tritnet/src/train_tritnet.py-- 3 subdirectories deep ->Path(__file__).parent.parent.parent.parent.resolve()(4.parents)
This ensures all scripts add the project root to sys.path, enabling imports like:
import ternary_simd_engine as tc
from models.tritnet.src.ternary_layers import TernaryLinearAll code changes must pass the existing test suite:
python tests/run_tests.py # Must pass (runs all wired suites)When adding a new operation, add tests to tests/python/test_phase0.py:
def test_new_operation():
"""Test new operation correctness."""
# Test all edge cases
a = np.array([0b00, 0b01, 0b10], dtype=np.uint8)
b = np.array([0b00, 0b01, 0b10], dtype=np.uint8)
result = tc.new_op(a, b)
expected = np.array([...], dtype=np.uint8)
assert np.array_equal(result, expected), "new_op failed"
print("✓ new_op passed")For optimization changes, provide before/after benchmarks:
# Before optimization
python benchmarks/python-with-interpreter-overhead/bench_simd_core_ops.py > before.txt
# Apply changes and rebuild
# After optimization
python benchmarks/python-with-interpreter-overhead/bench_simd_core_ops.py > after.txt
# Include diff in PR descriptionWhen modifying source files, update corresponding documentation:
src/core/algebra/ternary_lut_gen.h→ No docs (self-documenting)src/core/algebra/ternary_algebra.h→docs/api-reference/ternary-core-header.mdsrc/core/common/ternary_errors.h→docs/api-reference/error-handling.mdsrc/engine/bindings_core_ops.cpp→docs/api-reference/ternary-core-simd.md
# Use ATX-style headers (not Setext)
## Section Header
# Code blocks: specify language
```cpp
__m256i result = _mm256_shuffle_epi8(lut, indices);
```
# Link to related sections
See [Performance Analysis](#performance-analysis) for details.
# Include examples
Example usage:
```python
result = tc.tadd(a, b)
```Update README.md if changes affect:
- API surface (new operations)
- Installation process
- Performance characteristics
- System requirements
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Performance improvement
- [ ] Documentation update
- [ ] Build system change
## Testing
- [ ] All existing tests pass
- [ ] New tests added for new functionality
- [ ] Benchmarks show no regression (or improvement)
## Performance Impact
Before: X operations/sec
After: Y operations/sec
Speedup: Z%
## Documentation
- [ ] Code comments updated
- [ ] API documentation updated
- [ ] README updated (if needed)
## Checklist
- [ ] Code follows project style guidelines
- [ ] Commit messages are clear and descriptive
- [ ] No warnings from compiler
- [ ] Changes are backward compatible (or migration guide provided)- Automated checks: CI runs
tests/run_tests.pyautomatically (see.github/workflows/ci.yml) - Code review: Maintainers review for correctness and style
- Performance review: Benchmark results reviewed
- Documentation review: Docs checked for accuracy
- Merge: Approved PRs merged to main branch
Only add complexity if it provides >10% performance gain:
// Good: 15% speedup justified
#pragma omp parallel for if(n >= OMP_THRESHOLD)
// Bad: 2% speedup, adds complexity
if (is_aligned(ptr)) {
// Aligned load path
} else {
// Unaligned load path
}Use consistent methodology:
# Fixed seed for reproducibility
np.random.seed(42)
# Warmup iterations (not measured)
for _ in range(100):
result = tc.tadd(a, b)
# Measured iterations
start = time.perf_counter()
for _ in range(1000):
result = tc.tadd(a, b)
elapsed = time.perf_counter() - start- Correctness - Never sacrifice correctness for speed
- Maintainability - Simple code is better than complex optimizations
- Measurable gains - Profile before optimizing
- Documentation - Explain WHY optimizations work
// Add operation logic function
constexpr trit new_op_logic(trit a, trit b) noexcept {
// Implement logic using -1, 0, +1 values
if (a == -1 && b == -1) return -1;
// ... define all 9 cases
return 0;
}// Add constexpr LUT generation
constexpr auto NEW_OP_LUT = make_binary_lut(new_op_logic);
// Add scalar operation
static FORCE_INLINE trit new_op(trit a, trit b) {
return NEW_OP_LUT[(a << 2) | b];
}// Add SIMD template specialization
template <bool Sanitize>
__m256i new_op_simd(__m256i va, __m256i vb) {
// Mask if sanitizing
if constexpr (Sanitize) {
const __m256i mask = _mm256_set1_epi8(0b11);
va = _mm256_and_si256(va, mask);
vb = _mm256_and_si256(vb, mask);
}
// Build indices
__m256i hi = _mm256_slli_epi16(va, 2);
__m256i indices = _mm256_or_si256(hi, vb);
// Broadcast LUT
__m128i lut_128 = _mm_loadu_si128((const __m128i*)NEW_OP_LUT.data());
__m256i lut_256 = _mm256_broadcastsi128_si256(lut_128);
// Lookup
return _mm256_shuffle_epi8(lut_256, indices);
}py::array_t<uint8_t> new_op_array(py::array_t<uint8_t> A, py::array_t<uint8_t> B) {
return process_binary_array<SANITIZE>(A, B, new_op_simd<SANITIZE>, new_op);
}PYBIND11_MODULE(ternary_simd_engine, m) {
// ... existing bindings
m.def("new_op", &new_op_array, "New operation",
py::arg("A"), py::arg("B"));
}def test_new_op():
# Test all cases
a = np.array([0b00, 0b01, 0b10], dtype=np.uint8)
b = np.array([0b00, 0b01, 0b10], dtype=np.uint8)
result = tc.new_op(a, b)
expected = compute_expected_new_op(a, b)
assert np.array_equal(result, expected)- Update
docs/api-reference/ternary-core-header.md - Update
docs/api-reference/ternary-core-simd.md - Update
README.mdoperations table
- Issues: Open an issue on GitHub
- Discussions: Use GitHub Discussions for questions
- Email: Contact maintainers (see NOTICE file)
By contributing, you agree that your contributions will be licensed under the Apache License 2.0.
Last Updated: 2026-08-16 (paths/structure corrected against the src/core+src/engine reorganization and Nov 2025 script renames; original content from 2025-10-13) Maintained by: Jonathan Verdun (Ternary Engine Project)