From ed8327ed814e0cb5e590069856a6d7a60a96e2e0 Mon Sep 17 00:00:00 2001 From: YurenHao0426 Date: Sat, 29 Aug 2026 13:10:29 -0500 Subject: feat: reconstruct the Dillavou physical grid --- experiments/physical_grid_smoke.py | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 experiments/physical_grid_smoke.py (limited to 'experiments/physical_grid_smoke.py') diff --git a/experiments/physical_grid_smoke.py b/experiments/physical_grid_smoke.py new file mode 100644 index 0000000..a84ed84 --- /dev/null +++ b/experiments/physical_grid_smoke.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +"""Mechanics checks for the reconstructed Figure-5 physical grid.""" + +from __future__ import annotations + +from pathlib import Path +import sys + +import numpy as np + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from sdil.physical_grid import ( # noqa: E402 + EdgePolynomialPredictor, + GridCircuit, + GridSquareLawImperfection, + _residual_and_jacobian, + edge_voltage_drops, + fit_edge_predictor, + output_difference, + solve_grid_state, +) + + +def sample_local_states( + circuit: GridCircuit, count: int, seed: int +) -> np.ndarray: + rng = np.random.default_rng(seed) + states = [] + for _ in range(count): + gates = rng.normal(2.33, 0.08, circuit.edge_count) + sources = circuit.source_values( + rng.uniform(circuit.low_voltage, circuit.high_voltage), + rng.uniform(circuit.low_voltage, circuit.high_voltage), + ) + voltages = solve_grid_state(circuit, gates, sources) + states.append(edge_voltage_drops(circuit, voltages)) + return np.asarray(states) + + +def main() -> None: + circuit = GridCircuit() + assert circuit.edge_count == 32 + assert circuit.source_nodes == (15, 13, 7, 5) + assert circuit.target_nodes == (10, 8) + gates = np.full(circuit.edge_count, 2.33) + sources = circuit.source_values(0.2266, 0.2266) + free = solve_grid_state(circuit, gates, sources) + assert np.allclose(free[list(circuit.source_nodes)], sources) + assert abs(output_difference(circuit, free)) < 1e-12 + residual, _ = _residual_and_jacobian(circuit, gates, free) + unknown = [ + node for node in range(circuit.node_count) + if node not in circuit.source_nodes + ] + assert np.linalg.norm(residual[unknown], ord=np.inf) < 1e-10 + + target_difference = 0.018 + target_mean = float(np.mean(free[list(circuit.target_nodes)])) + target_values = np.asarray(( + target_mean + 0.5 * target_difference, + target_mean - 0.5 * target_difference, + )) + clamped = solve_grid_state( + circuit, gates, sources, target_values=target_values, + initial_state=free) + assert np.allclose(clamped[list(circuit.target_nodes)], target_values) + assert abs(output_difference(circuit, clamped) - target_difference) < 1e-12 + + ideal = GridSquareLawImperfection.ideal(circuit.edge_count) + free_drops = edge_voltage_drops(circuit, free) + clamped_drops = edge_voltage_drops(circuit, clamped) + assert np.array_equal( + ideal.observed_rate( + circuit.measured_learning_rate, free_drops, clamped_drops), + ideal.ideal_rate( + circuit.measured_learning_rate, free_drops, clamped_drops), + ) + + hardware = GridSquareLawImperfection.sample_appendix_c( + circuit.edge_count, 20260829) + calibration_states = sample_local_states(circuit, 48, 20260830) + neutral = np.asarray([ + hardware.neutral_bias(circuit.measured_learning_rate, state) + for state in calibration_states + ]) + center = np.mean(calibration_states, axis=0) + scale = np.maximum(np.ptp(calibration_states, axis=0), 1e-3) + constant = EdgePolynomialPredictor.zeros(center, scale, degree=0) + quadratic = EdgePolynomialPredictor.zeros(center, scale, degree=2) + assert fit_edge_predictor(constant, calibration_states, neutral) == 48 + assert fit_edge_predictor(quadratic, calibration_states, neutral) == 48 + heldout_states = sample_local_states(circuit, 16, 20260831) + heldout_neutral = np.asarray([ + hardware.neutral_bias(circuit.measured_learning_rate, state) + for state in heldout_states + ]) + constant_rmse = float(np.sqrt(np.mean([ + np.square(measurement - constant.predict(state)) + for state, measurement in zip(heldout_states, heldout_neutral) + ]))) + quadratic_rmse = float(np.sqrt(np.mean([ + np.square(measurement - quadratic.predict(state)) + for state, measurement in zip(heldout_states, heldout_neutral) + ]))) + assert quadratic_rmse < 1e-7 + assert constant_rmse > 1e-3 + print({ + "nodes": circuit.node_count, + "edges": circuit.edge_count, + "source_nodes": circuit.source_nodes, + "target_nodes": circuit.target_nodes, + "constant_neutral_rmse_v_per_s": constant_rmse, + "quadratic_neutral_rmse_v_per_s": quadratic_rmse, + "autodiff_used": False, + }) + + +if __name__ == "__main__": + main() -- cgit v1.2.3