Spaces:
Paused
Paused
Download src/playground/models.py from ThomasHeisig/MHRN-Space: direct link, hf CLI and curl.
- Browser
- Download file 70.2 kB
-
https://huggingface.co/spaces/ThomasHeisig/MHRN-Space/resolve/main/src/playground/models.py
- Command line
-
hf download hf://spaces/ThomasHeisig/MHRN-Space/src/playground/models.py
-
curl -L -o models.py https://huggingface.co/spaces/ThomasHeisig/MHRN-Space/resolve/main/src/playground/models.py
70.2 kB
| """Core data contracts for the non-canonical MHRN playground.""" | |
| from __future__ import annotations | |
| import math | |
| import re | |
| from dataclasses import dataclass | |
| from typing import Mapping, TypeAlias | |
| from .closed_loop import CLOSED_LOOP_PRESETS | |
| Coordinate: TypeAlias = tuple[float, ...] | |
| Edge: TypeAlias = tuple[int, int] | |
| ActionInputMap: TypeAlias = str | tuple[tuple[float, ...], ...] | |
| _NAME_RE = re.compile(r"[^A-Za-z0-9_.-]+") | |
| class Topology: | |
| """Neutral topology returned by every playground topology generator.""" | |
| name: str | |
| dimensions: int | |
| edges: tuple[Edge, ...] | |
| coordinates: tuple[Coordinate, ...] | |
| metadata: Mapping[str, object] | |
| class PlaygroundConfig: | |
| """Validated bounded configuration for one exploratory playground session.""" | |
| name: str = "playground_session" | |
| neuron_model: str = "izhikevich_rs" | |
| synapse_model: str = "static" | |
| plasticity_rule: str = "none" | |
| topology: str = "mhrn_5d" | |
| stimulus: str = "deterministic" | |
| readout: str = "population_vector" | |
| n_neurons: int = 128 | |
| edge_budget: int = 1024 | |
| ticks: int = 256 | |
| seed: int = 12345 | |
| dimensions: int = 5 | |
| dt_ms: float = 1.0 | |
| weight: float = 4.0 | |
| weight_decay: float = 0.0 | |
| weight_max_clamp: float = 100.0 | |
| delay_ticks: int = 1 | |
| stimulus_current: float = 8.0 | |
| stimulus_rate_hz: float = 20.0 | |
| radius: float = 0.35 | |
| k_neighbors: int = 16 | |
| rewiring_probability: float = 0.15 | |
| modules: int = 2 | |
| ensemble_runs: int = 1 | |
| persist: bool = False | |
| pan_enabled: bool = False | |
| pan_dimensions: int = 5 | |
| pan_closed_loop: bool = True | |
| pan_feedback_gain: float = 0.05 | |
| pan_health_decay: float = 0.001 | |
| pan_apoptosis_threshold: float = 0.1 | |
| pan_aging_threshold: float = 0.3 | |
| pan_bias_current: float = 10.0 | |
| clock_mode: str = "continuous" | |
| clock_base_hz: float = 100.0 | |
| clock_event_batch_ms: float = 10.0 | |
| neuron_backend: str = "cpu" | |
| execution_mode: str = "TICK_ONLY" | |
| execution_initial_mode: str = "EVENT_ONLY" | |
| execution_theta_high: float = 0.30 | |
| execution_theta_low: float = 0.05 | |
| execution_hysteresis: float = 0.02 | |
| execution_min_dwell: int = 100 | |
| execution_activity_window: int = 100 | |
| execution_transition_mode: str = "clean" | |
| execution_sync_on_switch: bool = True | |
| execution_log_transitions: bool = True | |
| execution_log_state_hash: bool = True | |
| growth_enabled: bool = False | |
| growth_neurogenesis: bool = True | |
| growth_synaptogenesis: bool = True | |
| growth_path_formation: bool = True | |
| growth_pruning: bool = True | |
| growth_activity_threshold: float = 0.25 | |
| growth_coactivation_threshold: int = 2 | |
| growth_information_threshold: float = 0.25 | |
| growth_prune_threshold: float = 0.05 | |
| growth_max_synapses_per_neuron: int = 128 | |
| growth_max_new_synapses_per_barrier: int = 8 | |
| cuda_budget_mb: int = 2048 | |
| offload_enabled: bool = False | |
| offload_snapshot_interval: int = 1000 | |
| hardware_profile_name: str = "reference_cpu" | |
| thalamic_gating_enabled: bool = False | |
| thalamic_relay_threshold: float = 0.0 | |
| thalamic_attention_gain: float = 1.15 | |
| thalamic_inhibition_gain: float = 0.35 | |
| cortical_layers_enabled: bool = False | |
| cortical_layer_count: int = 6 | |
| cortical_plasticity: bool = True | |
| cortical_learning_rate: float = 0.01 | |
| behavior_learning_enabled: bool = False | |
| behavior_action_count: int = 4 | |
| behavior_learning_rate: float = 0.2 | |
| behavior_epsilon: float = 0.2 | |
| behavior_target_action: int = 0 | |
| behavior_target_mode: str = "cycle" | |
| behavior_min_activity: float = 0.01 | |
| behavior_episode_ticks: int = 16 | |
| behavior_bias_current: float = 3.0 | |
| geometry_lambda_a: float = 0.5 | |
| geometry_lambda_b: float = 0.5 | |
| geometry_sigma: float = 0.1 | |
| geometry_p0: float = 0.3 | |
| geometry_mode: str = "mixed_additive" | |
| geometry_delay_velocity: float = 0.25 | |
| neural_io_enabled: bool = False | |
| neural_io_input_channels: int = 16 | |
| neural_io_output_channels: int = 16 | |
| neural_io_input_codec: str = "population_latency_v1" | |
| neural_io_output_decoder: str = "population_rate_v1" | |
| neural_io_input_payload: object = 0.5 | |
| neural_io_window_ticks: int = 16 | |
| neural_io_input_current: float = 25.0 | |
| neural_io_input_role: str = "GATEWAY_AFFERENT" | |
| neural_io_output_role: str = "GATEWAY_EFFERENT" | |
| neural_io_phase: str = "QUERY" | |
| neural_io_correlation_id: str = "" | |
| neural_io_modality: str = "digital" | |
| neural_io_source_id: str = "playground.input" | |
| closed_loop_preset: str = "custom" | |
| input_topology: str = "uniform" | |
| input_channels: int = 16 | |
| input_channel_map: tuple[tuple[int, ...], ...] = () | |
| input_amplitude_per_channel: tuple[float, ...] = () | |
| input_frequency_per_channel: tuple[float, ...] = () | |
| input_phase_per_channel: tuple[float, ...] = () | |
| input_noise_sigma: float = 0.0 | |
| target_cue_channel: int = 0 | |
| reward_cue_channel: int = 0 | |
| action_feedback_channel: int = 0 | |
| posture_score_channel: int = 2 | |
| reward_event_channel: int = 3 | |
| posture_current_scale: float = 25.0 | |
| reward_event_scale: float = 25.0 | |
| posture_reward_enabled: bool = False | |
| posture_weight_upright: float = 0.4 | |
| posture_weight_height: float = 0.3 | |
| posture_weight_stability: float = 0.2 | |
| posture_weight_symmetry: float = 0.1 | |
| posture_target_height: float = 1.0 | |
| posture_tilt_max: float = 1.0 | |
| posture_velocity_max: float = 5.0 | |
| trigger_good_score: float = 0.85 | |
| trigger_good_duration: int = 10 | |
| trigger_good_reward: float = 1.0 | |
| trigger_warning_score: float = 0.4 | |
| trigger_warning_reward: float = -0.3 | |
| trigger_falling_rate: float = -0.05 | |
| trigger_falling_reward: float = -1.0 | |
| trigger_collapse_score: float = 0.1 | |
| trigger_collapse_reward: float = -5.0 | |
| trigger_recovery_bonus: float = 2.0 | |
| reward_continuous_alpha: float = 0.1 | |
| episode_termination_enabled: bool = True | |
| episode_max_ticks: int = 256 | |
| episode_reset_on_collapse: bool = True | |
| pan_feedback_delay: int = 0 | |
| pan_feedback_source: str = "population" | |
| pan_feedback_target: str = "all" | |
| pan_feedback_nonlinearity: str = "linear" | |
| pan_feedback_threshold: float = 0.0 | |
| pan_feedback_saturation: float = 100.0 | |
| action_loop_enabled: bool = False | |
| action_loop_delay: int = 1 | |
| action_persistence: int = 1 | |
| action_to_input_map: ActionInputMap = "auto" | |
| action_space_size: int = 4 | |
| action_coupling_strength: float = 0.0 | |
| action_noise: float = 0.0 | |
| freeze_actions: bool = False | |
| frozen_action_sequence: tuple[int, ...] = () | |
| reward_signal_enabled: bool = False | |
| reward_magnitude: float = 1.0 | |
| reward_delay_ticks: int = 0 | |
| reward_shaping: str = "sparse" | |
| reward_baseline: float = 0.0 | |
| reward_decay: float = 0.0 | |
| reward_channel: int = 0 | |
| freeze_rewards: bool = False | |
| frozen_reward_sequence: tuple[float, ...] = () | |
| parity_reference_source: str = "CPU_PYTHON_PLAYGROUND" | |
| parity_reference_commit: str = "" | |
| target_cue_control: str = "aligned" | |
| target_encoding: str = "none" | |
| target_persistence: int = 1 | |
| target_cue_current: float = 0.0 | |
| target_shuffle: bool = False | |
| target_predictability: str = "deterministic" | |
| credit_window: int = 64 | |
| eligibility_trace_tau: float = 200.0 | |
| credit_assignment: str = "none" | |
| td_lambda: float = 0.9 | |
| gamma_discount: float = 0.95 | |
| neuron_threshold_variance: float = 0.0 | |
| neuron_tau_m_variance: float = 0.0 | |
| inhibitory_fraction: float = 0.0 | |
| gaba_strength: float = 1.0 | |
| e_i_ratio: float = 1.0 | |
| delay_distribution: str = "fixed" | |
| delay_mean_ticks: float = 1.0 | |
| refractory_variance: float = 0.0 | |
| adaptation_strength: float = 0.0 | |
| adaptation_tau: float = 200.0 | |
| oscillation_enabled: bool = False | |
| oscillation_frequency: float = 8.0 | |
| geometry_input_coupling: bool = False | |
| geometry_input_sigma: float = 0.2 | |
| sandbox_enabled: bool = False | |
| sandbox_physics: str = "stick_figure" | |
| sandbox_action_coupling: str = "direct" | |
| sandbox_sensor_noise: float = 0.05 | |
| def from_mapping(cls, payload: Mapping[str, object]) -> "PlaygroundConfig": | |
| """Build a config from untrusted API/CLI input and validate all bounds.""" | |
| defaults = cls() | |
| raw_payload = dict(payload) | |
| requested_preset = raw_payload.get("closed_loop_preset", "custom") | |
| if not isinstance(requested_preset, str): | |
| raise ValueError("closed_loop_preset must be a string") | |
| requested_preset = requested_preset.strip() or "custom" | |
| def resolve_preset(name: str, seen: set[str]) -> dict[str, object]: | |
| if name == "custom": | |
| return {} | |
| if name in seen: | |
| raise ValueError("closed_loop preset inheritance cycle") | |
| item = CLOSED_LOOP_PRESETS.get(name) | |
| if item is None: | |
| raise ValueError(f"unknown closed_loop_preset: {name}") | |
| raw_settings = item.get("settings", {}) | |
| if not isinstance(raw_settings, Mapping): | |
| raise ValueError(f"invalid settings for preset: {name}") | |
| settings = dict(raw_settings) | |
| parent = settings.pop("closed_loop_preset", None) | |
| merged: dict[str, object] = {} | |
| if isinstance(parent, str): | |
| merged.update(resolve_preset(parent, seen | {name})) | |
| merged.update(settings) | |
| return merged | |
| if requested_preset != "custom": | |
| preset_payload = resolve_preset(requested_preset, set()) | |
| preset_payload.update(raw_payload) | |
| payload = preset_payload | |
| else: | |
| payload = raw_payload | |
| def text(name: str, default: str) -> str: | |
| value = payload.get(name, default) | |
| if not isinstance(value, str): | |
| raise ValueError(f"{name} must be a string") | |
| value = value.strip() | |
| if not value: | |
| raise ValueError(f"{name} must not be empty") | |
| return value | |
| def integer(name: str, default: int) -> int: | |
| value = payload.get(name, default) | |
| if isinstance(value, bool) or not isinstance(value, (int, float)): | |
| raise ValueError(f"{name} must be an integer") | |
| converted = int(value) | |
| if float(value) != float(converted): | |
| raise ValueError(f"{name} must be an integer") | |
| return converted | |
| def number(name: str, default: float) -> float: | |
| value = payload.get(name, default) | |
| if isinstance(value, bool) or not isinstance(value, (int, float)): | |
| raise ValueError(f"{name} must be numeric") | |
| return float(value) | |
| def numbers(name: str, default: tuple[float, ...]) -> tuple[float, ...]: | |
| value = payload.get(name, default) | |
| if not isinstance(value, (list, tuple)): | |
| raise ValueError(f"{name} must be an array") | |
| result: list[float] = [] | |
| for item in value: | |
| if isinstance(item, bool) or not isinstance(item, (int, float)): | |
| raise ValueError(f"{name} values must be numeric") | |
| result.append(float(item)) | |
| return tuple(result) | |
| def integers(name: str, default: tuple[int, ...]) -> tuple[int, ...]: | |
| value = payload.get(name, default) | |
| if not isinstance(value, (list, tuple)): | |
| raise ValueError(f"{name} must be an array") | |
| result: list[int] = [] | |
| for item in value: | |
| if isinstance(item, bool) or not isinstance(item, (int, float)): | |
| raise ValueError(f"{name} values must be integers") | |
| converted = int(item) | |
| if float(item) != float(converted): | |
| raise ValueError(f"{name} values must be integers") | |
| result.append(converted) | |
| return tuple(result) | |
| def optional_text(name: str, default: str) -> str: | |
| value = payload.get(name, default) | |
| if not isinstance(value, str): | |
| raise ValueError(f"{name} must be a string") | |
| return value.strip() | |
| def integer_groups( | |
| name: str, | |
| default: tuple[tuple[int, ...], ...], | |
| ) -> tuple[tuple[int, ...], ...]: | |
| value = payload.get(name, default) | |
| if not isinstance(value, (list, tuple)): | |
| raise ValueError(f"{name} must be an array of arrays") | |
| groups: list[tuple[int, ...]] = [] | |
| for group in value: | |
| if not isinstance(group, (list, tuple)): | |
| raise ValueError(f"{name} must be an array of arrays") | |
| converted: list[int] = [] | |
| for item in group: | |
| if isinstance(item, bool) or not isinstance(item, (int, float)): | |
| raise ValueError( | |
| f"{name} values must be integer neuron indices" | |
| ) | |
| integer_value = int(item) | |
| if float(item) != float(integer_value): | |
| raise ValueError( | |
| f"{name} values must be integer neuron indices" | |
| ) | |
| converted.append(integer_value) | |
| groups.append(tuple(converted)) | |
| return tuple(groups) | |
| def action_map(name: str, default: ActionInputMap) -> ActionInputMap: | |
| value = payload.get(name, default) | |
| if isinstance(value, str): | |
| normalized = value.strip().lower() | |
| if normalized not in {"auto", "spatial"}: | |
| raise ValueError(f"{name} must be auto, spatial, or an array") | |
| return normalized | |
| if not isinstance(value, (list, tuple)): | |
| raise ValueError(f"{name} must be auto, spatial, or an array") | |
| rows: list[tuple[float, ...]] = [] | |
| for row in value: | |
| if not isinstance(row, (list, tuple)): | |
| raise ValueError(f"{name} array rows must be arrays") | |
| converted: list[float] = [] | |
| for item in row: | |
| if isinstance(item, bool) or not isinstance(item, (int, float)): | |
| raise ValueError(f"{name} values must be numeric") | |
| converted.append(float(item)) | |
| rows.append(tuple(converted)) | |
| return tuple(rows) | |
| raw_name = text("name", defaults.name) | |
| safe_name = _NAME_RE.sub("_", raw_name).strip("._-")[:80] or defaults.name | |
| config = cls( | |
| name=safe_name, | |
| neuron_model=text("neuron_model", defaults.neuron_model), | |
| synapse_model=text("synapse_model", defaults.synapse_model), | |
| plasticity_rule=text("plasticity_rule", defaults.plasticity_rule), | |
| topology=text("topology", defaults.topology), | |
| stimulus=text("stimulus", defaults.stimulus), | |
| readout=text("readout", defaults.readout), | |
| n_neurons=integer("n_neurons", defaults.n_neurons), | |
| edge_budget=integer("edge_budget", defaults.edge_budget), | |
| ticks=integer("ticks", defaults.ticks), | |
| seed=integer("seed", defaults.seed), | |
| dimensions=integer("dimensions", defaults.dimensions), | |
| dt_ms=number("dt_ms", defaults.dt_ms), | |
| weight=number("weight", defaults.weight), | |
| weight_decay=number("weight_decay", defaults.weight_decay), | |
| weight_max_clamp=number("weight_max_clamp", defaults.weight_max_clamp), | |
| delay_ticks=integer("delay_ticks", defaults.delay_ticks), | |
| stimulus_current=number("stimulus_current", defaults.stimulus_current), | |
| stimulus_rate_hz=number("stimulus_rate_hz", defaults.stimulus_rate_hz), | |
| radius=number("radius", defaults.radius), | |
| k_neighbors=integer("k_neighbors", defaults.k_neighbors), | |
| rewiring_probability=number( | |
| "rewiring_probability", defaults.rewiring_probability | |
| ), | |
| modules=integer("modules", defaults.modules), | |
| ensemble_runs=integer("ensemble_runs", defaults.ensemble_runs), | |
| persist=bool(payload.get("persist", defaults.persist)), | |
| pan_enabled=bool(payload.get("pan_enabled", defaults.pan_enabled)), | |
| pan_dimensions=integer("pan_dimensions", defaults.pan_dimensions), | |
| pan_closed_loop=bool( | |
| payload.get("pan_closed_loop", defaults.pan_closed_loop) | |
| ), | |
| pan_feedback_gain=number("pan_feedback_gain", defaults.pan_feedback_gain), | |
| pan_health_decay=number("pan_health_decay", defaults.pan_health_decay), | |
| pan_apoptosis_threshold=number( | |
| "pan_apoptosis_threshold", defaults.pan_apoptosis_threshold | |
| ), | |
| pan_aging_threshold=number( | |
| "pan_aging_threshold", defaults.pan_aging_threshold | |
| ), | |
| pan_bias_current=number("pan_bias_current", defaults.pan_bias_current), | |
| clock_mode=text("clock_mode", defaults.clock_mode), | |
| clock_base_hz=number("clock_base_hz", defaults.clock_base_hz), | |
| clock_event_batch_ms=number( | |
| "clock_event_batch_ms", defaults.clock_event_batch_ms | |
| ), | |
| neuron_backend=text("neuron_backend", defaults.neuron_backend).lower(), | |
| execution_mode=text("execution_mode", defaults.execution_mode).upper(), | |
| execution_initial_mode=text( | |
| "execution_initial_mode", defaults.execution_initial_mode | |
| ).upper(), | |
| execution_theta_high=number( | |
| "execution_theta_high", defaults.execution_theta_high | |
| ), | |
| execution_theta_low=number( | |
| "execution_theta_low", defaults.execution_theta_low | |
| ), | |
| execution_hysteresis=number( | |
| "execution_hysteresis", defaults.execution_hysteresis | |
| ), | |
| execution_min_dwell=integer( | |
| "execution_min_dwell", defaults.execution_min_dwell | |
| ), | |
| execution_activity_window=integer( | |
| "execution_activity_window", defaults.execution_activity_window | |
| ), | |
| execution_transition_mode=text( | |
| "execution_transition_mode", defaults.execution_transition_mode | |
| ).lower(), | |
| execution_sync_on_switch=bool( | |
| payload.get( | |
| "execution_sync_on_switch", defaults.execution_sync_on_switch | |
| ) | |
| ), | |
| execution_log_transitions=bool( | |
| payload.get( | |
| "execution_log_transitions", defaults.execution_log_transitions | |
| ) | |
| ), | |
| execution_log_state_hash=bool( | |
| payload.get( | |
| "execution_log_state_hash", defaults.execution_log_state_hash | |
| ) | |
| ), | |
| growth_enabled=bool(payload.get("growth_enabled", defaults.growth_enabled)), | |
| growth_neurogenesis=bool( | |
| payload.get("growth_neurogenesis", defaults.growth_neurogenesis) | |
| ), | |
| growth_synaptogenesis=bool( | |
| payload.get("growth_synaptogenesis", defaults.growth_synaptogenesis) | |
| ), | |
| growth_path_formation=bool( | |
| payload.get("growth_path_formation", defaults.growth_path_formation) | |
| ), | |
| growth_pruning=bool(payload.get("growth_pruning", defaults.growth_pruning)), | |
| growth_activity_threshold=number( | |
| "growth_activity_threshold", defaults.growth_activity_threshold | |
| ), | |
| growth_coactivation_threshold=integer( | |
| "growth_coactivation_threshold", defaults.growth_coactivation_threshold | |
| ), | |
| growth_information_threshold=number( | |
| "growth_information_threshold", defaults.growth_information_threshold | |
| ), | |
| growth_prune_threshold=number( | |
| "growth_prune_threshold", defaults.growth_prune_threshold | |
| ), | |
| growth_max_synapses_per_neuron=integer( | |
| "growth_max_synapses_per_neuron", | |
| defaults.growth_max_synapses_per_neuron, | |
| ), | |
| growth_max_new_synapses_per_barrier=integer( | |
| "growth_max_new_synapses_per_barrier", | |
| defaults.growth_max_new_synapses_per_barrier, | |
| ), | |
| cuda_budget_mb=integer("cuda_budget_mb", defaults.cuda_budget_mb), | |
| offload_enabled=bool( | |
| payload.get("offload_enabled", defaults.offload_enabled) | |
| ), | |
| offload_snapshot_interval=integer( | |
| "offload_snapshot_interval", defaults.offload_snapshot_interval | |
| ), | |
| hardware_profile_name=text( | |
| "hardware_profile_name", defaults.hardware_profile_name | |
| ), | |
| thalamic_gating_enabled=bool( | |
| payload.get("thalamic_gating_enabled", defaults.thalamic_gating_enabled) | |
| ), | |
| thalamic_relay_threshold=number( | |
| "thalamic_relay_threshold", defaults.thalamic_relay_threshold | |
| ), | |
| thalamic_attention_gain=number( | |
| "thalamic_attention_gain", defaults.thalamic_attention_gain | |
| ), | |
| thalamic_inhibition_gain=number( | |
| "thalamic_inhibition_gain", defaults.thalamic_inhibition_gain | |
| ), | |
| cortical_layers_enabled=bool( | |
| payload.get("cortical_layers_enabled", defaults.cortical_layers_enabled) | |
| ), | |
| cortical_layer_count=integer( | |
| "cortical_layer_count", defaults.cortical_layer_count | |
| ), | |
| cortical_plasticity=bool( | |
| payload.get("cortical_plasticity", defaults.cortical_plasticity) | |
| ), | |
| cortical_learning_rate=number( | |
| "cortical_learning_rate", defaults.cortical_learning_rate | |
| ), | |
| behavior_learning_enabled=bool( | |
| payload.get( | |
| "behavior_learning_enabled", defaults.behavior_learning_enabled | |
| ) | |
| ), | |
| behavior_action_count=integer( | |
| "behavior_action_count", defaults.behavior_action_count | |
| ), | |
| behavior_learning_rate=number( | |
| "behavior_learning_rate", defaults.behavior_learning_rate | |
| ), | |
| behavior_epsilon=number("behavior_epsilon", defaults.behavior_epsilon), | |
| behavior_target_action=integer( | |
| "behavior_target_action", defaults.behavior_target_action | |
| ), | |
| behavior_target_mode=text( | |
| "behavior_target_mode", defaults.behavior_target_mode | |
| ).lower(), | |
| behavior_min_activity=number( | |
| "behavior_min_activity", defaults.behavior_min_activity | |
| ), | |
| behavior_episode_ticks=integer( | |
| "behavior_episode_ticks", defaults.behavior_episode_ticks | |
| ), | |
| behavior_bias_current=number( | |
| "behavior_bias_current", defaults.behavior_bias_current | |
| ), | |
| geometry_lambda_a=number("geometry_lambda_a", defaults.geometry_lambda_a), | |
| geometry_lambda_b=number("geometry_lambda_b", defaults.geometry_lambda_b), | |
| geometry_sigma=number("geometry_sigma", defaults.geometry_sigma), | |
| geometry_p0=number("geometry_p0", defaults.geometry_p0), | |
| geometry_mode=text("geometry_mode", defaults.geometry_mode), | |
| geometry_delay_velocity=number( | |
| "geometry_delay_velocity", defaults.geometry_delay_velocity | |
| ), | |
| neural_io_enabled=bool( | |
| payload.get("neural_io_enabled", defaults.neural_io_enabled) | |
| ), | |
| neural_io_input_channels=integer( | |
| "neural_io_input_channels", defaults.neural_io_input_channels | |
| ), | |
| neural_io_output_channels=integer( | |
| "neural_io_output_channels", defaults.neural_io_output_channels | |
| ), | |
| neural_io_input_codec=text( | |
| "neural_io_input_codec", defaults.neural_io_input_codec | |
| ), | |
| neural_io_output_decoder=text( | |
| "neural_io_output_decoder", defaults.neural_io_output_decoder | |
| ), | |
| neural_io_input_payload=payload.get( | |
| "neural_io_input_payload", defaults.neural_io_input_payload | |
| ), | |
| neural_io_window_ticks=integer( | |
| "neural_io_window_ticks", defaults.neural_io_window_ticks | |
| ), | |
| neural_io_input_current=number( | |
| "neural_io_input_current", defaults.neural_io_input_current | |
| ), | |
| neural_io_input_role=text( | |
| "neural_io_input_role", defaults.neural_io_input_role | |
| ), | |
| neural_io_output_role=text( | |
| "neural_io_output_role", defaults.neural_io_output_role | |
| ), | |
| neural_io_phase=text("neural_io_phase", defaults.neural_io_phase), | |
| neural_io_correlation_id=text( | |
| "neural_io_correlation_id", | |
| defaults.neural_io_correlation_id or "auto", | |
| ), | |
| neural_io_modality=text("neural_io_modality", defaults.neural_io_modality), | |
| neural_io_source_id=text( | |
| "neural_io_source_id", defaults.neural_io_source_id | |
| ), | |
| closed_loop_preset=requested_preset, | |
| input_topology=text("input_topology", defaults.input_topology).lower(), | |
| input_channels=integer("input_channels", defaults.input_channels), | |
| input_channel_map=integer_groups( | |
| "input_channel_map", defaults.input_channel_map | |
| ), | |
| input_amplitude_per_channel=numbers( | |
| "input_amplitude_per_channel", | |
| defaults.input_amplitude_per_channel, | |
| ), | |
| input_frequency_per_channel=numbers( | |
| "input_frequency_per_channel", | |
| defaults.input_frequency_per_channel, | |
| ), | |
| input_phase_per_channel=numbers( | |
| "input_phase_per_channel", | |
| defaults.input_phase_per_channel, | |
| ), | |
| input_noise_sigma=number("input_noise_sigma", defaults.input_noise_sigma), | |
| target_cue_channel=integer( | |
| "target_cue_channel", defaults.target_cue_channel | |
| ), | |
| reward_cue_channel=integer( | |
| "reward_cue_channel", defaults.reward_cue_channel | |
| ), | |
| action_feedback_channel=integer( | |
| "action_feedback_channel", defaults.action_feedback_channel | |
| ), | |
| posture_score_channel=integer( | |
| "posture_score_channel", defaults.posture_score_channel | |
| ), | |
| reward_event_channel=integer( | |
| "reward_event_channel", defaults.reward_event_channel | |
| ), | |
| posture_current_scale=number( | |
| "posture_current_scale", defaults.posture_current_scale | |
| ), | |
| reward_event_scale=number( | |
| "reward_event_scale", defaults.reward_event_scale | |
| ), | |
| posture_reward_enabled=bool( | |
| payload.get("posture_reward_enabled", defaults.posture_reward_enabled) | |
| ), | |
| posture_weight_upright=number( | |
| "posture_weight_upright", defaults.posture_weight_upright | |
| ), | |
| posture_weight_height=number( | |
| "posture_weight_height", defaults.posture_weight_height | |
| ), | |
| posture_weight_stability=number( | |
| "posture_weight_stability", defaults.posture_weight_stability | |
| ), | |
| posture_weight_symmetry=number( | |
| "posture_weight_symmetry", defaults.posture_weight_symmetry | |
| ), | |
| posture_target_height=number( | |
| "posture_target_height", defaults.posture_target_height | |
| ), | |
| posture_tilt_max=number("posture_tilt_max", defaults.posture_tilt_max), | |
| posture_velocity_max=number( | |
| "posture_velocity_max", defaults.posture_velocity_max | |
| ), | |
| trigger_good_score=number( | |
| "trigger_good_score", defaults.trigger_good_score | |
| ), | |
| trigger_good_duration=integer( | |
| "trigger_good_duration", defaults.trigger_good_duration | |
| ), | |
| trigger_good_reward=number( | |
| "trigger_good_reward", defaults.trigger_good_reward | |
| ), | |
| trigger_warning_score=number( | |
| "trigger_warning_score", defaults.trigger_warning_score | |
| ), | |
| trigger_warning_reward=number( | |
| "trigger_warning_reward", defaults.trigger_warning_reward | |
| ), | |
| trigger_falling_rate=number( | |
| "trigger_falling_rate", defaults.trigger_falling_rate | |
| ), | |
| trigger_falling_reward=number( | |
| "trigger_falling_reward", defaults.trigger_falling_reward | |
| ), | |
| trigger_collapse_score=number( | |
| "trigger_collapse_score", defaults.trigger_collapse_score | |
| ), | |
| trigger_collapse_reward=number( | |
| "trigger_collapse_reward", defaults.trigger_collapse_reward | |
| ), | |
| trigger_recovery_bonus=number( | |
| "trigger_recovery_bonus", defaults.trigger_recovery_bonus | |
| ), | |
| reward_continuous_alpha=number( | |
| "reward_continuous_alpha", defaults.reward_continuous_alpha | |
| ), | |
| episode_termination_enabled=bool( | |
| payload.get( | |
| "episode_termination_enabled", defaults.episode_termination_enabled | |
| ) | |
| ), | |
| episode_max_ticks=integer("episode_max_ticks", defaults.episode_max_ticks), | |
| episode_reset_on_collapse=bool( | |
| payload.get( | |
| "episode_reset_on_collapse", defaults.episode_reset_on_collapse | |
| ) | |
| ), | |
| pan_feedback_delay=integer( | |
| "pan_feedback_delay", defaults.pan_feedback_delay | |
| ), | |
| pan_feedback_source=text( | |
| "pan_feedback_source", defaults.pan_feedback_source | |
| ).lower(), | |
| pan_feedback_target=text( | |
| "pan_feedback_target", defaults.pan_feedback_target | |
| ).lower(), | |
| pan_feedback_nonlinearity=text( | |
| "pan_feedback_nonlinearity", defaults.pan_feedback_nonlinearity | |
| ).lower(), | |
| pan_feedback_threshold=number( | |
| "pan_feedback_threshold", defaults.pan_feedback_threshold | |
| ), | |
| pan_feedback_saturation=number( | |
| "pan_feedback_saturation", defaults.pan_feedback_saturation | |
| ), | |
| action_loop_enabled=bool( | |
| payload.get("action_loop_enabled", defaults.action_loop_enabled) | |
| ), | |
| action_loop_delay=integer("action_loop_delay", defaults.action_loop_delay), | |
| action_persistence=integer( | |
| "action_persistence", defaults.action_persistence | |
| ), | |
| action_to_input_map=action_map( | |
| "action_to_input_map", defaults.action_to_input_map | |
| ), | |
| action_space_size=integer("action_space_size", defaults.action_space_size), | |
| action_coupling_strength=number( | |
| "action_coupling_strength", defaults.action_coupling_strength | |
| ), | |
| action_noise=number("action_noise", defaults.action_noise), | |
| freeze_actions=bool(payload.get("freeze_actions", defaults.freeze_actions)), | |
| frozen_action_sequence=integers( | |
| "frozen_action_sequence", defaults.frozen_action_sequence | |
| ), | |
| reward_signal_enabled=bool( | |
| payload.get( | |
| "reward_signal_enabled", | |
| defaults.reward_signal_enabled, | |
| ) | |
| ), | |
| reward_magnitude=number("reward_magnitude", defaults.reward_magnitude), | |
| reward_delay_ticks=integer( | |
| "reward_delay_ticks", defaults.reward_delay_ticks | |
| ), | |
| reward_shaping=text("reward_shaping", defaults.reward_shaping).lower(), | |
| reward_baseline=number("reward_baseline", defaults.reward_baseline), | |
| reward_decay=number("reward_decay", defaults.reward_decay), | |
| reward_channel=integer( | |
| "reward_channel", | |
| integer("reward_cue_channel", defaults.reward_channel), | |
| ), | |
| freeze_rewards=bool(payload.get("freeze_rewards", defaults.freeze_rewards)), | |
| frozen_reward_sequence=numbers( | |
| "frozen_reward_sequence", defaults.frozen_reward_sequence | |
| ), | |
| parity_reference_source=optional_text( | |
| "parity_reference_source", defaults.parity_reference_source | |
| ), | |
| parity_reference_commit=optional_text( | |
| "parity_reference_commit", defaults.parity_reference_commit | |
| ), | |
| target_cue_control=text( | |
| "target_cue_control", defaults.target_cue_control | |
| ).lower(), | |
| target_encoding=text("target_encoding", defaults.target_encoding).lower(), | |
| target_persistence=integer( | |
| "target_persistence", defaults.target_persistence | |
| ), | |
| target_cue_current=number( | |
| "target_cue_current", defaults.target_cue_current | |
| ), | |
| target_shuffle=bool(payload.get("target_shuffle", defaults.target_shuffle)), | |
| target_predictability=text( | |
| "target_predictability", defaults.target_predictability | |
| ).lower(), | |
| credit_window=integer("credit_window", defaults.credit_window), | |
| eligibility_trace_tau=number( | |
| "eligibility_trace_tau", defaults.eligibility_trace_tau | |
| ), | |
| credit_assignment=text( | |
| "credit_assignment", defaults.credit_assignment | |
| ).lower(), | |
| td_lambda=number("td_lambda", defaults.td_lambda), | |
| gamma_discount=number("gamma_discount", defaults.gamma_discount), | |
| neuron_threshold_variance=number( | |
| "neuron_threshold_variance", | |
| defaults.neuron_threshold_variance, | |
| ), | |
| neuron_tau_m_variance=number( | |
| "neuron_tau_m_variance", defaults.neuron_tau_m_variance | |
| ), | |
| inhibitory_fraction=number( | |
| "inhibitory_fraction", defaults.inhibitory_fraction | |
| ), | |
| gaba_strength=number("gaba_strength", defaults.gaba_strength), | |
| e_i_ratio=number("e_i_ratio", defaults.e_i_ratio), | |
| delay_distribution=text( | |
| "delay_distribution", defaults.delay_distribution | |
| ).lower(), | |
| delay_mean_ticks=number("delay_mean_ticks", defaults.delay_mean_ticks), | |
| refractory_variance=number( | |
| "refractory_variance", defaults.refractory_variance | |
| ), | |
| adaptation_strength=number( | |
| "adaptation_strength", defaults.adaptation_strength | |
| ), | |
| adaptation_tau=number("adaptation_tau", defaults.adaptation_tau), | |
| oscillation_enabled=bool( | |
| payload.get( | |
| "oscillation_enabled", | |
| defaults.oscillation_enabled, | |
| ) | |
| ), | |
| oscillation_frequency=number( | |
| "oscillation_frequency", defaults.oscillation_frequency | |
| ), | |
| geometry_input_coupling=bool( | |
| payload.get( | |
| "geometry_input_coupling", | |
| defaults.geometry_input_coupling, | |
| ) | |
| ), | |
| geometry_input_sigma=number( | |
| "geometry_input_sigma", defaults.geometry_input_sigma | |
| ), | |
| sandbox_enabled=bool( | |
| payload.get("sandbox_enabled", defaults.sandbox_enabled) | |
| ), | |
| sandbox_physics=text("sandbox_physics", defaults.sandbox_physics).lower(), | |
| sandbox_action_coupling=text( | |
| "sandbox_action_coupling", | |
| defaults.sandbox_action_coupling, | |
| ).lower(), | |
| sandbox_sensor_noise=number( | |
| "sandbox_sensor_noise", defaults.sandbox_sensor_noise | |
| ), | |
| ) | |
| config.validate() | |
| return config | |
| def validate(self) -> None: | |
| if not 2 <= self.n_neurons <= 1024: | |
| raise ValueError("n_neurons must be between 2 and 1024") | |
| max_edges = min(20_000, self.n_neurons * (self.n_neurons - 1)) | |
| if not self.n_neurons <= self.edge_budget <= max_edges: | |
| raise ValueError(f"edge_budget must be between n_neurons and {max_edges}") | |
| if not 1 <= self.ticks <= 2048: | |
| raise ValueError("ticks must be between 1 and 2048") | |
| if not 1 <= self.dimensions <= 32: | |
| raise ValueError("dimensions must be between 1 and 32") | |
| if not 0.05 <= self.dt_ms <= 5.0: | |
| raise ValueError("dt_ms must be between 0.05 and 5.0") | |
| if not 0.0 <= self.weight <= 100.0: | |
| raise ValueError("weight must be between 0 and 100") | |
| if not 0.0 <= self.weight_decay <= 1.0: | |
| raise ValueError("weight_decay must be between 0 and 1") | |
| if not 0.0 < self.weight_max_clamp <= 100.0: | |
| raise ValueError("weight_max_clamp must be > 0 and <= 100") | |
| if not 1 <= self.delay_ticks <= 64: | |
| raise ValueError("delay_ticks must be between 1 and 64") | |
| if not 0.0 <= self.stimulus_current <= 500.0: | |
| raise ValueError("stimulus_current must be between 0 and 500") | |
| if not 0.0 <= self.stimulus_rate_hz <= 1000.0: | |
| raise ValueError("stimulus_rate_hz must be between 0 and 1000") | |
| if not 0.001 <= self.radius <= 2.0: | |
| raise ValueError("radius must be between 0.001 and 2.0") | |
| if not 1 <= self.k_neighbors <= min(64, self.n_neurons - 1): | |
| raise ValueError("k_neighbors outside allowed range") | |
| if not 0.0 <= self.rewiring_probability <= 1.0: | |
| raise ValueError("rewiring_probability must be between 0 and 1") | |
| if not 1 <= self.modules <= min(32, self.n_neurons): | |
| raise ValueError("modules outside allowed range") | |
| if not 1 <= self.ensemble_runs <= 8: | |
| raise ValueError("ensemble_runs must be between 1 and 8") | |
| if not 5 <= self.pan_dimensions <= 32: | |
| raise ValueError("pan_dimensions must be between 5 and 32") | |
| if not 0.0 <= self.pan_feedback_gain <= 5.0: | |
| raise ValueError("pan_feedback_gain must be between 0 and 5") | |
| if not 0.0 <= self.pan_health_decay <= 1.0: | |
| raise ValueError("pan_health_decay must be between 0 and 1") | |
| if not 0.0 <= self.pan_apoptosis_threshold <= 1.0: | |
| raise ValueError("pan_apoptosis_threshold must be between 0 and 1") | |
| if not 0.0 <= self.pan_aging_threshold <= 1.0: | |
| raise ValueError("pan_aging_threshold must be between 0 and 1") | |
| if not 0.0 <= self.pan_bias_current <= 500.0: | |
| raise ValueError("pan_bias_current must be between 0 and 500") | |
| if self.pan_aging_threshold < self.pan_apoptosis_threshold: | |
| raise ValueError("pan_aging_threshold must be >= pan_apoptosis_threshold") | |
| if self.clock_mode not in {"continuous", "dual"}: | |
| raise ValueError("clock_mode must be continuous or dual") | |
| if not 1.0 <= self.clock_base_hz <= 10_000.0: | |
| raise ValueError("clock_base_hz must be between 1 and 10000") | |
| if not self.dt_ms <= self.clock_event_batch_ms <= 1000.0: | |
| raise ValueError("clock_event_batch_ms must be between dt_ms and 1000") | |
| if self.neuron_backend == "cuda_pan" and self.neuron_model != "pan_adex_5d": | |
| raise ValueError("cuda_pan requires pan_adex_5d") | |
| if self.neuron_backend not in {"cpu", "cuda_membrane", "cuda_pan"}: | |
| raise ValueError("neuron_backend must be cpu, cuda_membrane or cuda_pan") | |
| if self.neuron_backend != "cpu" and self.neuron_model not in { | |
| "lif", | |
| "adex", | |
| "pan_adex_5d", | |
| }: | |
| raise ValueError("CUDA membrane backend supports LIF/AdEx/PAN-AdEx only") | |
| if self.execution_mode not in {"EVENT_ONLY", "TICK_ONLY", "HYBRID_AUTO"}: | |
| raise ValueError("unsupported execution_mode") | |
| if self.execution_initial_mode not in {"EVENT_ONLY", "TICK_ONLY"}: | |
| raise ValueError("execution_initial_mode must be EVENT_ONLY or TICK_ONLY") | |
| if not 0.0 <= self.execution_theta_low <= self.execution_theta_high <= 1.0: | |
| raise ValueError("execution thresholds must satisfy 0 <= low <= high <= 1") | |
| if not 0.0 <= self.execution_hysteresis <= 0.5: | |
| raise ValueError("execution_hysteresis must be between 0 and 0.5") | |
| if not 0 <= self.execution_min_dwell <= 1_000_000: | |
| raise ValueError("execution_min_dwell must be between 0 and 1000000") | |
| if not 1 <= self.execution_activity_window <= 1_000_000: | |
| raise ValueError("execution_activity_window must be between 1 and 1000000") | |
| if self.execution_transition_mode not in {"clean", "fast", "debug"}: | |
| raise ValueError("unsupported execution_transition_mode") | |
| if not 0.0 <= self.growth_activity_threshold <= 1.0: | |
| raise ValueError("growth_activity_threshold must be between 0 and 1") | |
| if not 1 <= self.growth_coactivation_threshold <= 1000: | |
| raise ValueError("growth_coactivation_threshold must be between 1 and 1000") | |
| if not 0.0 <= self.growth_information_threshold <= 1.0: | |
| raise ValueError("growth_information_threshold must be between 0 and 1") | |
| if not 0.0 <= self.growth_prune_threshold <= 100.0: | |
| raise ValueError("growth_prune_threshold must be between 0 and 100") | |
| if not 1 <= self.growth_max_synapses_per_neuron <= 512: | |
| raise ValueError("growth_max_synapses_per_neuron must be between 1 and 512") | |
| if not 1 <= self.growth_max_new_synapses_per_barrier <= 256: | |
| raise ValueError( | |
| "growth_max_new_synapses_per_barrier must be between 1 and 256" | |
| ) | |
| if not 128 <= self.cuda_budget_mb <= 16_384: | |
| raise ValueError("cuda_budget_mb must be between 128 and 16384") | |
| if not 1 <= self.offload_snapshot_interval <= 1_000_000: | |
| raise ValueError("offload_snapshot_interval must be between 1 and 1000000") | |
| if self.growth_enabled and self.clock_mode != "dual": | |
| raise ValueError("growth_enabled requires clock_mode=dual") | |
| if self.hardware_profile_name not in { | |
| "reference_cpu", | |
| "cuda_8gb_balanced_plan", | |
| }: | |
| raise ValueError("unsupported hardware_profile_name") | |
| if not 0.0 <= self.thalamic_relay_threshold <= 1.0: | |
| raise ValueError("thalamic_relay_threshold must be between 0 and 1") | |
| if not 0.0 <= self.thalamic_attention_gain <= 4.0: | |
| raise ValueError("thalamic_attention_gain must be between 0 and 4") | |
| if not 0.0 <= self.thalamic_inhibition_gain <= 1.0: | |
| raise ValueError("thalamic_inhibition_gain must be between 0 and 1") | |
| if not 2 <= self.cortical_layer_count <= 12: | |
| raise ValueError("cortical_layer_count must be between 2 and 12") | |
| if not 0.0 <= self.cortical_learning_rate <= 1.0: | |
| raise ValueError("cortical_learning_rate must be between 0 and 1") | |
| if not 1 <= self.behavior_action_count <= 16: | |
| raise ValueError("behavior_action_count must be between 1 and 16") | |
| if not 0.0 < self.behavior_learning_rate <= 1.0: | |
| raise ValueError("behavior_learning_rate must be > 0 and <= 1") | |
| if not 0.0 <= self.behavior_epsilon <= 1.0: | |
| raise ValueError("behavior_epsilon must be between 0 and 1") | |
| if not 0 <= self.behavior_target_action < self.behavior_action_count: | |
| raise ValueError("behavior_target_action outside action range") | |
| if self.behavior_target_mode not in {"fixed", "cycle"}: | |
| raise ValueError("behavior_target_mode must be fixed or cycle") | |
| if not 0.0 <= self.behavior_min_activity <= 1.0: | |
| raise ValueError("behavior_min_activity must be between 0 and 1") | |
| if not 1 <= self.behavior_episode_ticks <= 1_000_000: | |
| raise ValueError("behavior_episode_ticks must be between 1 and 1000000") | |
| if not 0.0 <= self.behavior_bias_current <= 100.0: | |
| raise ValueError("behavior_bias_current must be between 0 and 100") | |
| if not 0.0 <= self.geometry_lambda_a <= 10.0: | |
| raise ValueError("geometry_lambda_a must be between 0 and 10") | |
| if not 0.0 <= self.geometry_lambda_b <= 10.0: | |
| raise ValueError("geometry_lambda_b must be between 0 and 10") | |
| if not 0.001 <= self.geometry_sigma <= 2.0: | |
| raise ValueError("geometry_sigma must be between 0.001 and 2") | |
| if not 0.0 <= self.geometry_p0 <= 1.0: | |
| raise ValueError("geometry_p0 must be between 0 and 1") | |
| if self.geometry_mode not in {"mixed_additive", "shortcut_union"}: | |
| raise ValueError("geometry_mode must be mixed_additive or shortcut_union") | |
| if not 0.001 <= self.geometry_delay_velocity <= 10.0: | |
| raise ValueError("geometry_delay_velocity must be between 0.001 and 10") | |
| if ( | |
| self.closed_loop_preset != "custom" | |
| and self.closed_loop_preset not in CLOSED_LOOP_PRESETS | |
| ): | |
| raise ValueError("unsupported closed_loop_preset") | |
| if self.input_topology not in { | |
| "uniform", | |
| "channel_partitioned", | |
| "spatial_gradient", | |
| "random_per_neuron", | |
| }: | |
| raise ValueError("unsupported input_topology") | |
| if not 1 <= self.input_channels <= 64: | |
| raise ValueError("input_channels must be between 1 and 64") | |
| if len(self.input_channel_map) > self.input_channels: | |
| raise ValueError("input_channel_map has more groups than input_channels") | |
| for group in self.input_channel_map: | |
| if any(index < 0 or index >= self.n_neurons for index in group): | |
| raise ValueError("input_channel_map contains an invalid neuron index") | |
| for name, values, low, high in ( | |
| ( | |
| "input_amplitude_per_channel", | |
| self.input_amplitude_per_channel, | |
| 0.0, | |
| 500.0, | |
| ), | |
| ( | |
| "input_frequency_per_channel", | |
| self.input_frequency_per_channel, | |
| 1.0, | |
| 200.0, | |
| ), | |
| ( | |
| "input_phase_per_channel", | |
| self.input_phase_per_channel, | |
| 0.0, | |
| 2.0 * math.pi, | |
| ), | |
| ): | |
| if len(values) > self.input_channels: | |
| raise ValueError(f"{name} has more values than input_channels") | |
| if any(value < low or value > high for value in values): | |
| raise ValueError(f"{name} values outside allowed range") | |
| if not 0.0 <= self.input_noise_sigma <= 1.0: | |
| raise ValueError("input_noise_sigma must be between 0 and 1") | |
| for name, channel in ( | |
| ("target_cue_channel", self.target_cue_channel), | |
| ("reward_cue_channel", self.reward_cue_channel), | |
| ("reward_channel", self.reward_channel), | |
| ("action_feedback_channel", self.action_feedback_channel), | |
| ("posture_score_channel", self.posture_score_channel), | |
| ("reward_event_channel", self.reward_event_channel), | |
| ): | |
| if not 0 <= channel < self.input_channels: | |
| raise ValueError(f"{name} outside input channel range") | |
| if not 0 <= self.pan_feedback_delay <= 64: | |
| raise ValueError("pan_feedback_delay must be between 0 and 64") | |
| if self.pan_feedback_source not in { | |
| "population", | |
| "layer", | |
| "subset", | |
| "hypervector", | |
| }: | |
| raise ValueError("unsupported pan_feedback_source") | |
| if self.pan_feedback_target not in {"all", "layer", "random_subset"}: | |
| raise ValueError("unsupported pan_feedback_target") | |
| if self.pan_feedback_nonlinearity not in {"linear", "tanh", "sign", "clip"}: | |
| raise ValueError("unsupported pan_feedback_nonlinearity") | |
| if not 0.0 <= self.pan_feedback_threshold <= 1.0: | |
| raise ValueError("pan_feedback_threshold must be between 0 and 1") | |
| if not 0.0 <= self.pan_feedback_saturation <= 100.0: | |
| raise ValueError("pan_feedback_saturation must be between 0 and 100") | |
| if not 1 <= self.action_loop_delay <= 64: | |
| raise ValueError("action_loop_delay must be between 1 and 64") | |
| if not 1 <= self.action_persistence <= 128: | |
| raise ValueError("action_persistence must be between 1 and 128") | |
| if not 1 <= self.action_space_size <= 32: | |
| raise ValueError("action_space_size must be between 1 and 32") | |
| if ( | |
| self.behavior_target_mode == "fixed" | |
| and self.behavior_target_action >= self.action_space_size | |
| ): | |
| raise ValueError("behavior_target_action outside closed-loop action range") | |
| if not 0.0 <= self.action_coupling_strength <= 10.0: | |
| raise ValueError("action_coupling_strength must be between 0 and 10") | |
| if not 0.0 <= self.action_noise <= 1.0: | |
| raise ValueError("action_noise must be between 0 and 1") | |
| required_episodes = self.ticks // self.behavior_episode_ticks | |
| if self.freeze_actions: | |
| if len(self.frozen_action_sequence) < required_episodes: | |
| raise ValueError( | |
| "frozen_action_sequence must cover every completed episode" | |
| ) | |
| if any( | |
| action < 0 or action >= self.action_space_size | |
| for action in self.frozen_action_sequence | |
| ): | |
| raise ValueError("frozen_action_sequence contains invalid action") | |
| if self.freeze_rewards: | |
| if not self.reward_signal_enabled: | |
| raise ValueError("freeze_rewards requires reward_signal_enabled") | |
| if len(self.frozen_reward_sequence) < required_episodes: | |
| raise ValueError( | |
| "frozen_reward_sequence must cover every completed episode" | |
| ) | |
| if any( | |
| not math.isfinite(reward) or abs(reward) > 100.0 | |
| for reward in self.frozen_reward_sequence | |
| ): | |
| raise ValueError( | |
| "frozen_reward_sequence values must be finite and <= 100 abs" | |
| ) | |
| if self.freeze_actions or self.freeze_rewards: | |
| if self.parity_reference_source != "CPU_PYTHON_PLAYGROUND": | |
| raise ValueError( | |
| "parity_reference_source must be CPU_PYTHON_PLAYGROUND" | |
| ) | |
| if re.fullmatch(r"[0-9a-fA-F]{7,40}", self.parity_reference_commit) is None: | |
| raise ValueError( | |
| "parity_reference_commit must be a 7-40 character git SHA" | |
| ) | |
| if not isinstance(self.action_to_input_map, str): | |
| if len(self.action_to_input_map) > self.action_space_size: | |
| raise ValueError("action_to_input_map has more rows than actions") | |
| for row in self.action_to_input_map: | |
| if len(row) > self.input_channels: | |
| raise ValueError("action_to_input_map row exceeds input_channels") | |
| if not 0.0 <= self.reward_magnitude <= 10.0: | |
| raise ValueError("reward_magnitude must be between 0 and 10") | |
| if not 0 <= self.reward_delay_ticks <= 64: | |
| raise ValueError("reward_delay_ticks must be between 0 and 64") | |
| if self.reward_shaping not in {"sparse", "dense", "potential_based"}: | |
| raise ValueError("unsupported reward_shaping") | |
| if not -1.0 <= self.reward_baseline <= 1.0: | |
| raise ValueError("reward_baseline must be between -1 and 1") | |
| if not 0.0 <= self.reward_decay <= 1.0: | |
| raise ValueError("reward_decay must be between 0 and 1") | |
| if self.target_cue_control not in {"aligned", "randomized", "absent"}: | |
| raise ValueError("unsupported target_cue_control") | |
| if self.target_encoding not in { | |
| "none", | |
| "one_hot", | |
| "rate", | |
| "population_latency", | |
| }: | |
| raise ValueError("unsupported target_encoding") | |
| if not 1 <= self.target_persistence <= 256: | |
| raise ValueError("target_persistence must be between 1 and 256") | |
| if not 0.0 <= self.target_cue_current <= 500.0: | |
| raise ValueError("target_cue_current must be between 0 and 500") | |
| if self.target_predictability not in { | |
| "deterministic", | |
| "stochastic", | |
| "adversarial", | |
| }: | |
| raise ValueError("unsupported target_predictability") | |
| if not 1 <= self.credit_window <= 512: | |
| raise ValueError("credit_window must be between 1 and 512") | |
| if not 1.0 <= self.eligibility_trace_tau <= 1000.0: | |
| raise ValueError("eligibility_trace_tau must be between 1 and 1000 ms") | |
| if self.credit_assignment not in {"none", "trace", "reward_modulated_stdp"}: | |
| raise ValueError("unsupported credit_assignment") | |
| if not 0.0 <= self.td_lambda <= 1.0: | |
| raise ValueError("td_lambda must be between 0 and 1") | |
| if not 0.0 <= self.gamma_discount <= 1.0: | |
| raise ValueError("gamma_discount must be between 0 and 1") | |
| for name, value in ( | |
| ("neuron_threshold_variance", self.neuron_threshold_variance), | |
| ("neuron_tau_m_variance", self.neuron_tau_m_variance), | |
| ("inhibitory_fraction", self.inhibitory_fraction), | |
| ("refractory_variance", self.refractory_variance), | |
| ): | |
| if not 0.0 <= value <= 1.0: | |
| raise ValueError(f"{name} must be between 0 and 1") | |
| if not 0.0 <= self.gaba_strength <= 10.0: | |
| raise ValueError("gaba_strength must be between 0 and 10") | |
| if not 0.0 <= self.e_i_ratio <= 10.0: | |
| raise ValueError("e_i_ratio must be between 0 and 10") | |
| if self.delay_distribution not in {"fixed", "uniform", "lognormal", "gamma"}: | |
| raise ValueError("unsupported delay_distribution") | |
| if not 1.0 <= self.delay_mean_ticks <= 32.0: | |
| raise ValueError("delay_mean_ticks must be between 1 and 32") | |
| if not 0.0 <= self.adaptation_strength <= 10.0: | |
| raise ValueError("adaptation_strength must be between 0 and 10") | |
| if not 10.0 <= self.adaptation_tau <= 1000.0: | |
| raise ValueError("adaptation_tau must be between 10 and 1000 ms") | |
| if not 0.5 <= self.oscillation_frequency <= 100.0: | |
| raise ValueError("oscillation_frequency must be between 0.5 and 100 Hz") | |
| if not 0.001 <= self.geometry_input_sigma <= 2.0: | |
| raise ValueError("geometry_input_sigma must be between 0.001 and 2") | |
| if not 0.0 <= self.sandbox_sensor_noise <= 1.0: | |
| raise ValueError("sandbox_sensor_noise must be between 0 and 1") | |
| if self.sandbox_physics != "stick_figure": | |
| raise ValueError("sandbox_physics must be stick_figure") | |
| if self.sandbox_action_coupling != "direct": | |
| raise ValueError("sandbox_action_coupling must be direct") | |
| if self.neural_io_enabled: | |
| if not 1 <= self.neural_io_input_channels <= 256: | |
| raise ValueError("neural_io_input_channels must be between 1 and 256") | |
| if not 1 <= self.neural_io_output_channels <= 256: | |
| raise ValueError("neural_io_output_channels must be between 1 and 256") | |
| if ( | |
| self.neural_io_input_channels + self.neural_io_output_channels | |
| > self.n_neurons | |
| ): | |
| raise ValueError("neural I/O populations must not overlap") | |
| if not 1 <= self.neural_io_window_ticks <= self.ticks: | |
| raise ValueError( | |
| "neural_io_window_ticks must be between 1 and session ticks" | |
| ) | |
| if not 0.0 <= self.neural_io_input_current <= 500.0: | |
| raise ValueError("neural_io_input_current must be between 0 and 500") | |
| if self.neural_io_input_role not in { | |
| "AFFERENT", | |
| "GATEWAY_AFFERENT", | |
| }: | |
| raise ValueError( | |
| "neural_io_input_role must be AFFERENT or GATEWAY_AFFERENT" | |
| ) | |
| if self.neural_io_output_role not in { | |
| "EFFERENT", | |
| "GATEWAY_EFFERENT", | |
| }: | |
| raise ValueError( | |
| "neural_io_output_role must be EFFERENT or GATEWAY_EFFERENT" | |
| ) | |
| if self.neural_io_phase not in { | |
| "IDLE", | |
| "QUERY", | |
| "WAIT", | |
| "RESPONSE", | |
| "TIMEOUT", | |
| }: | |
| raise ValueError("unsupported neural_io_phase") | |
| def to_dict(self) -> dict[str, object]: | |
| return { | |
| "name": self.name, | |
| "neuron_model": self.neuron_model, | |
| "synapse_model": self.synapse_model, | |
| "plasticity_rule": self.plasticity_rule, | |
| "topology": self.topology, | |
| "stimulus": self.stimulus, | |
| "readout": self.readout, | |
| "n_neurons": self.n_neurons, | |
| "edge_budget": self.edge_budget, | |
| "ticks": self.ticks, | |
| "seed": self.seed, | |
| "dimensions": self.dimensions, | |
| "dt_ms": self.dt_ms, | |
| "weight": self.weight, | |
| "weight_decay": self.weight_decay, | |
| "weight_max_clamp": self.weight_max_clamp, | |
| "delay_ticks": self.delay_ticks, | |
| "stimulus_current": self.stimulus_current, | |
| "stimulus_rate_hz": self.stimulus_rate_hz, | |
| "radius": self.radius, | |
| "k_neighbors": self.k_neighbors, | |
| "rewiring_probability": self.rewiring_probability, | |
| "modules": self.modules, | |
| "ensemble_runs": self.ensemble_runs, | |
| "persist": self.persist, | |
| "pan_enabled": self.pan_enabled, | |
| "pan_dimensions": self.pan_dimensions, | |
| "pan_closed_loop": self.pan_closed_loop, | |
| "pan_feedback_gain": self.pan_feedback_gain, | |
| "pan_health_decay": self.pan_health_decay, | |
| "pan_apoptosis_threshold": self.pan_apoptosis_threshold, | |
| "pan_aging_threshold": self.pan_aging_threshold, | |
| "pan_bias_current": self.pan_bias_current, | |
| "clock_mode": self.clock_mode, | |
| "clock_base_hz": self.clock_base_hz, | |
| "clock_event_batch_ms": self.clock_event_batch_ms, | |
| "neuron_backend": self.neuron_backend, | |
| "execution_mode": self.execution_mode, | |
| "execution_initial_mode": self.execution_initial_mode, | |
| "execution_theta_high": self.execution_theta_high, | |
| "execution_theta_low": self.execution_theta_low, | |
| "execution_hysteresis": self.execution_hysteresis, | |
| "execution_min_dwell": self.execution_min_dwell, | |
| "execution_activity_window": self.execution_activity_window, | |
| "execution_transition_mode": self.execution_transition_mode, | |
| "execution_sync_on_switch": self.execution_sync_on_switch, | |
| "execution_log_transitions": self.execution_log_transitions, | |
| "execution_log_state_hash": self.execution_log_state_hash, | |
| "growth_enabled": self.growth_enabled, | |
| "growth_neurogenesis": self.growth_neurogenesis, | |
| "growth_synaptogenesis": self.growth_synaptogenesis, | |
| "growth_path_formation": self.growth_path_formation, | |
| "growth_pruning": self.growth_pruning, | |
| "growth_activity_threshold": self.growth_activity_threshold, | |
| "growth_coactivation_threshold": self.growth_coactivation_threshold, | |
| "growth_information_threshold": self.growth_information_threshold, | |
| "growth_prune_threshold": self.growth_prune_threshold, | |
| "growth_max_synapses_per_neuron": self.growth_max_synapses_per_neuron, | |
| "growth_max_new_synapses_per_barrier": ( | |
| self.growth_max_new_synapses_per_barrier | |
| ), | |
| "cuda_budget_mb": self.cuda_budget_mb, | |
| "offload_enabled": self.offload_enabled, | |
| "offload_snapshot_interval": self.offload_snapshot_interval, | |
| "hardware_profile_name": self.hardware_profile_name, | |
| "thalamic_gating_enabled": self.thalamic_gating_enabled, | |
| "thalamic_relay_threshold": self.thalamic_relay_threshold, | |
| "thalamic_attention_gain": self.thalamic_attention_gain, | |
| "thalamic_inhibition_gain": self.thalamic_inhibition_gain, | |
| "cortical_layers_enabled": self.cortical_layers_enabled, | |
| "cortical_layer_count": self.cortical_layer_count, | |
| "cortical_plasticity": self.cortical_plasticity, | |
| "cortical_learning_rate": self.cortical_learning_rate, | |
| "behavior_learning_enabled": self.behavior_learning_enabled, | |
| "behavior_action_count": self.behavior_action_count, | |
| "behavior_learning_rate": self.behavior_learning_rate, | |
| "behavior_epsilon": self.behavior_epsilon, | |
| "behavior_target_action": self.behavior_target_action, | |
| "behavior_target_mode": self.behavior_target_mode, | |
| "behavior_min_activity": self.behavior_min_activity, | |
| "behavior_episode_ticks": self.behavior_episode_ticks, | |
| "behavior_bias_current": self.behavior_bias_current, | |
| "geometry_lambda_a": self.geometry_lambda_a, | |
| "geometry_lambda_b": self.geometry_lambda_b, | |
| "geometry_sigma": self.geometry_sigma, | |
| "geometry_p0": self.geometry_p0, | |
| "geometry_mode": self.geometry_mode, | |
| "geometry_delay_velocity": self.geometry_delay_velocity, | |
| "neural_io_enabled": self.neural_io_enabled, | |
| "neural_io_input_channels": self.neural_io_input_channels, | |
| "neural_io_output_channels": self.neural_io_output_channels, | |
| "neural_io_input_codec": self.neural_io_input_codec, | |
| "neural_io_output_decoder": self.neural_io_output_decoder, | |
| "neural_io_input_payload_present": ( | |
| self.neural_io_input_payload is not None | |
| ), | |
| "neural_io_window_ticks": self.neural_io_window_ticks, | |
| "neural_io_input_current": self.neural_io_input_current, | |
| "neural_io_input_role": self.neural_io_input_role, | |
| "neural_io_output_role": self.neural_io_output_role, | |
| "neural_io_phase": self.neural_io_phase, | |
| "neural_io_correlation_id": self.neural_io_correlation_id, | |
| "neural_io_modality": self.neural_io_modality, | |
| "neural_io_source_id": self.neural_io_source_id, | |
| "closed_loop_preset": self.closed_loop_preset, | |
| "input_topology": self.input_topology, | |
| "input_channels": self.input_channels, | |
| "input_channel_map": [list(group) for group in self.input_channel_map], | |
| "input_amplitude_per_channel": list(self.input_amplitude_per_channel), | |
| "input_frequency_per_channel": list(self.input_frequency_per_channel), | |
| "input_phase_per_channel": list(self.input_phase_per_channel), | |
| "input_noise_sigma": self.input_noise_sigma, | |
| "target_cue_channel": self.target_cue_channel, | |
| "reward_cue_channel": self.reward_cue_channel, | |
| "action_feedback_channel": self.action_feedback_channel, | |
| "posture_score_channel": self.posture_score_channel, | |
| "reward_event_channel": self.reward_event_channel, | |
| "posture_current_scale": self.posture_current_scale, | |
| "reward_event_scale": self.reward_event_scale, | |
| "posture_reward_enabled": self.posture_reward_enabled, | |
| "posture_weight_upright": self.posture_weight_upright, | |
| "posture_weight_height": self.posture_weight_height, | |
| "posture_weight_stability": self.posture_weight_stability, | |
| "posture_weight_symmetry": self.posture_weight_symmetry, | |
| "posture_target_height": self.posture_target_height, | |
| "posture_tilt_max": self.posture_tilt_max, | |
| "posture_velocity_max": self.posture_velocity_max, | |
| "trigger_good_score": self.trigger_good_score, | |
| "trigger_good_duration": self.trigger_good_duration, | |
| "trigger_good_reward": self.trigger_good_reward, | |
| "trigger_warning_score": self.trigger_warning_score, | |
| "trigger_warning_reward": self.trigger_warning_reward, | |
| "trigger_falling_rate": self.trigger_falling_rate, | |
| "trigger_falling_reward": self.trigger_falling_reward, | |
| "trigger_collapse_score": self.trigger_collapse_score, | |
| "trigger_collapse_reward": self.trigger_collapse_reward, | |
| "trigger_recovery_bonus": self.trigger_recovery_bonus, | |
| "reward_continuous_alpha": self.reward_continuous_alpha, | |
| "episode_termination_enabled": self.episode_termination_enabled, | |
| "episode_max_ticks": self.episode_max_ticks, | |
| "episode_reset_on_collapse": self.episode_reset_on_collapse, | |
| "pan_feedback_delay": self.pan_feedback_delay, | |
| "pan_feedback_source": self.pan_feedback_source, | |
| "pan_feedback_target": self.pan_feedback_target, | |
| "pan_feedback_nonlinearity": self.pan_feedback_nonlinearity, | |
| "pan_feedback_threshold": self.pan_feedback_threshold, | |
| "pan_feedback_saturation": self.pan_feedback_saturation, | |
| "action_loop_enabled": self.action_loop_enabled, | |
| "action_loop_delay": self.action_loop_delay, | |
| "action_persistence": self.action_persistence, | |
| "action_to_input_map": ( | |
| self.action_to_input_map | |
| if isinstance(self.action_to_input_map, str) | |
| else [list(row) for row in self.action_to_input_map] | |
| ), | |
| "action_space_size": self.action_space_size, | |
| "action_coupling_strength": self.action_coupling_strength, | |
| "action_noise": self.action_noise, | |
| "freeze_actions": self.freeze_actions, | |
| "frozen_action_sequence": list(self.frozen_action_sequence), | |
| "reward_signal_enabled": self.reward_signal_enabled, | |
| "reward_magnitude": self.reward_magnitude, | |
| "reward_delay_ticks": self.reward_delay_ticks, | |
| "reward_shaping": self.reward_shaping, | |
| "reward_baseline": self.reward_baseline, | |
| "reward_decay": self.reward_decay, | |
| "reward_channel": self.reward_channel, | |
| "freeze_rewards": self.freeze_rewards, | |
| "frozen_reward_sequence": list(self.frozen_reward_sequence), | |
| "parity_reference_source": self.parity_reference_source, | |
| "parity_reference_commit": self.parity_reference_commit, | |
| "target_cue_control": self.target_cue_control, | |
| "target_encoding": self.target_encoding, | |
| "target_persistence": self.target_persistence, | |
| "target_cue_current": self.target_cue_current, | |
| "target_shuffle": self.target_shuffle, | |
| "target_predictability": self.target_predictability, | |
| "credit_window": self.credit_window, | |
| "eligibility_trace_tau": self.eligibility_trace_tau, | |
| "credit_assignment": self.credit_assignment, | |
| "td_lambda": self.td_lambda, | |
| "gamma_discount": self.gamma_discount, | |
| "neuron_threshold_variance": self.neuron_threshold_variance, | |
| "neuron_tau_m_variance": self.neuron_tau_m_variance, | |
| "inhibitory_fraction": self.inhibitory_fraction, | |
| "gaba_strength": self.gaba_strength, | |
| "e_i_ratio": self.e_i_ratio, | |
| "delay_distribution": self.delay_distribution, | |
| "delay_mean_ticks": self.delay_mean_ticks, | |
| "refractory_variance": self.refractory_variance, | |
| "adaptation_strength": self.adaptation_strength, | |
| "adaptation_tau": self.adaptation_tau, | |
| "oscillation_enabled": self.oscillation_enabled, | |
| "oscillation_frequency": self.oscillation_frequency, | |
| "geometry_input_coupling": self.geometry_input_coupling, | |
| "geometry_input_sigma": self.geometry_input_sigma, | |
| "sandbox_enabled": self.sandbox_enabled, | |
| "sandbox_physics": self.sandbox_physics, | |
| "sandbox_action_coupling": self.sandbox_action_coupling, | |
| "sandbox_sensor_noise": self.sandbox_sensor_noise, | |
| } | |
| def to_runtime_dict(self) -> dict[str, object]: | |
| """Return transient config including the exact I/O payload. | |
| This mapping is for in-process re-execution only and must never be | |
| persisted or returned as a session result. | |
| """ | |
| payload = self.to_dict() | |
| payload["neural_io_input_payload"] = self.neural_io_input_payload | |
| return payload | |