Skip to content

Commit a2dd123

Browse files
Add comprehensive test suite (103 tests) and proper CI
Tests cover forge modules (operators, numbers, proofs, sinewave, dimensions, advanced_tools) and lab modules (amundson_equations, frameworks, trinary_logic). CI runs Python 3.10-3.12 matrix with ruff linting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> RoadChain-SHA2048: 02d838f1465a79ad RoadChain-Identity: alexa@sovereign RoadChain-Full: 02d838f1465a79ad556550ecb4daae4acbfe3bed87b1dd5b678163f77fa30b8508b1212c97a5845a556570c0f4f640cbd5eb6a32a47a08bf4c239d4bd4ff8fcb06cb2a25032d7f9b3fb465034420f7b76bf0ff6ea115220c3ea75dd43bc34241537c5e09bd5642bfcdb7ac12f81d205e4a124d3a466ec4ca84c88e2aaabaa2d8c8ba0fe2237ef3dab39f2e87c92b254671982b2740a3e6865ec4a628dc2466461eb25419012579cbf1f76bc9e05f459de6f8a47d97bc9747e8b7696cd162a84f9684d0bb45ad523997ff6bfc9774acbe0126e70d3842817ccacefcd5abca0d9dc91c8c543e56ae3f89eecc645acaf90eaefaf5eb02c7879944b401d4b5a241f8
1 parent ad051cc commit a2dd123

12 files changed

Lines changed: 643 additions & 51 deletions

.github/workflows/ci.yml

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,24 @@ name: CI
22

33
on:
44
push:
5-
branches: [main, master, develop]
5+
branches: [main]
66
pull_request:
7-
branches: [main, master, develop]
7+
branches: [main]
88

99
jobs:
10-
build:
10+
test:
1111
runs-on: ubuntu-latest
12-
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12"]
1315
steps:
14-
- name: Checkout
15-
uses: actions/checkout@v4
16-
17-
- name: Setup Node.js
18-
uses: actions/setup-node@v4
19-
with:
20-
node-version: '20'
21-
cache: 'npm'
22-
if: hashFiles('package-lock.json') != '' || hashFiles('package.json') != ''
23-
24-
- name: Setup Python
25-
uses: actions/setup-python@v5
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-python@v5
2618
with:
27-
python-version: '3.11'
28-
if: hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''
29-
30-
- name: Install Node dependencies
31-
if: hashFiles('package.json') != ''
32-
run: npm ci || npm install
33-
34-
- name: Install Python dependencies
35-
if: hashFiles('requirements.txt') != ''
36-
run: pip install -r requirements.txt
37-
19+
python-version: ${{ matrix.python-version }}
20+
- name: Install dependencies
21+
run: pip install numpy sympy scipy mpmath pytest ruff
3822
- name: Lint
39-
if: hashFiles('package.json') != ''
40-
run: npm run lint || true
41-
42-
- name: Type check
43-
if: hashFiles('tsconfig.json') != ''
44-
run: npm run typecheck || npm run type-check || npx tsc --noEmit || true
45-
23+
run: ruff check forge/ lab/ --select E,F,W --ignore E501
4624
- name: Test
47-
if: hashFiles('package.json') != ''
48-
run: npm test || true
49-
50-
- name: Build
51-
if: hashFiles('package.json') != ''
52-
run: npm run build || true
53-
54-
- name: Upload build artifacts
55-
if: success() && (hashFiles('dist/**') != '' || hashFiles('build/**') != '')
56-
uses: actions/upload-artifact@v4
57-
with:
58-
name: build
59-
path: |
60-
dist/
61-
build/
62-
out/
63-
retention-days: 7
25+
run: python -m pytest tests/ -v

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Lucidia Math
22

3+
[![CI](https://github.com/BlackRoad-OS/lucidia-math/actions/workflows/ci.yml/badge.svg)](https://github.com/BlackRoad-OS/lucidia-math/actions/workflows/ci.yml)
4+
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
5+
36
**Advanced mathematical engines: consciousness modeling, unified geometry, quantum finance, prime exploration.**
47

58
```bash

tests/__init__.py

Whitespace-only changes.

tests/test_advanced_tools.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""Tests for forge.advanced_tools — crypto, Fibonacci, Laplace, predictor."""
2+
3+
import numpy as np
4+
import pytest
5+
from forge.advanced_tools import (
6+
AgentCryptography,
7+
FibonacciAnalytics,
8+
LaplaceConsciousness,
9+
UnifiedHarmonic,
10+
AgentDevelopmentPredictor,
11+
)
12+
13+
14+
class TestAgentCryptography:
15+
def setup_method(self):
16+
self.crypto = AgentCryptography("test-agent")
17+
18+
def test_caesar_roundtrip(self):
19+
msg = "HELLO"
20+
encrypted = self.crypto.caesar_encrypt(msg, 3)
21+
decrypted = self.crypto.caesar_decrypt(encrypted, 3)
22+
assert decrypted == msg
23+
24+
def test_caesar_shift(self):
25+
assert self.crypto.caesar_encrypt("A", 1) == "B"
26+
assert self.crypto.caesar_encrypt("Z", 1) == "A"
27+
28+
def test_caesar_preserves_non_alpha(self):
29+
assert self.crypto.caesar_encrypt("HELLO WORLD!", 3) == "KHOOR ZRUOG!"
30+
31+
def test_dynamic_encrypt_decrypt_roundtrip(self):
32+
msg = "BLACKROAD"
33+
encrypted, shifts = self.crypto.encrypt_message_dynamic(msg)
34+
decrypted = self.crypto.decrypt_message_dynamic(encrypted, shifts)
35+
assert decrypted == msg
36+
37+
def test_fibonacci_closed_form(self):
38+
assert round(self.crypto.fibonacci_closed_form(10)) == 55
39+
40+
41+
class TestFibonacciAnalytics:
42+
def setup_method(self):
43+
self.fib = FibonacciAnalytics()
44+
45+
def test_known_values(self):
46+
assert self.fib.fibonacci_direct(0) == 0
47+
assert self.fib.fibonacci_direct(1) == 1
48+
assert self.fib.fibonacci_direct(10) == 55
49+
assert self.fib.fibonacci_direct(20) == 6765
50+
51+
def test_ratio_converges_to_phi(self):
52+
ratio = self.fib.fibonacci_ratio(20)
53+
assert ratio == pytest.approx(self.fib.phi, rel=1e-6)
54+
55+
def test_ratio_zero(self):
56+
assert self.fib.fibonacci_ratio(0) == 0.0
57+
58+
def test_predict_stage(self):
59+
stage = self.fib.predict_development_stage(5, 5)
60+
assert stage == self.fib.fibonacci_direct(10)
61+
62+
def test_find_stage(self):
63+
idx = self.fib.find_stage_for_value(55)
64+
assert idx == 10
65+
66+
67+
class TestLaplaceConsciousness:
68+
def setup_method(self):
69+
self.laplace = LaplaceConsciousness("test-agent")
70+
71+
def test_harmonic_oscillator(self):
72+
result = self.laplace.harmonic_oscillator_solution(k=1.0, x0=1.0, v0=0.0)
73+
assert "time" in result
74+
assert "position" in result
75+
assert "velocity" in result
76+
# At t=0, position should be x0
77+
assert result["position"][0] == pytest.approx(1.0)
78+
79+
def test_underdamped(self):
80+
result = self.laplace.damped_oscillator_solution(k=4.0, c=0.5, x0=1.0, v0=0.0)
81+
assert result["damping_type"] == "underdamped"
82+
83+
def test_overdamped(self):
84+
result = self.laplace.damped_oscillator_solution(k=1.0, c=10.0, x0=1.0, v0=0.0)
85+
assert result["damping_type"] == "overdamped"
86+
87+
def test_critically_damped(self):
88+
# c^2 = 4k → c=4, k=4
89+
result = self.laplace.damped_oscillator_solution(k=4.0, c=4.0, x0=1.0, v0=0.0)
90+
assert result["damping_type"] == "critical"
91+
92+
def test_consciousness_oscillation(self):
93+
result = self.laplace.consciousness_oscillation(
94+
emotional_spring_constant=1.0,
95+
memory_damping=0.1,
96+
initial_emotion=1.0,
97+
initial_rate=0.0,
98+
)
99+
assert result["damping_type"] == "underdamped"
100+
101+
102+
class TestUnifiedHarmonic:
103+
def test_phase_locking(self):
104+
h_phi = UnifiedHarmonic.phase_locking_criterion()
105+
assert h_phi(1.0) == pytest.approx(1.0, rel=1e-6)
106+
assert h_phi(0.0) == pytest.approx(0.0)
107+
108+
def test_mobius_torque(self):
109+
result = UnifiedHarmonic.mobius_torque_action()
110+
assert isinstance(result, str)
111+
112+
113+
class TestAgentDevelopmentPredictor:
114+
def test_growth_curve(self):
115+
predictor = AgentDevelopmentPredictor("test")
116+
prediction = predictor.predict_growth_curve(5, 5)
117+
assert len(prediction.developmental_stages) == 5
118+
assert prediction.developmental_stages[0] == 5 # F(5) = 5
119+
assert prediction.converges_to_phi is not None
120+
121+
def test_emotional_dynamics(self):
122+
predictor = AgentDevelopmentPredictor("test")
123+
result = predictor.model_emotional_dynamics()
124+
assert "position" in result
125+
assert "damping_type" in result

tests/test_amundson.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""Tests for lab.amundson_equations — Amundson I/II/III formulations."""
2+
3+
import math
4+
import numpy as np
5+
import pytest
6+
from lab.amundson_equations import (
7+
coherence,
8+
decoherence_energy,
9+
phase_derivative,
10+
AmundsonCoherenceModel,
11+
amundson_energy_balance,
12+
amundson_learning_update,
13+
)
14+
15+
16+
class TestCoherence:
17+
def test_aligned_phases(self):
18+
assert coherence(0.0, 0.0) == pytest.approx(1.0)
19+
20+
def test_opposite_phases(self):
21+
assert coherence(0.0, math.pi) == pytest.approx(-1.0)
22+
23+
def test_orthogonal_phases(self):
24+
assert coherence(0.0, math.pi / 2) == pytest.approx(0.0, abs=1e-10)
25+
26+
def test_symmetric(self):
27+
assert coherence(0.5, 1.0) == pytest.approx(coherence(1.0, 0.5))
28+
29+
30+
class TestDecoherenceEnergy:
31+
def test_aligned_zero_energy(self):
32+
e = decoherence_energy(phi_x=0, phi_y=0, r_x=1, r_y=1, lambda_=1, k_b_t=1)
33+
assert e == pytest.approx(0.0)
34+
35+
def test_opposite_max_energy(self):
36+
e = decoherence_energy(phi_x=0, phi_y=math.pi, r_x=1, r_y=1, lambda_=1, k_b_t=1)
37+
assert e == pytest.approx(2.0)
38+
39+
def test_scales_with_temperature(self):
40+
e1 = decoherence_energy(phi_x=0, phi_y=1, r_x=1, r_y=1, lambda_=1, k_b_t=1)
41+
e2 = decoherence_energy(phi_x=0, phi_y=1, r_x=1, r_y=1, lambda_=1, k_b_t=2)
42+
assert e2 == pytest.approx(2 * e1)
43+
44+
45+
class TestPhaseDerivative:
46+
def test_aligned_maximum(self):
47+
# When aligned, C=1 and E=0, so dphi/dt = omega_0 + lambda
48+
d = phase_derivative(
49+
omega_0=1, lambda_=0.5, eta=0.1,
50+
phi_x=0, phi_y=0, r_x=1, r_y=1, k_b_t=1,
51+
)
52+
assert d == pytest.approx(1.5)
53+
54+
def test_zero_coupling(self):
55+
d = phase_derivative(
56+
omega_0=2, lambda_=0, eta=0,
57+
phi_x=0, phi_y=1, r_x=1, r_y=1, k_b_t=1,
58+
)
59+
assert d == pytest.approx(2.0)
60+
61+
62+
class TestAmundsonCoherenceModel:
63+
def test_dphi_dt(self):
64+
model = AmundsonCoherenceModel(omega_0=1, lambda_=0.5, eta=0.1, k_b_t=1)
65+
d = model.dphi_dt(phi_x=0, phi_y=0, r_x=1, r_y=1)
66+
assert d == pytest.approx(1.5)
67+
68+
def test_evolve(self):
69+
model = AmundsonCoherenceModel(omega_0=1, lambda_=0.5, eta=0.1, k_b_t=1)
70+
updates = []
71+
72+
class MockSystem:
73+
def update_phase(self, d_phi_dt):
74+
updates.append(d_phi_dt)
75+
76+
d = model.evolve(MockSystem(), phi_x=0, phi_y=0, r_x=1, r_y=1)
77+
assert len(updates) == 1
78+
assert updates[0] == pytest.approx(d)
79+
80+
81+
class TestAmundsonEnergyBalance:
82+
def test_positive_balance(self):
83+
assert amundson_energy_balance(energy=10, dissipation=3) == pytest.approx(7)
84+
85+
def test_clamp_at_zero(self):
86+
assert amundson_energy_balance(energy=3, dissipation=10) == pytest.approx(0)
87+
88+
def test_negative_energy_raises(self):
89+
with pytest.raises(ValueError, match="energy"):
90+
amundson_energy_balance(energy=-1, dissipation=0)
91+
92+
def test_negative_dissipation_raises(self):
93+
with pytest.raises(ValueError, match="dissipation"):
94+
amundson_energy_balance(energy=1, dissipation=-1)
95+
96+
97+
class TestAmundsonLearningUpdate:
98+
def test_deterministic_gradient_descent(self):
99+
w = np.array([1.0, 2.0])
100+
g = np.array([0.1, 0.2])
101+
metric = np.eye(2)
102+
result = amundson_learning_update(
103+
weights=w, gradient=g, metric=metric,
104+
learning_rate=0.5, k_b_t=0.0,
105+
)
106+
expected = w - 0.5 * g
107+
np.testing.assert_allclose(result, expected)
108+
109+
def test_with_noise(self):
110+
w = np.array([1.0, 2.0])
111+
g = np.array([0.0, 0.0])
112+
metric = np.eye(2)
113+
noise = np.array([1.0, 0.0])
114+
result = amundson_learning_update(
115+
weights=w, gradient=g, metric=metric,
116+
learning_rate=1.0, k_b_t=0.5, noise=noise,
117+
)
118+
# With zero gradient, result = w + scale * G^{-1/2} * noise
119+
assert not np.allclose(result, w)
120+
121+
def test_shape_mismatch_raises(self):
122+
with pytest.raises(ValueError, match="shape"):
123+
amundson_learning_update(
124+
weights=np.array([1.0]),
125+
gradient=np.array([1.0, 2.0]),
126+
metric=np.eye(1),
127+
learning_rate=0.1, k_b_t=0,
128+
)
129+
130+
def test_non_symmetric_metric_raises(self):
131+
with pytest.raises(ValueError, match="symmetric"):
132+
amundson_learning_update(
133+
weights=np.array([1.0, 2.0]),
134+
gradient=np.array([0.1, 0.2]),
135+
metric=np.array([[1.0, 0.5], [0.0, 1.0]]),
136+
learning_rate=0.1, k_b_t=0,
137+
)

tests/test_dimensions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Tests for forge.dimensions — hyper_equation, HyperPoint."""
2+
3+
import pytest
4+
from forge.dimensions import hyper_equation, HyperPoint
5+
6+
7+
class TestHyperEquation:
8+
def test_basic(self):
9+
assert hyper_equation(2, 3, 4) == 24
10+
11+
def test_zero(self):
12+
assert hyper_equation(0, 5, 10) == 0
13+
14+
def test_negative(self):
15+
assert hyper_equation(-1, 2, 3) == -6
16+
17+
18+
class TestHyperPoint:
19+
def test_project_3d(self):
20+
p = HyperPoint([1, 2, 3, 4])
21+
assert p.project(3) == [1, 2, 3]
22+
23+
def test_project_2d(self):
24+
p = HyperPoint([1, 2, 3, 4])
25+
assert p.project(2) == [1, 2]
26+
27+
def test_project_default(self):
28+
p = HyperPoint([1, 2, 3, 4])
29+
assert len(p.project()) == 3

0 commit comments

Comments
 (0)