feat: neural_sim Python golden functional reference simulator
Adds tools/neural_sim/, a NumPy-based reference implementation of the FPGA-Neural V2 numeric model (INT8 in/weight, 32-bit wraparound accumulation, ReLU+saturate out), derived directly from hardware/v2/rtl/neural_processor.v (not assumed) and reusing tools/validation/mac_oracle.py's own pre-existing, hand-verified two's-complement primitives rather than duplicating them. Provides: neuron/layer/network models, a logical memory model of the real V2 SDRAM map (weights/activations/results), deterministic test-vector generators (simple/signed/extremes/zero/random/D-Stress 256x128) with JSON golden-vector export, an FPGA-vs-Python bit-exact comparison utility, four example networks, a CLI (`python -m tools.neural_sim ...`), and a 96-test pytest suite (all passing) covering signed-arithmetic edge cases (including a direct 32-bit wraparound proof), scalar-vs-vectorized neuron cross-checks, layer/memory/vector/comparison tests. This is a golden functional reference (bit-exact numeric result), explicitly NOT a cycle-accurate FPGA simulator -- see tools/neural_sim/README.md for the full scope statement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from tools.neural_sim.layer import FCLayer
|
||||
from tools.neural_sim.neuron import neuron_scalar
|
||||
|
||||
|
||||
def test_layer_matches_per_neuron_scalar_reference():
|
||||
rng = np.random.default_rng(7)
|
||||
n_inputs, n_neurons = 32, 5
|
||||
weights = rng.integers(-128, 128, size=(n_neurons, n_inputs), dtype=np.int64)
|
||||
biases = rng.integers(-128, 128, size=n_neurons, dtype=np.int64)
|
||||
inputs = rng.integers(-128, 128, size=n_inputs, dtype=np.int64)
|
||||
|
||||
layer = FCLayer(weights, biases=biases, activation="relu")
|
||||
outputs = layer.forward(inputs)
|
||||
|
||||
for n in range(n_neurons):
|
||||
expected = neuron_scalar(inputs.tolist(), weights[n].tolist(), bias=int(biases[n]), activation="relu")
|
||||
assert outputs[n] == expected
|
||||
|
||||
|
||||
def test_layer_rejects_bad_input_shape():
|
||||
layer = FCLayer([[1] * 8])
|
||||
with pytest.raises(ValueError):
|
||||
layer.forward([1] * 7)
|
||||
|
||||
|
||||
def test_layer_rejects_non_multiple_of_p_in():
|
||||
with pytest.raises(ValueError):
|
||||
FCLayer([[1] * 7])
|
||||
|
||||
|
||||
def test_layer_default_bias_is_zero():
|
||||
layer = FCLayer([[1] * 8, [2] * 8])
|
||||
assert layer.biases.tolist() == [0, 0]
|
||||
|
||||
|
||||
def test_layer_all_outputs_in_int8_range():
|
||||
rng = np.random.default_rng(99)
|
||||
weights = rng.integers(-128, 128, size=(20, 64), dtype=np.int64)
|
||||
inputs = rng.integers(-128, 128, size=64, dtype=np.int64)
|
||||
outputs = FCLayer(weights).forward(inputs)
|
||||
assert all(-128 <= v <= 127 for v in outputs.tolist())
|
||||
Reference in New Issue
Block a user