SPDX-License-Identifier: AGPL-3.0-or-later¶
Commercial license available¶
© Concepts 1996–2026 Miroslav Šotek. All rights reserved.¶
© Code 2020–2026 Miroslav Šotek. All rights reserved.¶
ORCID: 0009-0009-3560-0851¶
Contact: www.anulum.li | protoscience@anulum.li¶
SCPN Phase Orchestrator — Autotune reward API reference¶
Autotune Reward Evaluation¶
The reward evaluator is the first safe slice of the reinforcement-learning autotune track. It scores candidate knob policies from replay or simulation metrics without applying control actions directly.
The default reward is coherence improvement minus target deficit, low-coherence risk, actuation energy, unsafe rollout flags, regime churn, positive Lyapunov growth, negative STL robustness, and explicit safety cost. The output is an audit-ready record that can be used by later PPO/SAC or hybrid physics-RL learners.
from scpn_phase_orchestrator.autotune import (
KnobPolicyCandidate,
RewardObservation,
evaluate_knob_policy,
)
candidate = KnobPolicyCandidate(K=0.2, alpha=0.0, zeta=0.05, Psi=0.1)
observation = RewardObservation(
coherence=0.82,
previous_coherence=0.74,
lyapunov_exponent=-0.015,
stl_robustness=0.08,
safety_cost=0.01,
)
report = evaluate_knob_policy(candidate, observation)
assert report.to_audit_record()["reward"] == report.reward
Replay-trained or simulation-trained searches can rank multiple candidates without applying any control action:
from scpn_phase_orchestrator.autotune import rank_replay_candidates
ranked = rank_replay_candidates(
(
(KnobPolicyCandidate(K=0.15), RewardObservation(coherence=0.72)),
(KnobPolicyCandidate(K=0.30), RewardObservation(coherence=0.65)),
),
top_k=1,
)
best_report = ranked[0].to_audit_record()
Offline search can generate deterministic coordinate candidates around a seed policy before replay scoring. The candidate surface includes the universal knobs, per-channel weights, and cross-channel coupling gains; the generator is still side-effect free and only emits replay candidates.
from scpn_phase_orchestrator.autotune import (
OfflinePolicySearchConfig,
generate_offline_policy_candidates,
)
candidates = generate_offline_policy_candidates(
KnobPolicyCandidate(
K=0.2,
zeta=0.05,
channel_weights=(1.0, 0.8),
cross_channel_gains=(0.3, 0.5),
),
OfflinePolicySearchConfig(
K_step=0.05,
zeta_step=0.02,
channel_weight_step=0.1,
cross_channel_gain_step=0.1,
max_abs_knob=1.0,
),
)
Proposal records apply simple acceptance gates and remain review artefacts:
from scpn_phase_orchestrator.autotune import (
PolicyProposalConfig,
SafetyConstraintConfig,
propose_replay_policy,
)
proposal = propose_replay_policy(
(
(
candidate,
RewardObservation(
coherence=0.82,
lyapunov_exponent=-0.015,
stl_robustness=0.08,
safety_cost=0.01,
),
),
),
proposal_config=PolicyProposalConfig(
min_coherence=0.75,
safety_constraints=SafetyConstraintConfig(
max_lyapunov_exponent=0.0,
min_stl_robustness=0.0,
max_safety_cost=0.05,
require_lyapunov=True,
require_stl=True,
require_safety_cost=True,
),
),
)
audit_record = proposal.to_audit_record()
The safety-constraint gate is intentionally conservative. If a proposal config requires Lyapunov or STL evidence and a replay observation omits it, the candidate is rejected even when its coherence reward is high. This keeps safe-RL integration reviewable: the learner may optimise, but the acceptance record must still carry explicit stability and temporal-logic evidence.
For the higher-level replay-only search wrapper that generates candidates, evaluates them through a caller-supplied replay adapter, and returns a proposal record, see Autotune Replay Policy Search.
Operational overview¶
This module is the scoring seam between raw simulation outcomes and policy action. Rewards are structured to preserve risk awareness while still allowing optimization experiments.
When used in a staged autonomy lane, teams typically:
- score candidates with replay evidence first,
- enforce explicit safety gates,
- only then promote a candidate into a proposal record.
That order is important because it separates numeric optimisation from safety admissibility. A candidate can look strong on raw coherence and still fail safe policy constraints.
Safe RL readiness¶
The shape and fields in evaluate_knob_policy are aligned with later RL loop
integration:
- Observations carry stability, coherence, and safety fields together.
- Proposal records contain audit-ready traces and gating decisions.
- Rejection reasons are represented in the proposal output, not only in external logs.
This makes the reward module usable as the first hard requirement layer for PPO/SAC or other search learners without changing governance rules later.
Why this function is separated from control execution¶
evaluate_knob_policy is intentionally scoped to scoring and evidence generation.
It computes reward and audit fields so a learner can rank candidates, but it does not
decide control actions by itself.
When integrating with RL stacks, keep the same sequence:
- replay or simulation produces observations,
- reward scoring evaluates candidates with coherence, stability, and safety terms,
- proposal gating enforces the hard safety thresholds,
- deployment code consumes only accepted proposals that preserve evidence boundaries.
This keeps model-driven optimisation inside a constrained review envelope rather than directly connecting policy gradients to hardware or external actuators.
reward ¶
Auditable reward scoring for candidate autotune policies.
Classes¶
KnobPolicyCandidate
dataclass
¶
KnobPolicyCandidate(
K: float | FloatArray = 0.0,
alpha: float | FloatArray = 0.0,
zeta: float | FloatArray = 0.0,
Psi: float | FloatArray = 0.0,
channel_weights: tuple[float, ...] = (),
cross_channel_gains: tuple[float, ...] = (),
)
Candidate phase-control knobs proposed by autotune tooling.
RewardObservation
dataclass
¶
RewardObservation(
coherence: float,
previous_coherence: float | None = None,
unsafe: bool = False,
regime_changed: bool = False,
lyapunov_exponent: float | None = None,
stl_robustness: float | None = None,
safety_cost: float = 0.0,
)
Observed rollout metrics used to score one policy candidate.
Methods:¶
__post_init__ ¶
Validate observation probabilities, flags, and optional safety evidence.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
OfflinePolicySearchConfig
dataclass
¶
OfflinePolicySearchConfig(
K_step: float = 0.05,
alpha_step: float = 0.05,
zeta_step: float = 0.05,
Psi_step: float = 0.05,
channel_weight_step: float = 0.05,
cross_channel_gain_step: float = 0.05,
include_baseline: bool = True,
max_abs_knob: float | None = None,
)
Deterministic candidate-generation settings for replay searches.
Methods:¶
__post_init__ ¶
Validate coordinate-search step sizes and clipping bounds.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
RewardConfig
dataclass
¶
RewardConfig(
target_coherence: float = 1.0,
bad_coherence_threshold: float = 0.35,
coherence_weight: float = 1.0,
bad_coherence_penalty: float = 2.0,
actuation_penalty: float = 0.01,
churn_penalty: float = 0.1,
unsafe_penalty: float = 10.0,
lyapunov_penalty: float = 1.0,
stl_penalty: float = 1.0,
safety_cost_penalty: float = 1.0,
component_order: tuple[str, ...] = (
"coherence_gain",
"target_tracking",
"bad_coherence",
"actuation",
"regime_churn",
"unsafe",
"lyapunov_stability",
"stl_robustness",
"safety_cost",
),
)
Weights for auditable coherence-minus-risk autotune reward.
Methods:¶
__post_init__ ¶
Validate reward weights and component-order policy.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
SafetyConstraintConfig
dataclass
¶
SafetyConstraintConfig(
max_lyapunov_exponent: float | None = None,
min_stl_robustness: float | None = None,
max_safety_cost: float | None = None,
require_lyapunov: bool = False,
require_stl: bool = False,
require_safety_cost: bool = False,
)
Lyapunov/STL gates for review-only safe-RL proposals.
Methods:¶
__post_init__ ¶
Validate safety evidence bounds and require-evidence flags.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
to_audit_record ¶
Return a JSON-serialisable safety-gate configuration.
Returns¶
dict[str, object] A JSON-serialisable safety-gate configuration.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
PolicyProposalConfig
dataclass
¶
PolicyProposalConfig(
min_reward: float = -np.inf,
min_coherence: float = 0.0,
max_alternatives: int = 3,
require_safe: bool = True,
safety_constraints: SafetyConstraintConfig = SafetyConstraintConfig(),
)
Acceptance gates for replay-trained policy proposals.
Methods:¶
__post_init__ ¶
Validate policy proposal gates and embedded safety constraints.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
AutotuneRewardReport
dataclass
¶
AutotuneRewardReport(
reward: float,
components: dict[str, float],
candidate: KnobPolicyCandidate,
observation: RewardObservation,
config: RewardConfig,
)
Reward result suitable for policy search and audit logs.
Methods:¶
to_audit_record ¶
Return a serialisable reward record.
Returns¶
dict[str, object] A serialisable reward record.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
AutotunePolicyProposal
dataclass
¶
AutotunePolicyProposal(
accepted: bool,
selected: AutotuneRewardReport | None,
alternatives: tuple[AutotuneRewardReport, ...],
reasons: tuple[str, ...],
config: PolicyProposalConfig,
)
Replay-trained policy proposal record for human or CI review.
Methods:¶
to_audit_record ¶
Return a serialisable policy proposal record.
Returns¶
dict[str, object] A serialisable policy proposal record.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
Functions:¶
evaluate_knob_policy ¶
evaluate_knob_policy(
candidate: KnobPolicyCandidate,
observation: RewardObservation,
config: RewardConfig | None = None,
) -> AutotuneRewardReport
Score a candidate policy from coherence and safety metrics.
The reward is intentionally model-free and side-effect free. Training systems can use it to rank replay candidates before any future policy learner is allowed to propose production control actions.
Parameters¶
candidate : KnobPolicyCandidate The candidate configuration. observation : RewardObservation The observation record. config : RewardConfig | None The configuration object.
Returns¶
AutotuneRewardReport A candidate policy from coherence and safety metrics.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 | |
rank_replay_candidates ¶
rank_replay_candidates(
replay_candidates: Sequence[
tuple[KnobPolicyCandidate, RewardObservation]
],
config: RewardConfig | None = None,
*,
top_k: int | None = None,
require_safe: bool = True,
) -> tuple[AutotuneRewardReport, ...]
Rank replay-evaluated policy candidates by reward.
This helper is the non-actuating bridge between reward scoring and future policy learners. It consumes replay or simulation observations, filters unsafe rollouts by default, and returns audit-ready reports sorted from highest to lowest reward.
Parameters¶
replay_candidates : Sequence[tuple[KnobPolicyCandidate, RewardObservation]] Replay candidate proposals. config : RewardConfig | None The configuration object. top_k : int | None Number of top items to retain. require_safe : bool Whether to require publication-safe output.
Returns¶
tuple[AutotuneRewardReport, ...] Rank replay-evaluated policy candidates by reward.
Raises¶
ValueError If the inputs are invalid or inconsistent. TypeError If an argument has the wrong type.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
propose_replay_policy ¶
propose_replay_policy(
replay_candidates: Sequence[
tuple[KnobPolicyCandidate, RewardObservation]
],
reward_config: RewardConfig | None = None,
proposal_config: PolicyProposalConfig | None = None,
) -> AutotunePolicyProposal
Build a reviewable policy proposal from replay-ranked candidates.
The proposal is an audit artefact only. A caller still has to pass it through domain-specific policy review before any candidate is deployed.
Parameters¶
replay_candidates : Sequence[tuple[KnobPolicyCandidate, RewardObservation]] Replay candidate proposals. reward_config : RewardConfig | None The reward configuration. proposal_config : PolicyProposalConfig | None The proposal configuration.
Returns¶
AutotunePolicyProposal A reviewable policy proposal from replay-ranked candidates.
Source code in src/scpn_phase_orchestrator/autotune/reward.py
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 | |
generate_offline_policy_candidates ¶
generate_offline_policy_candidates(
seed: KnobPolicyCandidate,
config: OfflinePolicySearchConfig | None = None,
) -> tuple[KnobPolicyCandidate, ...]
Generate deterministic replay-search candidates around a seed policy.
The generator performs a bounded coordinate search over the universal knobs, channel weights, and cross-channel coupling gains. It does not inspect plant state and it does not apply actions; callers must evaluate the returned candidates through replay or simulation before ranking them.
Parameters¶
seed : KnobPolicyCandidate Seed for the deterministic RNG. config : OfflinePolicySearchConfig | None The configuration object.
Returns¶
tuple[KnobPolicyCandidate, ...] Deterministic replay-search candidates around a seed policy.