diff options
| author | YurenHao0426 <Blackhao0426@gmail.com> | 2026-08-29 12:51:32 -0500 |
|---|---|---|
| committer | YurenHao0426 <Blackhao0426@gmail.com> | 2026-08-29 12:51:32 -0500 |
| commit | 75422a917bffa308d3f6d84e705d1f7a35d74992 (patch) | |
| tree | 84508c3c623c97ddd191768153935c5299ca94e3 /sdil | |
| parent | 903f77bbf11b20011abc70645b06ccc0f669bfdf (diff) | |
feat: compose SDIL with physical overclamping
Diffstat (limited to 'sdil')
| -rw-r--r-- | sdil/physical_coupled.py | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/sdil/physical_coupled.py b/sdil/physical_coupled.py index f451947..5e5b181 100644 --- a/sdil/physical_coupled.py +++ b/sdil/physical_coupled.py @@ -198,12 +198,18 @@ def overclamped_clean_rate( *, nudging: float = 0.25, clamp_magnitude: Optional[float] = None, + exact_target: bool = False, ) -> tuple[Array, float, float]: - """Leading-order overclamping signal from Appendix F, Eq. F6--F8.""" + """Overclamping signal from Appendix F, Eq. F5 or its Eq. F6 limit.""" output_free = free_output(circuit, gates, task.input_voltage) error = task.label_voltage - output_free magnitude = circuit.high if clamp_magnitude is None else clamp_magnitude - output_clamped = output_free + nudging * magnitude * np.sign(error) + if exact_target: + output_clamped = output_free + nudging * ( + magnitude * np.sign(error) - output_free + ) + else: + output_clamped = output_free + nudging * magnitude * np.sign(error) rate = circuit.measured_learning_rate * ( voltage_drop_squares(circuit, output_free) - voltage_drop_squares(circuit, output_clamped) @@ -269,6 +275,9 @@ def simulate_alternating_tasks( seed: int = 0, summary_cycles: int = 20, record_history: bool = False, + overclamp_nudging: float = 0.25, + overclamp_magnitude: Optional[float] = None, + overclamp_exact_target: bool = False, ) -> dict: """Alternate two tasks using explicit local circuit updates. @@ -282,6 +291,7 @@ def simulate_alternating_tasks( allowed = { "raw", "frozen_constant", "frozen_sdil", "online_constant", "online_sdil", "oracle", "same_rms_noise", "overclamp", + "overclamp_sdil", } if method not in allowed: raise ValueError(f"unrecognized method {method}") @@ -291,7 +301,8 @@ def simulate_alternating_tasks( if period_seconds <= 0.0 or cycles < 1: raise ValueError("period and cycles must be positive") if method in { - "frozen_constant", "frozen_sdil", "online_constant", "online_sdil" + "frozen_constant", "frozen_sdil", "online_constant", "online_sdil", + "overclamp_sdil", } and predictor is None: raise ValueError(f"{method} requires a predictor") if method == "same_rms_noise" and noise_standard_deviation is None: @@ -318,6 +329,9 @@ def simulate_alternating_tasks( learning_on_time = 0.0 neutral_observations = 0 clipped_updates = 0 + clamp_displacement_l1_time = 0.0 + clamp_displacement_l2_time = 0.0 + max_abs_clamp_displacement = 0.0 for _ in range(cycles): half_endpoints = [] @@ -330,14 +344,26 @@ def simulate_alternating_tasks( neutral_observations += 1 for _ in range(half_steps): physical_bias = bias_field(gates, bias_strength) - if method == "overclamp": - clean_rate, output_free, _ = overclamped_clean_rate( - circuit, gates, task) + if method in {"overclamp", "overclamp_sdil"}: + clean_rate, output_free, output_clamped = overclamped_clean_rate( + circuit, + gates, + task, + nudging=overclamp_nudging, + clamp_magnitude=overclamp_magnitude, + exact_target=overclamp_exact_target, + ) duration = nominal_step * abs( task.label_voltage - output_free) / initial_error_scale - residual_bias = physical_bias + if method == "overclamp": + residual_bias = physical_bias + else: + residual_bias = ( + physical_bias - active_predictor.predict(gates) + ) else: - clean_rate, _, _ = standard_clean_rate(circuit, gates, task) + clean_rate, output_free, output_clamped = standard_clean_rate( + circuit, gates, task) duration = nominal_step if method == "raw": residual_bias = physical_bias @@ -359,6 +385,11 @@ def simulate_alternating_tasks( clipped_updates += int(np.any(clipped != proposed)) gates = clipped learning_on_time += duration + displacement = abs(output_clamped - output_free) + clamp_displacement_l1_time += duration * displacement + clamp_displacement_l2_time += duration * displacement * displacement + max_abs_clamp_displacement = max( + max_abs_clamp_displacement, displacement) half_endpoints.append(gates.copy()) half_task_errors.append(task_errors(circuit, gates, tasks)) half_task_errors_array = np.asarray(half_task_errors) @@ -385,6 +416,12 @@ def simulate_alternating_tasks( "std_cycle_span": float(np.std(spans)), "neutral_observations_during_learning": neutral_observations, "learning_on_time_seconds": float(learning_on_time), + "clamp_displacement_l1_time_v_s": float( + clamp_displacement_l1_time), + "clamp_displacement_l2_time_v2_s": float( + clamp_displacement_l2_time), + "max_abs_clamp_displacement_v": float( + max_abs_clamp_displacement), "clipped_updates": clipped_updates, "final_predictor_coefficients": ( None if active_predictor is None |
