agentevolve-optimizer 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_evolve/__init__.py +722 -0
- agent_evolve/agentic.py +2800 -0
- agent_evolve/api.py +767 -0
- agent_evolve/application/__init__.py +1580 -0
- agent_evolve/application/action_allocation.py +744 -0
- agent_evolve/application/action_allocation_frame.py +347 -0
- agent_evolve/application/action_allocation_frame_commit.py +185 -0
- agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
- agent_evolve/application/action_allocation_frame_v3.py +338 -0
- agent_evolve/application/action_archive_value.py +497 -0
- agent_evolve/application/action_evidence_consistency.py +455 -0
- agent_evolve/application/action_forecast_partitioning.py +1471 -0
- agent_evolve/application/action_metric_projection.py +211 -0
- agent_evolve/application/action_role_value.py +680 -0
- agent_evolve/application/action_score_authorities.py +363 -0
- agent_evolve/application/action_structural_signature.py +116 -0
- agent_evolve/application/action_target_realization.py +402 -0
- agent_evolve/application/agentic_evolution.py +7734 -0
- agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
- agent_evolve/application/anchor_residual_identification.py +463 -0
- agent_evolve/application/archive_conditioned_action_target.py +208 -0
- agent_evolve/application/artifact_journal.py +246 -0
- agent_evolve/application/artifact_replay.py +347 -0
- agent_evolve/application/budgeted_optimizer.py +1828 -0
- agent_evolve/application/calibrated_campaign.py +485 -0
- agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
- agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
- agent_evolve/application/campaign_capacity_recourse.py +254 -0
- agent_evolve/application/campaign_contextual_outcomes.py +119 -0
- agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
- agent_evolve/application/campaign_evidence_registry.py +262 -0
- agent_evolve/application/campaign_execution.py +2537 -0
- agent_evolve/application/campaign_generation_audit.py +942 -0
- agent_evolve/application/campaign_learning.py +1812 -0
- agent_evolve/application/campaign_learning_runtime.py +1977 -0
- agent_evolve/application/campaign_search_phase.py +227 -0
- agent_evolve/application/campaign_selector_context_extension.py +220 -0
- agent_evolve/application/campaign_variation_envelope.py +649 -0
- agent_evolve/application/campaign_variation_trace.py +451 -0
- agent_evolve/application/candidate_archive_consequence.py +128 -0
- agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
- agent_evolve/application/composite_outcome_updater.py +145 -0
- agent_evolve/application/composition_portfolio_selection.py +363 -0
- agent_evolve/application/concurrent_stage.py +144 -0
- agent_evolve/application/contextual_action_allocation.py +181 -0
- agent_evolve/application/contextual_campaign_outcomes.py +267 -0
- agent_evolve/application/contextual_campaign_planning.py +1366 -0
- agent_evolve/application/contextual_delayed_credit.py +651 -0
- agent_evolve/application/contextual_search_controller.py +2374 -0
- agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
- agent_evolve/application/decision_metric_projection.py +112 -0
- agent_evolve/application/derived_action_semantics.py +129 -0
- agent_evolve/application/detailed_evaluation.py +449 -0
- agent_evolve/application/earned_lineage.py +1011 -0
- agent_evolve/application/effective_choice_audit.py +484 -0
- agent_evolve/application/empirical_consequence_calibration.py +908 -0
- agent_evolve/application/evaluation_accounting.py +325 -0
- agent_evolve/application/evaluation_cache.py +199 -0
- agent_evolve/application/evaluation_escrow.py +547 -0
- agent_evolve/application/evaluation_recourse.py +253 -0
- agent_evolve/application/event_recorder.py +151 -0
- agent_evolve/application/evolution_campaign.py +1840 -0
- agent_evolve/application/executable_hypothesis.py +323 -0
- agent_evolve/application/factorial_branch_pilot.py +772 -0
- agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
- agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
- agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
- agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
- agent_evolve/application/finite_action_selection.py +188 -0
- agent_evolve/application/finite_action_set.py +306 -0
- agent_evolve/application/finite_action_transition.py +537 -0
- agent_evolve/application/finite_variation_eligibility.py +296 -0
- agent_evolve/application/forecast_geometry_portfolio.py +799 -0
- agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
- agent_evolve/application/front_proximity_admission.py +311 -0
- agent_evolve/application/front_proximity_parent_basis.py +458 -0
- agent_evolve/application/frozen_hurdle_score.py +659 -0
- agent_evolve/application/g3_causal_screen.py +2257 -0
- agent_evolve/application/g3_causal_validation.py +1046 -0
- agent_evolve/application/g3_postseal_curation.py +818 -0
- agent_evolve/application/gated_agentic_generator.py +205 -0
- agent_evolve/application/generation_feedback.py +293 -0
- agent_evolve/application/generative_proposal_journal.py +185 -0
- agent_evolve/application/geometry_conditional_elasticity.py +453 -0
- agent_evolve/application/global_wave_action_allocation.py +1151 -0
- agent_evolve/application/head_mass_conditional_seat.py +268 -0
- agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
- agent_evolve/application/identifiable_reflection_learning.py +395 -0
- agent_evolve/application/identifiable_reflection_request.py +364 -0
- agent_evolve/application/in_memory_residual_archive.py +341 -0
- agent_evolve/application/insight_memory.py +1804 -0
- agent_evolve/application/live_runtime_manifest.py +758 -0
- agent_evolve/application/llm_task_queue.py +769 -0
- agent_evolve/application/matched_finite_action_block.py +409 -0
- agent_evolve/application/materialized_action_broker.py +2328 -0
- agent_evolve/application/materialized_action_constraints.py +83 -0
- agent_evolve/application/materialized_variation.py +211 -0
- agent_evolve/application/multi_option_evolution.py +1536 -0
- agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
- agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
- agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
- agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
- agent_evolve/application/outcome_relation.py +193 -0
- agent_evolve/application/paired_allocation_comparison.py +241 -0
- agent_evolve/application/paired_block_schedule.py +127 -0
- agent_evolve/application/parent_measurement.py +226 -0
- agent_evolve/application/pareto_archive.py +811 -0
- agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
- agent_evolve/application/portfolio_evolution.py +2950 -0
- agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
- agent_evolve/application/portfolio_memory_attribution.py +581 -0
- agent_evolve/application/portfolio_memory_dose.py +788 -0
- agent_evolve/application/portfolio_memory_matched_control.py +938 -0
- agent_evolve/application/portfolio_memory_transfer.py +297 -0
- agent_evolve/application/portfolio_optimization_memory.py +363 -0
- agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
- agent_evolve/application/portfolio_projection.py +335 -0
- agent_evolve/application/portfolio_recombination.py +2032 -0
- agent_evolve/application/post_evolution_reflection.py +834 -0
- agent_evolve/application/postcommit_rank_authority.py +245 -0
- agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
- agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
- agent_evolve/application/prequential_residual_exploration.py +343 -0
- agent_evolve/application/prequential_score_portfolio.py +954 -0
- agent_evolve/application/projections.py +292 -0
- agent_evolve/application/protected_action_committee.py +1027 -0
- agent_evolve/application/protected_branch_pilot.py +376 -0
- agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
- agent_evolve/application/provider_replay.py +910 -0
- agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
- agent_evolve/application/recombination_residual_expert.py +403 -0
- agent_evolve/application/reflection_workflow.py +571 -0
- agent_evolve/application/region_conditional_credit.py +911 -0
- agent_evolve/application/residual_campaign_runtime.py +531 -0
- agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
- agent_evolve/application/residual_headroom_ledger.py +1544 -0
- agent_evolve/application/residual_learning_transaction.py +396 -0
- agent_evolve/application/residual_portfolio_evolution.py +1228 -0
- agent_evolve/application/residual_reachability.py +749 -0
- agent_evolve/application/residual_stage_credit.py +499 -0
- agent_evolve/application/same_prefix_paired_audit.py +1580 -0
- agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
- agent_evolve/application/sequential_lineage_allocation.py +1017 -0
- agent_evolve/application/sequential_market_replay.py +1395 -0
- agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
- agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
- agent_evolve/application/single_score_action_allocation.py +299 -0
- agent_evolve/application/source_exposure_allocation.py +906 -0
- agent_evolve/application/staged_memory.py +210 -0
- agent_evolve/application/stratified_cold_start_allocation.py +732 -0
- agent_evolve/application/support_guarded_hurdle_score.py +549 -0
- agent_evolve/application/target_conditioned_action_forecast.py +595 -0
- agent_evolve/application/target_conditioned_campaign.py +566 -0
- agent_evolve/application/treatment_assignment.py +201 -0
- agent_evolve/application/trusted_objective_evidence.py +217 -0
- agent_evolve/application/two_stage_action_evolution.py +1131 -0
- agent_evolve/application/v8lite_allocation_policy.py +1083 -0
- agent_evolve/application/v9_candidate_policy.py +1303 -0
- agent_evolve/bootstrap.py +108 -0
- agent_evolve/campaign_presets.py +517 -0
- agent_evolve/campaign_profiles.py +452 -0
- agent_evolve/campaign_variation_topology.py +288 -0
- agent_evolve/campaign_workload.py +950 -0
- agent_evolve/cli.py +797 -0
- agent_evolve/contract.py +241 -0
- agent_evolve/core/__init__.py +91 -0
- agent_evolve/core/action_semantics.py +411 -0
- agent_evolve/core/authored.py +105 -0
- agent_evolve/core/formatting.py +286 -0
- agent_evolve/core/optimization_semantics.py +324 -0
- agent_evolve/core/problem.py +167 -0
- agent_evolve/core/results.py +323 -0
- agent_evolve/core/stats.py +70 -0
- agent_evolve/core/telemetry.py +100 -0
- agent_evolve/domain/__init__.py +89 -0
- agent_evolve/domain/artifact.py +162 -0
- agent_evolve/domain/durable_text.py +68 -0
- agent_evolve/domain/event.py +1454 -0
- agent_evolve/domain/finite_action_set.py +426 -0
- agent_evolve/domain/finite_variation.py +526 -0
- agent_evolve/domain/generative_emission.py +559 -0
- agent_evolve/domain/ids.py +163 -0
- agent_evolve/domain/inline_text.py +106 -0
- agent_evolve/domain/insight.py +27 -0
- agent_evolve/domain/lineage.py +737 -0
- agent_evolve/domain/llm_task_queue.py +960 -0
- agent_evolve/domain/outcome.py +96 -0
- agent_evolve/domain/patch.py +854 -0
- agent_evolve/domain/typed_json.py +542 -0
- agent_evolve/domain/variation_space.py +158 -0
- agent_evolve/driver.py +1014 -0
- agent_evolve/harness/__init__.py +29 -0
- agent_evolve/harness/base.py +242 -0
- agent_evolve/harness/directives.py +163 -0
- agent_evolve/harness/generative_seal.py +479 -0
- agent_evolve/harness/registry.py +41 -0
- agent_evolve/infrastructure/__init__.py +39 -0
- agent_evolve/infrastructure/artifacts/__init__.py +6 -0
- agent_evolve/infrastructure/artifacts/_verification.py +67 -0
- agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
- agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
- agent_evolve/infrastructure/asyncio_runtime.py +109 -0
- agent_evolve/infrastructure/authored_runtime.py +188 -0
- agent_evolve/infrastructure/authored_worker.py +171 -0
- agent_evolve/infrastructure/clock.py +53 -0
- agent_evolve/infrastructure/events/__init__.py +6 -0
- agent_evolve/infrastructure/events/_validation.py +89 -0
- agent_evolve/infrastructure/events/in_memory.py +56 -0
- agent_evolve/infrastructure/events/jsonl.py +193 -0
- agent_evolve/infrastructure/exception_provenance.py +215 -0
- agent_evolve/infrastructure/ids.py +118 -0
- agent_evolve/infrastructure/lineage_codec.py +1836 -0
- agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
- agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
- agent_evolve/infrastructure/resource_lease.py +370 -0
- agent_evolve/infrastructure/sanitization/__init__.py +8 -0
- agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
- agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
- agent_evolve/infrastructure/stream_liveness.py +383 -0
- agent_evolve/infrastructure/subprocess_boundary.py +136 -0
- agent_evolve/integrations/__init__.py +1 -0
- agent_evolve/integrations/botorch/__init__.py +28 -0
- agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
- agent_evolve/integrations/completion.py +242 -0
- agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
- agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
- agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
- agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
- agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
- agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
- agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
- agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
- agent_evolve/integrations/pydantic_ai/harness.py +159 -0
- agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
- agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
- agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
- agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
- agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
- agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
- agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
- agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
- agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
- agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
- agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
- agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
- agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
- agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
- agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
- agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
- agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
- agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
- agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
- agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
- agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
- agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
- agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
- agent_evolve/integrations/pymoo_adapter.py +242 -0
- agent_evolve/policies/__init__.py +17 -0
- agent_evolve/policies/check.py +469 -0
- agent_evolve/policies/emit_scaffold.py +451 -0
- agent_evolve/policies/feedback/__init__.py +37 -0
- agent_evolve/policies/feedback/held_out_asn.py +1325 -0
- agent_evolve/policies/genetic.py +607 -0
- agent_evolve/policies/llm_backoff.py +183 -0
- agent_evolve/policies/llm_chooser.py +226 -0
- agent_evolve/policies/llm_generator.py +1760 -0
- agent_evolve/policies/llm_init.py +267 -0
- agent_evolve/policies/llm_operator.py +109 -0
- agent_evolve/policies/llm_prior.py +194 -0
- agent_evolve/policies/llm_surrogate.py +334 -0
- agent_evolve/policies/measurement_evidence.py +704 -0
- agent_evolve/policies/memory/__init__.py +223 -0
- agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
- agent_evolve/policies/memory/compatibility_matching.py +593 -0
- agent_evolve/policies/memory/global_falsification.py +1841 -0
- agent_evolve/policies/memory/prompt_shape.py +503 -0
- agent_evolve/policies/memory/randomized_subset.py +714 -0
- agent_evolve/policies/memory/staged_causal.py +1270 -0
- agent_evolve/policies/memory/treatment_compliance.py +759 -0
- agent_evolve/policies/objective_resolution/__init__.py +17 -0
- agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
- agent_evolve/policies/operator_portfolio.py +407 -0
- agent_evolve/policies/reguidance.py +1133 -0
- agent_evolve/policies/reward/__init__.py +83 -0
- agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
- agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
- agent_evolve/policies/reward/affine_hypervolume.py +490 -0
- agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
- agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
- agent_evolve/policies/reward/frozen_archive.py +360 -0
- agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
- agent_evolve/policies/search_state.py +208 -0
- agent_evolve/policies/selection/__init__.py +345 -0
- agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
- agent_evolve/policies/selection/affine_frontier_context.py +330 -0
- agent_evolve/policies/selection/affine_frontier_target.py +473 -0
- agent_evolve/policies/selection/archive_elite.py +1346 -0
- agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
- agent_evolve/policies/selection/calibrated_slate.py +1394 -0
- agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
- agent_evolve/policies/selection/common_candidate_pool.py +685 -0
- agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
- agent_evolve/policies/selection/disjoint_pairs.py +479 -0
- agent_evolve/policies/selection/elite_explorer.py +719 -0
- agent_evolve/policies/selection/finite_action.py +187 -0
- agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
- agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
- agent_evolve/policies/selection/forecast_calibration.py +922 -0
- agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
- agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
- agent_evolve/policies/selection/full_support_slate.py +91 -0
- agent_evolve/policies/selection/meaningful_direction.py +240 -0
- agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
- agent_evolve/policies/selection/model_anchored_slate.py +826 -0
- agent_evolve/policies/selection/phenotype_recourse.py +979 -0
- agent_evolve/policies/selection/proposal_support.py +368 -0
- agent_evolve/policies/selection/random_portfolio.py +254 -0
- agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
- agent_evolve/policies/selection/residual_frontier.py +463 -0
- agent_evolve/policies/selection/residual_frontier_target.py +605 -0
- agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
- agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
- agent_evolve/policies/selection/target_conditioned_features.py +812 -0
- agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
- agent_evolve/policies/selection/task_keyed_palette.py +906 -0
- agent_evolve/policies/semantics.py +147 -0
- agent_evolve/policies/structure.py +362 -0
- agent_evolve/policies/structured_output_budget.py +62 -0
- agent_evolve/policies/surrogate.py +696 -0
- agent_evolve/policies/variation/__init__.py +1 -0
- agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
- agent_evolve/policies/variation/crossover_inheritance.py +575 -0
- agent_evolve/policies/variation/disjoint_recombination.py +611 -0
- agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
- agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
- agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
- agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
- agent_evolve/policies/variation/typed_patch.py +1981 -0
- agent_evolve/policies/weighted_prior.py +394 -0
- agent_evolve/ports/__init__.py +383 -0
- agent_evolve/ports/action_allocation.py +733 -0
- agent_evolve/ports/action_allocation_frame.py +1153 -0
- agent_evolve/ports/action_allocation_frame_commit.py +294 -0
- agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
- agent_evolve/ports/action_allocation_frame_v3.py +995 -0
- agent_evolve/ports/action_forecast.py +1568 -0
- agent_evolve/ports/action_metric_projection.py +165 -0
- agent_evolve/ports/agentic_generator.py +1561 -0
- agent_evolve/ports/archive_context.py +136 -0
- agent_evolve/ports/artifact_sanitizer.py +44 -0
- agent_evolve/ports/artifact_store.py +225 -0
- agent_evolve/ports/clock.py +13 -0
- agent_evolve/ports/contextual_search_allocation.py +827 -0
- agent_evolve/ports/decision_metric_projection.py +258 -0
- agent_evolve/ports/event_store.py +55 -0
- agent_evolve/ports/executable_hypothesis.py +557 -0
- agent_evolve/ports/finite_acquisition.py +377 -0
- agent_evolve/ports/finite_acquisition_batch.py +296 -0
- agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
- agent_evolve/ports/finite_acquisition_json.py +247 -0
- agent_evolve/ports/finite_acquisition_space.py +168 -0
- agent_evolve/ports/finite_action_selection.py +348 -0
- agent_evolve/ports/finite_action_set.py +256 -0
- agent_evolve/ports/frontier_target.py +396 -0
- agent_evolve/ports/generation_failure.py +43 -0
- agent_evolve/ports/hard_feasibility.py +233 -0
- agent_evolve/ports/id_factory.py +34 -0
- agent_evolve/ports/llm_task_queue.py +93 -0
- agent_evolve/ports/objective_resolution.py +419 -0
- agent_evolve/ports/paired_allocation_comparison.py +401 -0
- agent_evolve/ports/paired_block_schedule.py +475 -0
- agent_evolve/ports/parent_measurement.py +336 -0
- agent_evolve/ports/portfolio_memory_dose.py +643 -0
- agent_evolve/ports/portfolio_selection.py +3169 -0
- agent_evolve/ports/postcommit_rank_authority.py +467 -0
- agent_evolve/ports/presented_action_evidence.py +794 -0
- agent_evolve/ports/resource_lease.py +162 -0
- agent_evolve/ports/structured_generator.py +734 -0
- agent_evolve/ports/structured_output_budget.py +120 -0
- agent_evolve/ports/subprocess_boundary.py +138 -0
- agent_evolve/ports/treatment_assignment.py +466 -0
- agent_evolve/ports/variation_catalog.py +76 -0
- agent_evolve/ports/variation_source.py +226 -0
- agent_evolve/proposal_mode.py +157 -0
- agent_evolve/proposers/__init__.py +10 -0
- agent_evolve/proposers/random_proposer.py +188 -0
- agent_evolve/provider_accounting.py +163 -0
- agent_evolve/py.typed +0 -0
- agent_evolve/reference_method.py +1570 -0
- agent_evolve/session/__init__.py +11 -0
- agent_evolve/session/authorship.py +864 -0
- agent_evolve/session/evaluate.py +236 -0
- agent_evolve/session/fidelity.py +237 -0
- agent_evolve/session/genetic_loop.py +742 -0
- agent_evolve/session/loop.py +803 -0
- agent_evolve/session/screening.py +671 -0
- agent_evolve/settings.py +376 -0
- agent_evolve/workload_kit.py +368 -0
- agent_evolve/workload_prompt.py +398 -0
- agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
- agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
- agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
- agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
- agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
- agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1571 @@
|
|
|
1
|
+
"""Prior-only calibrated acquisition over a finite K8 slate.
|
|
2
|
+
|
|
3
|
+
The model proposes eight workload-grounded candidates. This provider-free
|
|
4
|
+
policy assigns four distinct evaluation roles using only facts sealed before
|
|
5
|
+
the current wave: calibrated forecast correctness, explicit abstention,
|
|
6
|
+
structural evidence, and feasibility constraints. Model-authored card
|
|
7
|
+
citations remain observable diagnostics but have no allocation authority.
|
|
8
|
+
|
|
9
|
+
The policy is intentionally workload-neutral and has no fitted benchmark
|
|
10
|
+
hyperparameters. In particular, an ``unknown`` direction is treated as
|
|
11
|
+
maximum epistemic uncertainty rather than as zero information. Epistemic
|
|
12
|
+
priority is multiplied by structural novelty/coverage so that uncertainty is
|
|
13
|
+
spent on candidates that can distinguish materially different hypotheses.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
import math
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from itertools import permutations
|
|
24
|
+
from typing import Any, ClassVar
|
|
25
|
+
|
|
26
|
+
from agent_evolve.domain.patch import require_sha256
|
|
27
|
+
from agent_evolve.policies.selection.calibrated_slate import (
|
|
28
|
+
CalibratedSlateMember,
|
|
29
|
+
MetricOptimizationGoal,
|
|
30
|
+
SlateAllocationRequest,
|
|
31
|
+
assess_allocated_slate_memory_dose,
|
|
32
|
+
)
|
|
33
|
+
from agent_evolve.policies.selection.forecast_calibration import (
|
|
34
|
+
ForecastCalibrationCell,
|
|
35
|
+
ForecastConfidenceBin,
|
|
36
|
+
)
|
|
37
|
+
from agent_evolve.policies.variation.compositional_finite_catalog import (
|
|
38
|
+
COMPOSITE_OPTION_FAMILY,
|
|
39
|
+
)
|
|
40
|
+
from agent_evolve.ports.agentic_generator import MetricEffectDirection
|
|
41
|
+
from agent_evolve.ports.portfolio_memory_dose import (
|
|
42
|
+
PortfolioMemoryDoseAssessment,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
POLICY_ID = "calibrated_frontier_four_role_slate"
|
|
47
|
+
POLICY_VERSION = 2
|
|
48
|
+
POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
49
|
+
b"agent-evolve:calibrated-frontier-four-role-slate:v2;"
|
|
50
|
+
b"slate-size=8;portfolio-size=4;outcome-blind=true;"
|
|
51
|
+
b"calibration-cutoff-exclusive=true;"
|
|
52
|
+
b"roles=calibrated-exploit,calibrated-frontier,epistemic-structural,"
|
|
53
|
+
b"structural-coverage;unknown-epistemic-score=1;"
|
|
54
|
+
b"expected-signed-utility=favorable:2p-1,adverse:1-2p;"
|
|
55
|
+
b"known-epistemic-score=mean(posterior-uncertainty,calibrated-disagreement);"
|
|
56
|
+
b"epistemic-structural-coupling=multiply;"
|
|
57
|
+
b"frontier-score=expected-utility+0.5*structural;"
|
|
58
|
+
b"joint-exact-role-assignment=true;diversity-weight=0.25;"
|
|
59
|
+
b"model-card-citations=diagnostic-only;hard-memory-dose=matched-diagnostic-only;"
|
|
60
|
+
b"compatibility-family-and-patch-feasibility=true;"
|
|
61
|
+
b"no-benchmark-specific-parameters=true"
|
|
62
|
+
).hexdigest()
|
|
63
|
+
|
|
64
|
+
OPERATOR_STRATIFIED_POLICY_ID = (
|
|
65
|
+
"operator_stratified_calibrated_frontier_four_role_slate"
|
|
66
|
+
)
|
|
67
|
+
OPERATOR_STRATIFIED_POLICY_VERSION = 1
|
|
68
|
+
OPERATOR_STRATIFIED_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
69
|
+
b"agent-evolve:operator-stratified-calibrated-frontier-four-role-slate:v1;"
|
|
70
|
+
b"base=calibrated-frontier-four-role-v2;"
|
|
71
|
+
b"required-family-minimums=authenticated-configuration;"
|
|
72
|
+
b"minimums-are-evaluation-assay-slots-not-quality-priors;"
|
|
73
|
+
b"joint-feasibility-and-role-optimization=true;outcome-blind=true;"
|
|
74
|
+
b"no-workload-specific-parameters=true"
|
|
75
|
+
).hexdigest()
|
|
76
|
+
|
|
77
|
+
HORIZON_BOUNDED_POLICY_ID = (
|
|
78
|
+
"horizon_bounded_calibrated_frontier_four_role_slate"
|
|
79
|
+
)
|
|
80
|
+
HORIZON_BOUNDED_POLICY_VERSION = 1
|
|
81
|
+
HORIZON_BOUNDED_POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
82
|
+
b"agent-evolve:horizon-bounded-calibrated-frontier-four-role-slate:v1;"
|
|
83
|
+
b"base=calibrated-frontier-four-role-v2;"
|
|
84
|
+
b"family-exposure-phases=authenticated-configuration;"
|
|
85
|
+
b"phase-index=sealed-wave-index;lower-and-upper-bounds=true;"
|
|
86
|
+
b"bounds-are-evaluation-assay-slots-not-quality-priors;"
|
|
87
|
+
b"infeasible-bounds=minimum-l1-structural-recourse;"
|
|
88
|
+
b"joint-feasibility-and-role-optimization=true;outcome-blind=true;"
|
|
89
|
+
b"arbitrary-action-family-identifiers=true;"
|
|
90
|
+
b"no-workload-specific-parameters=true"
|
|
91
|
+
).hexdigest()
|
|
92
|
+
|
|
93
|
+
_DECISION_DOMAIN = b"agent-evolve:structural-posterior-slate-decision:v1\x00"
|
|
94
|
+
_CONFIGURATION_DOMAIN = b"agent-evolve:calibrated-frontier-configuration:v2\x00"
|
|
95
|
+
_OPERATOR_STRATIFIED_CONFIGURATION_DOMAIN = (
|
|
96
|
+
b"agent-evolve:operator-stratified-frontier-configuration:v1\x00"
|
|
97
|
+
)
|
|
98
|
+
_OPERATOR_STRATIFIED_DECISION_DOMAIN = (
|
|
99
|
+
b"agent-evolve:operator-stratified-frontier-decision:v1\x00"
|
|
100
|
+
)
|
|
101
|
+
_HORIZON_BOUNDED_CONFIGURATION_DOMAIN = (
|
|
102
|
+
b"agent-evolve:horizon-bounded-frontier-configuration:v1\x00"
|
|
103
|
+
)
|
|
104
|
+
_HORIZON_BOUNDED_DECISION_DOMAIN = (
|
|
105
|
+
b"agent-evolve:horizon-bounded-frontier-decision:v1\x00"
|
|
106
|
+
)
|
|
107
|
+
_SLATE_SIZE = 8
|
|
108
|
+
_PORTFOLIO_SIZE = 4
|
|
109
|
+
_FRONTIER_STRUCTURAL_WEIGHT = 0.5
|
|
110
|
+
_DIVERSITY_WEIGHT = 0.25
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _configuration_record() -> dict[str, object]:
|
|
114
|
+
return {
|
|
115
|
+
"schema_version": 2,
|
|
116
|
+
"slate_size": _SLATE_SIZE,
|
|
117
|
+
"portfolio_size": _PORTFOLIO_SIZE,
|
|
118
|
+
"unknown_epistemic_score_hex": (1.0).hex(),
|
|
119
|
+
"epistemic_structural_coupling": "multiply",
|
|
120
|
+
"expected_signed_utility": {
|
|
121
|
+
"favorable": "2p_minus_1",
|
|
122
|
+
"adverse": "1_minus_2p",
|
|
123
|
+
},
|
|
124
|
+
"frontier_structural_weight_hex": _FRONTIER_STRUCTURAL_WEIGHT.hex(),
|
|
125
|
+
"diversity_weight_hex": _DIVERSITY_WEIGHT.hex(),
|
|
126
|
+
"model_card_citation_authority": "diagnostic_only",
|
|
127
|
+
"hard_memory_dose": "matched_diagnostic_only",
|
|
128
|
+
"benchmark_specific_parameters": [],
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _canonical_json(value: object) -> bytes:
|
|
133
|
+
return json.dumps(
|
|
134
|
+
value,
|
|
135
|
+
allow_nan=False,
|
|
136
|
+
ensure_ascii=True,
|
|
137
|
+
separators=(",", ":"),
|
|
138
|
+
sort_keys=True,
|
|
139
|
+
).encode("ascii", errors="strict")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
143
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _require_finite(value: float, *, name: str) -> None:
|
|
147
|
+
if type(value) is not float or not math.isfinite(value):
|
|
148
|
+
raise TypeError(f"{name} must be a finite canonical float")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _require_unit_interval(value: float, *, name: str) -> None:
|
|
152
|
+
_require_finite(value, name=name)
|
|
153
|
+
if not 0.0 <= value <= 1.0:
|
|
154
|
+
raise ValueError(f"{name} must lie in [0, 1]")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _require_canonical_strings(values: tuple[str, ...], *, name: str) -> None:
|
|
158
|
+
if type(values) is not tuple or any(
|
|
159
|
+
type(value) is not str or not value for value in values
|
|
160
|
+
):
|
|
161
|
+
raise TypeError(f"{name} must be an exact tuple of non-empty strings")
|
|
162
|
+
if values != tuple(sorted(set(values))):
|
|
163
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _validate_required_family_minimums(
|
|
167
|
+
values: tuple[tuple[str, int], ...],
|
|
168
|
+
) -> None:
|
|
169
|
+
if type(values) is not tuple or not values:
|
|
170
|
+
raise ValueError("required_family_minimums must be a non-empty exact tuple")
|
|
171
|
+
if values != tuple(sorted(values)):
|
|
172
|
+
raise ValueError("required_family_minimums must be canonical")
|
|
173
|
+
families: list[str] = []
|
|
174
|
+
total = 0
|
|
175
|
+
for family, minimum in values:
|
|
176
|
+
if type(family) is not str or not family:
|
|
177
|
+
raise ValueError("required family must be a non-empty string")
|
|
178
|
+
if type(minimum) is not int or not 1 <= minimum <= _PORTFOLIO_SIZE:
|
|
179
|
+
raise ValueError("required family minimum must lie in [1, 4]")
|
|
180
|
+
families.append(family)
|
|
181
|
+
total += minimum
|
|
182
|
+
if len(set(families)) != len(families):
|
|
183
|
+
raise ValueError("required families cannot repeat")
|
|
184
|
+
if total > _PORTFOLIO_SIZE:
|
|
185
|
+
raise ValueError("required family minimums exceed the evaluation budget")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@dataclass(frozen=True, slots=True)
|
|
189
|
+
class FamilyExposureBound:
|
|
190
|
+
"""Authenticated lower and upper evaluator exposure for one action family."""
|
|
191
|
+
|
|
192
|
+
family: str
|
|
193
|
+
minimum_evaluations: int
|
|
194
|
+
maximum_evaluations: int
|
|
195
|
+
|
|
196
|
+
def __post_init__(self) -> None:
|
|
197
|
+
if type(self.family) is not str or not self.family:
|
|
198
|
+
raise ValueError("family must be a non-empty exact string")
|
|
199
|
+
if (
|
|
200
|
+
type(self.minimum_evaluations) is not int
|
|
201
|
+
or not 0 <= self.minimum_evaluations <= _PORTFOLIO_SIZE
|
|
202
|
+
):
|
|
203
|
+
raise ValueError("minimum_evaluations must lie in [0, 4]")
|
|
204
|
+
if (
|
|
205
|
+
type(self.maximum_evaluations) is not int
|
|
206
|
+
or not 0 <= self.maximum_evaluations <= _PORTFOLIO_SIZE
|
|
207
|
+
):
|
|
208
|
+
raise ValueError("maximum_evaluations must lie in [0, 4]")
|
|
209
|
+
if self.minimum_evaluations > self.maximum_evaluations:
|
|
210
|
+
raise ValueError("family exposure minimum cannot exceed its maximum")
|
|
211
|
+
|
|
212
|
+
def to_record(self) -> dict[str, object]:
|
|
213
|
+
self.__post_init__()
|
|
214
|
+
return {
|
|
215
|
+
"family": self.family,
|
|
216
|
+
"minimum_evaluations": self.minimum_evaluations,
|
|
217
|
+
"maximum_evaluations": self.maximum_evaluations,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(frozen=True, slots=True)
|
|
222
|
+
class FamilyExposurePhase:
|
|
223
|
+
"""Family exposure bounds activated at one sealed campaign wave index."""
|
|
224
|
+
|
|
225
|
+
start_wave_index: int
|
|
226
|
+
bounds: tuple[FamilyExposureBound, ...]
|
|
227
|
+
|
|
228
|
+
def __post_init__(self) -> None:
|
|
229
|
+
if type(self.start_wave_index) is not int or self.start_wave_index < 0:
|
|
230
|
+
raise ValueError("start_wave_index must be a non-negative exact integer")
|
|
231
|
+
if type(self.bounds) is not tuple or not self.bounds:
|
|
232
|
+
raise ValueError("bounds must be a non-empty exact tuple")
|
|
233
|
+
if len(self.bounds) > _PORTFOLIO_SIZE:
|
|
234
|
+
raise ValueError("family exposure bound count exceeds the portfolio size")
|
|
235
|
+
if any(type(value) is not FamilyExposureBound for value in self.bounds):
|
|
236
|
+
raise TypeError("bounds must contain exact FamilyExposureBound values")
|
|
237
|
+
for value in self.bounds:
|
|
238
|
+
value.__post_init__()
|
|
239
|
+
families = tuple(value.family for value in self.bounds)
|
|
240
|
+
if families != tuple(sorted(set(families))):
|
|
241
|
+
raise ValueError("phase families must be unique and canonical")
|
|
242
|
+
if sum(value.minimum_evaluations for value in self.bounds) > _PORTFOLIO_SIZE:
|
|
243
|
+
raise ValueError("phase family minima exceed the evaluation budget")
|
|
244
|
+
|
|
245
|
+
def to_record(self) -> dict[str, object]:
|
|
246
|
+
self.__post_init__()
|
|
247
|
+
return {
|
|
248
|
+
"start_wave_index": self.start_wave_index,
|
|
249
|
+
"bounds": [value.to_record() for value in self.bounds],
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _validate_family_exposure_phases(
|
|
254
|
+
values: tuple[FamilyExposurePhase, ...],
|
|
255
|
+
) -> None:
|
|
256
|
+
if type(values) is not tuple or not values:
|
|
257
|
+
raise ValueError("family_exposure_phases must be a non-empty exact tuple")
|
|
258
|
+
if any(type(value) is not FamilyExposurePhase for value in values):
|
|
259
|
+
raise TypeError(
|
|
260
|
+
"family_exposure_phases must contain exact FamilyExposurePhase values"
|
|
261
|
+
)
|
|
262
|
+
for value in values:
|
|
263
|
+
value.__post_init__()
|
|
264
|
+
starts = tuple(value.start_wave_index for value in values)
|
|
265
|
+
if starts[0] != 0:
|
|
266
|
+
raise ValueError("family exposure phases must start at wave zero")
|
|
267
|
+
if starts != tuple(sorted(set(starts))):
|
|
268
|
+
raise ValueError("family exposure phase starts must be unique and canonical")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def build_terminal_tapered_family_exposure_phases(
|
|
272
|
+
*,
|
|
273
|
+
family: str,
|
|
274
|
+
terminal_wave_index: int,
|
|
275
|
+
discovery_exposure: int = 2,
|
|
276
|
+
terminal_exposure: int = 0,
|
|
277
|
+
) -> tuple[FamilyExposurePhase, ...]:
|
|
278
|
+
"""Build a generic explore-then-taper schedule for a finite campaign.
|
|
279
|
+
|
|
280
|
+
The caller supplies a topology family and the campaign's final selector
|
|
281
|
+
wave. No workload identifier or objective value enters the schedule.
|
|
282
|
+
"""
|
|
283
|
+
|
|
284
|
+
if type(terminal_wave_index) is not int or terminal_wave_index <= 0:
|
|
285
|
+
raise ValueError("terminal_wave_index must be a positive exact integer")
|
|
286
|
+
discovery = FamilyExposureBound(
|
|
287
|
+
family=family,
|
|
288
|
+
minimum_evaluations=discovery_exposure,
|
|
289
|
+
maximum_evaluations=discovery_exposure,
|
|
290
|
+
)
|
|
291
|
+
terminal = FamilyExposureBound(
|
|
292
|
+
family=family,
|
|
293
|
+
minimum_evaluations=terminal_exposure,
|
|
294
|
+
maximum_evaluations=terminal_exposure,
|
|
295
|
+
)
|
|
296
|
+
phases = (
|
|
297
|
+
FamilyExposurePhase(start_wave_index=0, bounds=(discovery,)),
|
|
298
|
+
FamilyExposurePhase(
|
|
299
|
+
start_wave_index=terminal_wave_index,
|
|
300
|
+
bounds=(terminal,),
|
|
301
|
+
),
|
|
302
|
+
)
|
|
303
|
+
_validate_family_exposure_phases(phases)
|
|
304
|
+
return phases
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def build_controller_owned_family_exposure_phases(
|
|
308
|
+
*,
|
|
309
|
+
family: str,
|
|
310
|
+
) -> tuple[FamilyExposurePhase, ...]:
|
|
311
|
+
"""Expose the full K4 family range to an authenticated outer controller.
|
|
312
|
+
|
|
313
|
+
The horizon allocator remains responsible for structural feasibility and
|
|
314
|
+
replay, but contributes no fixed family-dose prior. A prospective
|
|
315
|
+
contextual allocation contract can therefore own the exact evaluator
|
|
316
|
+
exposure without conflicting with a second schedule.
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
phases = (
|
|
320
|
+
FamilyExposurePhase(
|
|
321
|
+
start_wave_index=0,
|
|
322
|
+
bounds=(
|
|
323
|
+
FamilyExposureBound(
|
|
324
|
+
family=family,
|
|
325
|
+
minimum_evaluations=0,
|
|
326
|
+
maximum_evaluations=_PORTFOLIO_SIZE,
|
|
327
|
+
),
|
|
328
|
+
),
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
_validate_family_exposure_phases(phases)
|
|
332
|
+
return phases
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _active_family_exposure_phase(
|
|
336
|
+
phases: tuple[FamilyExposurePhase, ...],
|
|
337
|
+
*,
|
|
338
|
+
wave_index: int,
|
|
339
|
+
) -> FamilyExposurePhase:
|
|
340
|
+
_validate_family_exposure_phases(phases)
|
|
341
|
+
if type(wave_index) is not int or wave_index < 0:
|
|
342
|
+
raise ValueError("wave_index must be a non-negative exact integer")
|
|
343
|
+
return max(
|
|
344
|
+
(value for value in phases if value.start_wave_index <= wave_index),
|
|
345
|
+
key=lambda value: value.start_wave_index,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _horizon_bounded_configuration_record(
|
|
350
|
+
phases: tuple[FamilyExposurePhase, ...],
|
|
351
|
+
) -> dict[str, object]:
|
|
352
|
+
_validate_family_exposure_phases(phases)
|
|
353
|
+
return {
|
|
354
|
+
"schema_version": 1,
|
|
355
|
+
"base_allocator": {
|
|
356
|
+
"policy_id": POLICY_ID,
|
|
357
|
+
"policy_version": POLICY_VERSION,
|
|
358
|
+
"definition_sha256": POLICY_DEFINITION_SHA256,
|
|
359
|
+
"configuration_sha256": _hash(
|
|
360
|
+
_CONFIGURATION_DOMAIN,
|
|
361
|
+
_configuration_record(),
|
|
362
|
+
),
|
|
363
|
+
},
|
|
364
|
+
"family_exposure_phases": [value.to_record() for value in phases],
|
|
365
|
+
"phase_authority": "sealed_wave_index",
|
|
366
|
+
"constraint_semantics": "assay_exposure_not_quality_prior",
|
|
367
|
+
"outcomes_consulted": False,
|
|
368
|
+
"workload_identifiers": [],
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
class StructuralPosteriorSlateRole(str, Enum):
|
|
373
|
+
"""Engine-owned purpose of one selected evaluation."""
|
|
374
|
+
|
|
375
|
+
CALIBRATED_EXPLOIT = "calibrated_exploit"
|
|
376
|
+
CALIBRATED_FRONTIER = "calibrated_frontier"
|
|
377
|
+
EPISTEMIC_STRUCTURAL = "epistemic_structural"
|
|
378
|
+
STRUCTURAL_COVERAGE = "structural_coverage"
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
_ROLES = (
|
|
382
|
+
StructuralPosteriorSlateRole.CALIBRATED_EXPLOIT,
|
|
383
|
+
StructuralPosteriorSlateRole.CALIBRATED_FRONTIER,
|
|
384
|
+
StructuralPosteriorSlateRole.EPISTEMIC_STRUCTURAL,
|
|
385
|
+
StructuralPosteriorSlateRole.STRUCTURAL_COVERAGE,
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@dataclass(frozen=True, slots=True)
|
|
390
|
+
class StructuralPosteriorMetricScoreRow:
|
|
391
|
+
"""One objective's auditable exploitation and epistemic contributions."""
|
|
392
|
+
|
|
393
|
+
metric_id: str
|
|
394
|
+
goal: MetricOptimizationGoal
|
|
395
|
+
asserted_direction: MetricEffectDirection
|
|
396
|
+
confidence: ForecastConfidenceBin
|
|
397
|
+
weight: float
|
|
398
|
+
calibration_cell: ForecastCalibrationCell
|
|
399
|
+
calibration_source: str
|
|
400
|
+
favorable_assertion: bool
|
|
401
|
+
adverse_assertion: bool
|
|
402
|
+
signed_exploitation_score: float
|
|
403
|
+
posterior_uncertainty_score: float
|
|
404
|
+
calibrated_disagreement_score: float
|
|
405
|
+
epistemic_score: float
|
|
406
|
+
explicit_abstention: bool
|
|
407
|
+
|
|
408
|
+
def __post_init__(self) -> None:
|
|
409
|
+
if type(self.metric_id) is not str or not self.metric_id:
|
|
410
|
+
raise ValueError("metric_id must be a non-empty string")
|
|
411
|
+
if type(self.goal) is not MetricOptimizationGoal:
|
|
412
|
+
raise TypeError("goal must be exact MetricOptimizationGoal")
|
|
413
|
+
if type(self.asserted_direction) is not MetricEffectDirection:
|
|
414
|
+
raise TypeError("asserted_direction must be exact MetricEffectDirection")
|
|
415
|
+
if type(self.confidence) is not ForecastConfidenceBin:
|
|
416
|
+
raise TypeError("confidence must be exact ForecastConfidenceBin")
|
|
417
|
+
_require_finite(self.weight, name="weight")
|
|
418
|
+
if self.weight <= 0.0:
|
|
419
|
+
raise ValueError("weight must be strictly positive")
|
|
420
|
+
if type(self.calibration_cell) is not ForecastCalibrationCell:
|
|
421
|
+
raise TypeError("calibration_cell must be exact ForecastCalibrationCell")
|
|
422
|
+
self.calibration_cell.__post_init__()
|
|
423
|
+
if self.calibration_source not in {
|
|
424
|
+
"supported_family",
|
|
425
|
+
"metric_direction_confidence",
|
|
426
|
+
"declared_prior",
|
|
427
|
+
}:
|
|
428
|
+
raise ValueError("unsupported calibration_source")
|
|
429
|
+
if type(self.favorable_assertion) is not bool:
|
|
430
|
+
raise TypeError("favorable_assertion must be an exact bool")
|
|
431
|
+
if type(self.adverse_assertion) is not bool:
|
|
432
|
+
raise TypeError("adverse_assertion must be an exact bool")
|
|
433
|
+
if self.favorable_assertion and self.adverse_assertion:
|
|
434
|
+
raise ValueError("one assertion cannot be favorable and adverse")
|
|
435
|
+
_require_finite(
|
|
436
|
+
self.signed_exploitation_score,
|
|
437
|
+
name="signed_exploitation_score",
|
|
438
|
+
)
|
|
439
|
+
for name in (
|
|
440
|
+
"posterior_uncertainty_score",
|
|
441
|
+
"calibrated_disagreement_score",
|
|
442
|
+
"epistemic_score",
|
|
443
|
+
):
|
|
444
|
+
_require_unit_interval(getattr(self, name), name=name)
|
|
445
|
+
if type(self.explicit_abstention) is not bool:
|
|
446
|
+
raise TypeError("explicit_abstention must be an exact bool")
|
|
447
|
+
expected_abstention = (
|
|
448
|
+
self.asserted_direction is MetricEffectDirection.UNKNOWN
|
|
449
|
+
)
|
|
450
|
+
if self.explicit_abstention is not expected_abstention:
|
|
451
|
+
raise ValueError("explicit_abstention differs from asserted direction")
|
|
452
|
+
if self.explicit_abstention and self.epistemic_score != 1.0:
|
|
453
|
+
raise ValueError("explicit abstention must have maximum epistemic score")
|
|
454
|
+
|
|
455
|
+
def to_record(self) -> dict[str, object]:
|
|
456
|
+
self.__post_init__()
|
|
457
|
+
return {
|
|
458
|
+
"metric_id": self.metric_id,
|
|
459
|
+
"goal": self.goal.value,
|
|
460
|
+
"asserted_direction": self.asserted_direction.value,
|
|
461
|
+
"confidence": self.confidence.value,
|
|
462
|
+
"weight_hex": self.weight.hex(),
|
|
463
|
+
"calibration_cell": self.calibration_cell.to_record(),
|
|
464
|
+
"calibration_source": self.calibration_source,
|
|
465
|
+
"favorable_assertion": self.favorable_assertion,
|
|
466
|
+
"adverse_assertion": self.adverse_assertion,
|
|
467
|
+
"signed_exploitation_score_hex": (
|
|
468
|
+
self.signed_exploitation_score.hex()
|
|
469
|
+
),
|
|
470
|
+
"posterior_uncertainty_score_hex": (
|
|
471
|
+
self.posterior_uncertainty_score.hex()
|
|
472
|
+
),
|
|
473
|
+
"calibrated_disagreement_score_hex": (
|
|
474
|
+
self.calibrated_disagreement_score.hex()
|
|
475
|
+
),
|
|
476
|
+
"epistemic_score_hex": self.epistemic_score.hex(),
|
|
477
|
+
"explicit_abstention": self.explicit_abstention,
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
@dataclass(frozen=True, slots=True)
|
|
482
|
+
class StructuralPosteriorMemberScoreRow:
|
|
483
|
+
"""All prior-only role scores for one member of the current slate."""
|
|
484
|
+
|
|
485
|
+
option_id: str
|
|
486
|
+
option_identity_sha256: str
|
|
487
|
+
model_rank: int
|
|
488
|
+
metric_scores: tuple[StructuralPosteriorMetricScoreRow, ...]
|
|
489
|
+
calibrated_exploitation_score: float
|
|
490
|
+
calibrated_frontier_score: float
|
|
491
|
+
raw_epistemic_score: float
|
|
492
|
+
structural_coverage_score: float
|
|
493
|
+
epistemic_structural_score: float
|
|
494
|
+
model_declared_assigned_card_keys: tuple[str, ...]
|
|
495
|
+
|
|
496
|
+
def __post_init__(self) -> None:
|
|
497
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
498
|
+
raise ValueError("option_id must be a non-empty string")
|
|
499
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
500
|
+
if type(self.model_rank) is not int or self.model_rank <= 0:
|
|
501
|
+
raise ValueError("model_rank must be a positive exact integer")
|
|
502
|
+
if type(self.metric_scores) is not tuple or not self.metric_scores or any(
|
|
503
|
+
type(value) is not StructuralPosteriorMetricScoreRow
|
|
504
|
+
for value in self.metric_scores
|
|
505
|
+
):
|
|
506
|
+
raise ValueError("metric_scores must contain exact score rows")
|
|
507
|
+
for value in self.metric_scores:
|
|
508
|
+
value.__post_init__()
|
|
509
|
+
metric_ids = tuple(value.metric_id for value in self.metric_scores)
|
|
510
|
+
if metric_ids != tuple(sorted(set(metric_ids))):
|
|
511
|
+
raise ValueError("metric_scores must use canonical unique metric order")
|
|
512
|
+
for name in (
|
|
513
|
+
"calibrated_exploitation_score",
|
|
514
|
+
"calibrated_frontier_score",
|
|
515
|
+
):
|
|
516
|
+
_require_finite(getattr(self, name), name=name)
|
|
517
|
+
for name in (
|
|
518
|
+
"raw_epistemic_score",
|
|
519
|
+
"structural_coverage_score",
|
|
520
|
+
"epistemic_structural_score",
|
|
521
|
+
):
|
|
522
|
+
_require_unit_interval(getattr(self, name), name=name)
|
|
523
|
+
if self.epistemic_structural_score != (
|
|
524
|
+
self.raw_epistemic_score * self.structural_coverage_score
|
|
525
|
+
):
|
|
526
|
+
raise ValueError("epistemic-structural score differs from fixed coupling")
|
|
527
|
+
_require_canonical_strings(
|
|
528
|
+
self.model_declared_assigned_card_keys,
|
|
529
|
+
name="model_declared_assigned_card_keys",
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
def score_for(self, role: StructuralPosteriorSlateRole) -> float:
|
|
533
|
+
self.__post_init__()
|
|
534
|
+
if role is StructuralPosteriorSlateRole.CALIBRATED_EXPLOIT:
|
|
535
|
+
return self.calibrated_exploitation_score
|
|
536
|
+
if role is StructuralPosteriorSlateRole.CALIBRATED_FRONTIER:
|
|
537
|
+
return self.calibrated_frontier_score
|
|
538
|
+
if role is StructuralPosteriorSlateRole.EPISTEMIC_STRUCTURAL:
|
|
539
|
+
return self.epistemic_structural_score
|
|
540
|
+
if role is StructuralPosteriorSlateRole.STRUCTURAL_COVERAGE:
|
|
541
|
+
return self.structural_coverage_score
|
|
542
|
+
raise TypeError("role must be exact StructuralPosteriorSlateRole")
|
|
543
|
+
|
|
544
|
+
def to_record(self) -> dict[str, object]:
|
|
545
|
+
self.__post_init__()
|
|
546
|
+
return {
|
|
547
|
+
"option_id": self.option_id,
|
|
548
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
549
|
+
"model_rank": self.model_rank,
|
|
550
|
+
"metric_scores": [value.to_record() for value in self.metric_scores],
|
|
551
|
+
"calibrated_exploitation_score_hex": (
|
|
552
|
+
self.calibrated_exploitation_score.hex()
|
|
553
|
+
),
|
|
554
|
+
"calibrated_frontier_score_hex": (
|
|
555
|
+
self.calibrated_frontier_score.hex()
|
|
556
|
+
),
|
|
557
|
+
"raw_epistemic_score_hex": self.raw_epistemic_score.hex(),
|
|
558
|
+
"structural_coverage_score_hex": (
|
|
559
|
+
self.structural_coverage_score.hex()
|
|
560
|
+
),
|
|
561
|
+
"epistemic_structural_score_hex": (
|
|
562
|
+
self.epistemic_structural_score.hex()
|
|
563
|
+
),
|
|
564
|
+
"model_declared_assigned_card_keys": list(
|
|
565
|
+
self.model_declared_assigned_card_keys
|
|
566
|
+
),
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
@dataclass(frozen=True, slots=True)
|
|
571
|
+
class StructuralPosteriorAllocatedMember:
|
|
572
|
+
"""One selected member with its engine-owned role and score."""
|
|
573
|
+
|
|
574
|
+
role: StructuralPosteriorSlateRole
|
|
575
|
+
option_id: str
|
|
576
|
+
option_identity_sha256: str
|
|
577
|
+
model_rank: int
|
|
578
|
+
role_score: float
|
|
579
|
+
|
|
580
|
+
def __post_init__(self) -> None:
|
|
581
|
+
if type(self.role) is not StructuralPosteriorSlateRole:
|
|
582
|
+
raise TypeError("role must be exact StructuralPosteriorSlateRole")
|
|
583
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
584
|
+
raise ValueError("option_id must be a non-empty string")
|
|
585
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
586
|
+
if type(self.model_rank) is not int or self.model_rank <= 0:
|
|
587
|
+
raise ValueError("model_rank must be a positive exact integer")
|
|
588
|
+
_require_finite(self.role_score, name="role_score")
|
|
589
|
+
|
|
590
|
+
def to_record(self) -> dict[str, object]:
|
|
591
|
+
self.__post_init__()
|
|
592
|
+
return {
|
|
593
|
+
"role": self.role.value,
|
|
594
|
+
"option_id": self.option_id,
|
|
595
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
596
|
+
"model_rank": self.model_rank,
|
|
597
|
+
"role_score_hex": self.role_score.hex(),
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
@dataclass(frozen=True, slots=True)
|
|
602
|
+
class _StructuralPosteriorAssignment:
|
|
603
|
+
selected: tuple[StructuralPosteriorAllocatedMember, ...]
|
|
604
|
+
joint_score: float
|
|
605
|
+
diversity_score: float
|
|
606
|
+
distinct_family_count: int
|
|
607
|
+
distinct_locus_count: int
|
|
608
|
+
distinct_phenotype_count: int
|
|
609
|
+
administered_card_keys: tuple[str, ...]
|
|
610
|
+
feasible_role_assignment_count: int
|
|
611
|
+
memory_dose_assessment: PortfolioMemoryDoseAssessment | None
|
|
612
|
+
family_exposure_violation_count: int
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def _favorable(
|
|
616
|
+
direction: MetricEffectDirection,
|
|
617
|
+
goal: MetricOptimizationGoal,
|
|
618
|
+
) -> tuple[bool, bool]:
|
|
619
|
+
if direction in {MetricEffectDirection.UNKNOWN, MetricEffectDirection.UNCHANGED}:
|
|
620
|
+
return False, False
|
|
621
|
+
favorable = (
|
|
622
|
+
goal is MetricOptimizationGoal.MINIMIZE
|
|
623
|
+
and direction is MetricEffectDirection.DECREASE
|
|
624
|
+
) or (
|
|
625
|
+
goal is MetricOptimizationGoal.MAXIMIZE
|
|
626
|
+
and direction is MetricEffectDirection.INCREASE
|
|
627
|
+
)
|
|
628
|
+
return favorable, not favorable
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def _score_member(
|
|
632
|
+
request: SlateAllocationRequest,
|
|
633
|
+
member: CalibratedSlateMember,
|
|
634
|
+
) -> StructuralPosteriorMemberScoreRow:
|
|
635
|
+
snapshot = request.calibration_snapshot
|
|
636
|
+
if snapshot is None:
|
|
637
|
+
raise ValueError("structural-posterior policy requires a calibration snapshot")
|
|
638
|
+
objective_by_metric = {value.metric_id: value for value in request.objectives}
|
|
639
|
+
metric_scores: list[StructuralPosteriorMetricScoreRow] = []
|
|
640
|
+
weighted_exploitation = 0.0
|
|
641
|
+
weighted_epistemic = 0.0
|
|
642
|
+
total_weight = sum(value.weight for value in request.objectives)
|
|
643
|
+
for prediction in member.predictions:
|
|
644
|
+
objective = objective_by_metric[prediction.metric_id]
|
|
645
|
+
cell, source = snapshot.lookup(
|
|
646
|
+
metric_id=prediction.metric_id,
|
|
647
|
+
asserted_direction=prediction.asserted_direction,
|
|
648
|
+
confidence=prediction.confidence,
|
|
649
|
+
family=member.family,
|
|
650
|
+
)
|
|
651
|
+
probability = cell.posterior_correctness
|
|
652
|
+
favorable, adverse = _favorable(
|
|
653
|
+
prediction.asserted_direction,
|
|
654
|
+
objective.goal,
|
|
655
|
+
)
|
|
656
|
+
# Posterior correctness is a probability that the asserted direction
|
|
657
|
+
# is right, not a non-negative confidence multiplier. A model family
|
|
658
|
+
# below chance is useful negative evidence: its favorable assertion
|
|
659
|
+
# has negative expected utility and its adverse assertion has positive
|
|
660
|
+
# expected utility. This is the key distinction between calibration
|
|
661
|
+
# and merely shrinking an unreliable model toward zero.
|
|
662
|
+
signed_exploitation = (
|
|
663
|
+
(2.0 * probability) - 1.0
|
|
664
|
+
if favorable
|
|
665
|
+
else 1.0 - (2.0 * probability)
|
|
666
|
+
if adverse
|
|
667
|
+
else 0.0
|
|
668
|
+
)
|
|
669
|
+
posterior_uncertainty = 1.0 - abs((2.0 * probability) - 1.0)
|
|
670
|
+
calibrated_disagreement = max(0.0, 0.5 - probability) * 2.0
|
|
671
|
+
explicit_abstention = (
|
|
672
|
+
prediction.asserted_direction is MetricEffectDirection.UNKNOWN
|
|
673
|
+
)
|
|
674
|
+
epistemic = (
|
|
675
|
+
1.0
|
|
676
|
+
if explicit_abstention
|
|
677
|
+
else (posterior_uncertainty + calibrated_disagreement) / 2.0
|
|
678
|
+
)
|
|
679
|
+
weighted_exploitation += objective.weight * signed_exploitation
|
|
680
|
+
weighted_epistemic += objective.weight * epistemic
|
|
681
|
+
metric_scores.append(
|
|
682
|
+
StructuralPosteriorMetricScoreRow(
|
|
683
|
+
metric_id=prediction.metric_id,
|
|
684
|
+
goal=objective.goal,
|
|
685
|
+
asserted_direction=prediction.asserted_direction,
|
|
686
|
+
confidence=prediction.confidence,
|
|
687
|
+
weight=objective.weight,
|
|
688
|
+
calibration_cell=cell,
|
|
689
|
+
calibration_source=source,
|
|
690
|
+
favorable_assertion=favorable,
|
|
691
|
+
adverse_assertion=adverse,
|
|
692
|
+
signed_exploitation_score=signed_exploitation,
|
|
693
|
+
posterior_uncertainty_score=posterior_uncertainty,
|
|
694
|
+
calibrated_disagreement_score=calibrated_disagreement,
|
|
695
|
+
epistemic_score=epistemic,
|
|
696
|
+
explicit_abstention=explicit_abstention,
|
|
697
|
+
)
|
|
698
|
+
)
|
|
699
|
+
exploitation = weighted_exploitation / total_weight
|
|
700
|
+
raw_epistemic = weighted_epistemic / total_weight
|
|
701
|
+
declared_cards = tuple(
|
|
702
|
+
card
|
|
703
|
+
for card in request.assigned_card_keys
|
|
704
|
+
if card in member.supporting_card_keys
|
|
705
|
+
)
|
|
706
|
+
structural = (
|
|
707
|
+
member.structural_evidence.archive_novelty_score
|
|
708
|
+
+ member.structural_evidence.structural_coverage_score
|
|
709
|
+
) / 2.0
|
|
710
|
+
return StructuralPosteriorMemberScoreRow(
|
|
711
|
+
option_id=member.option_id,
|
|
712
|
+
option_identity_sha256=member.option_identity_sha256,
|
|
713
|
+
model_rank=member.model_rank,
|
|
714
|
+
metric_scores=tuple(metric_scores),
|
|
715
|
+
calibrated_exploitation_score=exploitation,
|
|
716
|
+
calibrated_frontier_score=(
|
|
717
|
+
exploitation + (_FRONTIER_STRUCTURAL_WEIGHT * structural)
|
|
718
|
+
),
|
|
719
|
+
raw_epistemic_score=raw_epistemic,
|
|
720
|
+
structural_coverage_score=structural,
|
|
721
|
+
epistemic_structural_score=raw_epistemic * structural,
|
|
722
|
+
model_declared_assigned_card_keys=declared_cards,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def score_structural_posterior_slate(
|
|
727
|
+
request: SlateAllocationRequest,
|
|
728
|
+
) -> tuple[StructuralPosteriorMemberScoreRow, ...]:
|
|
729
|
+
"""Project prior-only calibrated structural evidence without allocating.
|
|
730
|
+
|
|
731
|
+
This is the stable projection boundary for downstream acquisition policies.
|
|
732
|
+
It deliberately returns score evidence for the complete sealed slate; it
|
|
733
|
+
does not choose evaluator exposures or consult any candidate outcome.
|
|
734
|
+
"""
|
|
735
|
+
|
|
736
|
+
if type(request) is not SlateAllocationRequest:
|
|
737
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
738
|
+
request.revalidate()
|
|
739
|
+
return tuple(_score_member(request, member) for member in request.slate.members)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def _best_assignment(
|
|
743
|
+
request: SlateAllocationRequest,
|
|
744
|
+
score_rows: tuple[StructuralPosteriorMemberScoreRow, ...],
|
|
745
|
+
*,
|
|
746
|
+
required_family_minimums: tuple[tuple[str, int], ...] = (),
|
|
747
|
+
required_family_bounds: tuple[FamilyExposureBound, ...] = (),
|
|
748
|
+
minimize_family_bound_violation: bool = False,
|
|
749
|
+
) -> _StructuralPosteriorAssignment:
|
|
750
|
+
if required_family_minimums and required_family_bounds:
|
|
751
|
+
raise ValueError("family minima and family bounds are mutually exclusive")
|
|
752
|
+
if required_family_minimums:
|
|
753
|
+
_validate_required_family_minimums(required_family_minimums)
|
|
754
|
+
if required_family_bounds:
|
|
755
|
+
FamilyExposurePhase(0, required_family_bounds).__post_init__()
|
|
756
|
+
if minimize_family_bound_violation and not required_family_bounds:
|
|
757
|
+
raise ValueError("family-bound recourse requires explicit family bounds")
|
|
758
|
+
member_by_id = {value.option_id: value for value in request.slate.members}
|
|
759
|
+
required_option_ids = set(request.required_option_ids)
|
|
760
|
+
allowed_pairs = (
|
|
761
|
+
None
|
|
762
|
+
if request.pairwise_disjoint_option_id_pairs is None
|
|
763
|
+
else {frozenset(value) for value in request.pairwise_disjoint_option_id_pairs}
|
|
764
|
+
)
|
|
765
|
+
memory_dose_by_subset: dict[
|
|
766
|
+
tuple[str, ...], PortfolioMemoryDoseAssessment | None
|
|
767
|
+
] = {}
|
|
768
|
+
feasible: list[
|
|
769
|
+
tuple[
|
|
770
|
+
tuple[object, ...],
|
|
771
|
+
tuple[StructuralPosteriorMemberScoreRow, ...],
|
|
772
|
+
tuple[float, ...],
|
|
773
|
+
float,
|
|
774
|
+
float,
|
|
775
|
+
int,
|
|
776
|
+
int,
|
|
777
|
+
int,
|
|
778
|
+
tuple[str, ...],
|
|
779
|
+
PortfolioMemoryDoseAssessment | None,
|
|
780
|
+
]
|
|
781
|
+
] = []
|
|
782
|
+
for rows in permutations(score_rows, _PORTFOLIO_SIZE):
|
|
783
|
+
if not required_option_ids.issubset(
|
|
784
|
+
{row.option_id for row in rows}
|
|
785
|
+
):
|
|
786
|
+
continue
|
|
787
|
+
if allowed_pairs is not None and any(
|
|
788
|
+
frozenset((left.option_id, right.option_id)) not in allowed_pairs
|
|
789
|
+
for left_index, left in enumerate(rows)
|
|
790
|
+
for right in rows[left_index + 1 :]
|
|
791
|
+
):
|
|
792
|
+
continue
|
|
793
|
+
members = tuple(member_by_id[row.option_id] for row in rows)
|
|
794
|
+
family_count = len({value.family for value in members})
|
|
795
|
+
family_exposure_violation = sum(
|
|
796
|
+
max(
|
|
797
|
+
bound.minimum_evaluations
|
|
798
|
+
- sum(value.family == bound.family for value in members),
|
|
799
|
+
0,
|
|
800
|
+
sum(value.family == bound.family for value in members)
|
|
801
|
+
- bound.maximum_evaluations,
|
|
802
|
+
)
|
|
803
|
+
for bound in required_family_bounds
|
|
804
|
+
)
|
|
805
|
+
if (
|
|
806
|
+
request.min_distinct_families is not None
|
|
807
|
+
and family_count < request.min_distinct_families
|
|
808
|
+
):
|
|
809
|
+
continue
|
|
810
|
+
if any(
|
|
811
|
+
sum(value.family == family for value in members) < minimum
|
|
812
|
+
for family, minimum in required_family_minimums
|
|
813
|
+
):
|
|
814
|
+
continue
|
|
815
|
+
if family_exposure_violation and not minimize_family_bound_violation:
|
|
816
|
+
continue
|
|
817
|
+
if (
|
|
818
|
+
request.memory_dose_contract is not None
|
|
819
|
+
and not rows[1].model_declared_assigned_card_keys
|
|
820
|
+
):
|
|
821
|
+
continue
|
|
822
|
+
administered = (
|
|
823
|
+
()
|
|
824
|
+
if request.memory_dose_contract is None
|
|
825
|
+
else tuple(
|
|
826
|
+
sorted(
|
|
827
|
+
{
|
|
828
|
+
card
|
|
829
|
+
for row in rows
|
|
830
|
+
for card in row.model_declared_assigned_card_keys
|
|
831
|
+
}
|
|
832
|
+
)
|
|
833
|
+
)
|
|
834
|
+
)
|
|
835
|
+
if (
|
|
836
|
+
request.memory_dose_contract is not None
|
|
837
|
+
and administered != request.assigned_card_keys
|
|
838
|
+
):
|
|
839
|
+
continue
|
|
840
|
+
subset_key = tuple(sorted(row.option_id for row in rows))
|
|
841
|
+
if subset_key not in memory_dose_by_subset:
|
|
842
|
+
canonical_members = tuple(
|
|
843
|
+
member_by_id[option_id] for option_id in subset_key
|
|
844
|
+
)
|
|
845
|
+
memory_dose_by_subset[subset_key] = (
|
|
846
|
+
None
|
|
847
|
+
if request.memory_dose_contract is None
|
|
848
|
+
else assess_allocated_slate_memory_dose(
|
|
849
|
+
request,
|
|
850
|
+
canonical_members,
|
|
851
|
+
)
|
|
852
|
+
)
|
|
853
|
+
dose_assessment = memory_dose_by_subset[subset_key]
|
|
854
|
+
if dose_assessment is not None and not dose_assessment.passed:
|
|
855
|
+
continue
|
|
856
|
+
locus_count = len({value.locus_key for value in members})
|
|
857
|
+
phenotype_count = len(
|
|
858
|
+
{value.phenotype_identity_sha256 for value in members}
|
|
859
|
+
)
|
|
860
|
+
diversity = (
|
|
861
|
+
family_count + locus_count + phenotype_count
|
|
862
|
+
) / (3.0 * _PORTFOLIO_SIZE)
|
|
863
|
+
role_scores = tuple(row.score_for(role) for role, row in zip(_ROLES, rows))
|
|
864
|
+
joint_score = sum(role_scores) + (_DIVERSITY_WEIGHT * diversity)
|
|
865
|
+
tie_key: tuple[object, ...] = (
|
|
866
|
+
*((family_exposure_violation,) if minimize_family_bound_violation else ()),
|
|
867
|
+
-joint_score,
|
|
868
|
+
*(-value for value in role_scores),
|
|
869
|
+
tuple(value.model_rank for value in rows),
|
|
870
|
+
tuple(value.option_id for value in rows),
|
|
871
|
+
)
|
|
872
|
+
feasible.append(
|
|
873
|
+
(
|
|
874
|
+
tie_key,
|
|
875
|
+
rows,
|
|
876
|
+
role_scores,
|
|
877
|
+
joint_score,
|
|
878
|
+
diversity,
|
|
879
|
+
family_count,
|
|
880
|
+
locus_count,
|
|
881
|
+
phenotype_count,
|
|
882
|
+
administered,
|
|
883
|
+
dose_assessment,
|
|
884
|
+
)
|
|
885
|
+
)
|
|
886
|
+
if not feasible:
|
|
887
|
+
raise ValueError(
|
|
888
|
+
"slate has no feasible calibrated four-role assignment"
|
|
889
|
+
)
|
|
890
|
+
(
|
|
891
|
+
_,
|
|
892
|
+
rows,
|
|
893
|
+
role_scores,
|
|
894
|
+
joint_score,
|
|
895
|
+
diversity,
|
|
896
|
+
family_count,
|
|
897
|
+
locus_count,
|
|
898
|
+
phenotype_count,
|
|
899
|
+
administered,
|
|
900
|
+
dose_assessment,
|
|
901
|
+
) = min(feasible, key=lambda value: value[0])
|
|
902
|
+
# Feasibility is invariant to member order and is therefore cached by
|
|
903
|
+
# unordered subset above. The durable dose receipt is not: its member
|
|
904
|
+
# bindings include the final rank. Reissue that receipt over the exact
|
|
905
|
+
# four-role order that will cross the decision trust boundary.
|
|
906
|
+
if request.memory_dose_contract is not None:
|
|
907
|
+
dose_assessment = assess_allocated_slate_memory_dose(
|
|
908
|
+
request,
|
|
909
|
+
tuple(member_by_id[row.option_id] for row in rows),
|
|
910
|
+
)
|
|
911
|
+
if not dose_assessment.passed: # Defensive after subset feasibility.
|
|
912
|
+
raise RuntimeError(
|
|
913
|
+
"winning structural-posterior assignment violated memory dose"
|
|
914
|
+
)
|
|
915
|
+
selected = tuple(
|
|
916
|
+
StructuralPosteriorAllocatedMember(
|
|
917
|
+
role=role,
|
|
918
|
+
option_id=row.option_id,
|
|
919
|
+
option_identity_sha256=row.option_identity_sha256,
|
|
920
|
+
model_rank=row.model_rank,
|
|
921
|
+
role_score=score,
|
|
922
|
+
)
|
|
923
|
+
for role, row, score in zip(_ROLES, rows, role_scores)
|
|
924
|
+
)
|
|
925
|
+
return _StructuralPosteriorAssignment(
|
|
926
|
+
selected=selected,
|
|
927
|
+
joint_score=joint_score,
|
|
928
|
+
diversity_score=diversity,
|
|
929
|
+
distinct_family_count=family_count,
|
|
930
|
+
distinct_locus_count=locus_count,
|
|
931
|
+
distinct_phenotype_count=phenotype_count,
|
|
932
|
+
administered_card_keys=administered,
|
|
933
|
+
feasible_role_assignment_count=len(feasible),
|
|
934
|
+
memory_dose_assessment=dose_assessment,
|
|
935
|
+
family_exposure_violation_count=(
|
|
936
|
+
sum(
|
|
937
|
+
max(
|
|
938
|
+
bound.minimum_evaluations
|
|
939
|
+
- sum(
|
|
940
|
+
member_by_id[row.option_id].family == bound.family
|
|
941
|
+
for row in rows
|
|
942
|
+
),
|
|
943
|
+
0,
|
|
944
|
+
sum(
|
|
945
|
+
member_by_id[row.option_id].family == bound.family
|
|
946
|
+
for row in rows
|
|
947
|
+
)
|
|
948
|
+
- bound.maximum_evaluations,
|
|
949
|
+
)
|
|
950
|
+
for bound in required_family_bounds
|
|
951
|
+
)
|
|
952
|
+
),
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
def _validate_request(request: SlateAllocationRequest) -> None:
|
|
957
|
+
if len(request.slate.members) != _SLATE_SIZE:
|
|
958
|
+
raise ValueError("structural-posterior policy requires exactly eight members")
|
|
959
|
+
if request.portfolio_size != _PORTFOLIO_SIZE:
|
|
960
|
+
raise ValueError("structural-posterior policy requires four evaluations")
|
|
961
|
+
snapshot = request.calibration_snapshot
|
|
962
|
+
if snapshot is None:
|
|
963
|
+
raise ValueError("structural-posterior policy requires a calibration snapshot")
|
|
964
|
+
if snapshot.cutoff_wave_index_exclusive > request.slate.wave_index:
|
|
965
|
+
raise ValueError("calibration snapshot cutoff reaches beyond current wave")
|
|
966
|
+
if any(
|
|
967
|
+
value.prediction.wave_index >= request.slate.wave_index
|
|
968
|
+
for value in snapshot.observations
|
|
969
|
+
):
|
|
970
|
+
raise ValueError("calibration snapshot contains current-wave outcome evidence")
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
def _validate_structural_decision(
|
|
974
|
+
value: Any,
|
|
975
|
+
*,
|
|
976
|
+
required_family_minimums: tuple[tuple[str, int], ...] = (),
|
|
977
|
+
required_family_bounds: tuple[FamilyExposureBound, ...] = (),
|
|
978
|
+
minimize_family_bound_violation: bool = False,
|
|
979
|
+
) -> _StructuralPosteriorAssignment:
|
|
980
|
+
if required_family_minimums and required_family_bounds:
|
|
981
|
+
raise ValueError("family minima and family bounds are mutually exclusive")
|
|
982
|
+
if required_family_minimums:
|
|
983
|
+
_validate_required_family_minimums(required_family_minimums)
|
|
984
|
+
if required_family_bounds:
|
|
985
|
+
FamilyExposurePhase(0, required_family_bounds).__post_init__()
|
|
986
|
+
if type(value.request) is not SlateAllocationRequest:
|
|
987
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
988
|
+
value.request.revalidate()
|
|
989
|
+
_validate_request(value.request)
|
|
990
|
+
if type(value.score_rows) is not tuple or any(
|
|
991
|
+
type(row) is not StructuralPosteriorMemberScoreRow
|
|
992
|
+
for row in value.score_rows
|
|
993
|
+
):
|
|
994
|
+
raise TypeError("score_rows must contain exact member score rows")
|
|
995
|
+
for row in value.score_rows:
|
|
996
|
+
row.__post_init__()
|
|
997
|
+
if type(value.selected) is not tuple or any(
|
|
998
|
+
type(row) is not StructuralPosteriorAllocatedMember
|
|
999
|
+
for row in value.selected
|
|
1000
|
+
):
|
|
1001
|
+
raise TypeError("selected must contain exact allocated members")
|
|
1002
|
+
for row in value.selected:
|
|
1003
|
+
row.__post_init__()
|
|
1004
|
+
if value.memory_dose_assessment is not None:
|
|
1005
|
+
if type(value.memory_dose_assessment) is not PortfolioMemoryDoseAssessment:
|
|
1006
|
+
raise TypeError("memory_dose_assessment must be exact or None")
|
|
1007
|
+
value.memory_dose_assessment.__post_init__()
|
|
1008
|
+
expected_rows = tuple(
|
|
1009
|
+
_score_member(value.request, member)
|
|
1010
|
+
for member in value.request.slate.members
|
|
1011
|
+
)
|
|
1012
|
+
if value.score_rows != expected_rows:
|
|
1013
|
+
raise ValueError("score rows differ from sealed prior evidence")
|
|
1014
|
+
expected = _best_assignment(
|
|
1015
|
+
value.request,
|
|
1016
|
+
expected_rows,
|
|
1017
|
+
required_family_minimums=required_family_minimums,
|
|
1018
|
+
required_family_bounds=required_family_bounds,
|
|
1019
|
+
minimize_family_bound_violation=minimize_family_bound_violation,
|
|
1020
|
+
)
|
|
1021
|
+
observed_values = (
|
|
1022
|
+
value.selected,
|
|
1023
|
+
value.joint_score,
|
|
1024
|
+
value.diversity_score,
|
|
1025
|
+
value.distinct_family_count,
|
|
1026
|
+
value.distinct_locus_count,
|
|
1027
|
+
value.distinct_phenotype_count,
|
|
1028
|
+
value.administered_card_keys,
|
|
1029
|
+
value.feasible_role_assignment_count,
|
|
1030
|
+
value.memory_dose_assessment,
|
|
1031
|
+
)
|
|
1032
|
+
expected_values = (
|
|
1033
|
+
expected.selected,
|
|
1034
|
+
expected.joint_score,
|
|
1035
|
+
expected.diversity_score,
|
|
1036
|
+
expected.distinct_family_count,
|
|
1037
|
+
expected.distinct_locus_count,
|
|
1038
|
+
expected.distinct_phenotype_count,
|
|
1039
|
+
expected.administered_card_keys,
|
|
1040
|
+
expected.feasible_role_assignment_count,
|
|
1041
|
+
expected.memory_dose_assessment,
|
|
1042
|
+
)
|
|
1043
|
+
if observed_values != expected_values:
|
|
1044
|
+
raise ValueError("decision differs from exact structural-posterior allocation")
|
|
1045
|
+
return expected
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1049
|
+
class StructuralPosteriorSlateDecision:
|
|
1050
|
+
"""Replayable receipt for one outcome-blind structural-posterior decision."""
|
|
1051
|
+
|
|
1052
|
+
request: SlateAllocationRequest
|
|
1053
|
+
score_rows: tuple[StructuralPosteriorMemberScoreRow, ...]
|
|
1054
|
+
selected: tuple[StructuralPosteriorAllocatedMember, ...]
|
|
1055
|
+
joint_score: float
|
|
1056
|
+
diversity_score: float
|
|
1057
|
+
distinct_family_count: int
|
|
1058
|
+
distinct_locus_count: int
|
|
1059
|
+
distinct_phenotype_count: int
|
|
1060
|
+
administered_card_keys: tuple[str, ...]
|
|
1061
|
+
feasible_role_assignment_count: int
|
|
1062
|
+
memory_dose_assessment: PortfolioMemoryDoseAssessment | None = None
|
|
1063
|
+
|
|
1064
|
+
policy_id: ClassVar[str] = POLICY_ID
|
|
1065
|
+
policy_version: ClassVar[int] = POLICY_VERSION
|
|
1066
|
+
policy_definition_sha256: ClassVar[str] = POLICY_DEFINITION_SHA256
|
|
1067
|
+
|
|
1068
|
+
def __post_init__(self) -> None:
|
|
1069
|
+
_validate_structural_decision(self)
|
|
1070
|
+
|
|
1071
|
+
def revalidate(self) -> None:
|
|
1072
|
+
if type(self) is not StructuralPosteriorSlateDecision:
|
|
1073
|
+
raise TypeError("decision must be exact StructuralPosteriorSlateDecision")
|
|
1074
|
+
StructuralPosteriorSlateDecision.__post_init__(self)
|
|
1075
|
+
|
|
1076
|
+
@property
|
|
1077
|
+
def prior_only(self) -> bool:
|
|
1078
|
+
self.revalidate()
|
|
1079
|
+
snapshot = self.request.calibration_snapshot
|
|
1080
|
+
assert snapshot is not None
|
|
1081
|
+
return all(
|
|
1082
|
+
value.prediction.wave_index < self.request.slate.wave_index
|
|
1083
|
+
for value in snapshot.observations
|
|
1084
|
+
)
|
|
1085
|
+
|
|
1086
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1087
|
+
self.revalidate()
|
|
1088
|
+
return {
|
|
1089
|
+
"schema_version": 1,
|
|
1090
|
+
"event_type": "structural_posterior_four_role_slate_allocated",
|
|
1091
|
+
"policy_id": self.policy_id,
|
|
1092
|
+
"policy_version": self.policy_version,
|
|
1093
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
1094
|
+
"request": self.request.to_record(),
|
|
1095
|
+
"request_sha256": self.request.request_sha256,
|
|
1096
|
+
"calibration_snapshot_sha256": (
|
|
1097
|
+
self.request.calibration_snapshot.snapshot_sha256
|
|
1098
|
+
if self.request.calibration_snapshot is not None
|
|
1099
|
+
else None
|
|
1100
|
+
),
|
|
1101
|
+
"prior_only": self.prior_only,
|
|
1102
|
+
"roles": [value.value for value in _ROLES],
|
|
1103
|
+
"score_rows": [value.to_record() for value in self.score_rows],
|
|
1104
|
+
"selected": [value.to_record() for value in self.selected],
|
|
1105
|
+
"joint_score_hex": self.joint_score.hex(),
|
|
1106
|
+
"diversity_score_hex": self.diversity_score.hex(),
|
|
1107
|
+
"distinct_family_count": self.distinct_family_count,
|
|
1108
|
+
"distinct_locus_count": self.distinct_locus_count,
|
|
1109
|
+
"distinct_phenotype_count": self.distinct_phenotype_count,
|
|
1110
|
+
"administered_card_keys": list(self.administered_card_keys),
|
|
1111
|
+
"feasible_role_assignment_count": self.feasible_role_assignment_count,
|
|
1112
|
+
**(
|
|
1113
|
+
{}
|
|
1114
|
+
if self.memory_dose_assessment is None
|
|
1115
|
+
else {
|
|
1116
|
+
"memory_dose_assessment": self.memory_dose_assessment.to_record()
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
"claim_scope": (
|
|
1120
|
+
"replayable_prior_only_allocation_not_efficacy_or_outcome_claim"
|
|
1121
|
+
),
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
@property
|
|
1125
|
+
def decision_sha256(self) -> str:
|
|
1126
|
+
return _hash(_DECISION_DOMAIN, self._unsigned_record())
|
|
1127
|
+
|
|
1128
|
+
def to_record(self) -> dict[str, object]:
|
|
1129
|
+
return {
|
|
1130
|
+
**self._unsigned_record(),
|
|
1131
|
+
"decision_sha256": self.decision_sha256,
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
def __eq__(self, other: object) -> bool:
|
|
1135
|
+
return (
|
|
1136
|
+
type(other) is StructuralPosteriorSlateDecision
|
|
1137
|
+
and self.decision_sha256 == other.decision_sha256
|
|
1138
|
+
)
|
|
1139
|
+
|
|
1140
|
+
__hash__ = None
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1144
|
+
class OperatorStratifiedStructuralPosteriorSlateDecision(
|
|
1145
|
+
StructuralPosteriorSlateDecision
|
|
1146
|
+
):
|
|
1147
|
+
"""Structural-posterior decision with authenticated assay minima."""
|
|
1148
|
+
|
|
1149
|
+
required_family_minimums: tuple[tuple[str, int], ...] = (
|
|
1150
|
+
(COMPOSITE_OPTION_FAMILY, 1),
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
policy_id: ClassVar[str] = OPERATOR_STRATIFIED_POLICY_ID
|
|
1154
|
+
policy_version: ClassVar[int] = OPERATOR_STRATIFIED_POLICY_VERSION
|
|
1155
|
+
policy_definition_sha256: ClassVar[str] = (
|
|
1156
|
+
OPERATOR_STRATIFIED_POLICY_DEFINITION_SHA256
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
def __post_init__(self) -> None:
|
|
1160
|
+
if type(self) is not OperatorStratifiedStructuralPosteriorSlateDecision:
|
|
1161
|
+
raise TypeError("decision must be exact operator-stratified decision")
|
|
1162
|
+
_validate_required_family_minimums(self.required_family_minimums)
|
|
1163
|
+
_validate_structural_decision(
|
|
1164
|
+
self,
|
|
1165
|
+
required_family_minimums=self.required_family_minimums,
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
def revalidate(self) -> None:
|
|
1169
|
+
if type(self) is not OperatorStratifiedStructuralPosteriorSlateDecision:
|
|
1170
|
+
raise TypeError("decision must be exact operator-stratified decision")
|
|
1171
|
+
OperatorStratifiedStructuralPosteriorSlateDecision.__post_init__(self)
|
|
1172
|
+
|
|
1173
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1174
|
+
record = StructuralPosteriorSlateDecision._unsigned_record(self)
|
|
1175
|
+
return {
|
|
1176
|
+
**record,
|
|
1177
|
+
"schema_version": 2,
|
|
1178
|
+
"event_type": "operator_stratified_structural_posterior_allocated",
|
|
1179
|
+
"operator_stratification": {
|
|
1180
|
+
"required_family_minimums": [
|
|
1181
|
+
{"family": family, "minimum_evaluations": minimum}
|
|
1182
|
+
for family, minimum in self.required_family_minimums
|
|
1183
|
+
],
|
|
1184
|
+
"semantics": "assay_exposure_not_quality_prior",
|
|
1185
|
+
"outcomes_consulted": False,
|
|
1186
|
+
},
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
@property
|
|
1190
|
+
def decision_sha256(self) -> str:
|
|
1191
|
+
return _hash(
|
|
1192
|
+
_OPERATOR_STRATIFIED_DECISION_DOMAIN,
|
|
1193
|
+
self._unsigned_record(),
|
|
1194
|
+
)
|
|
1195
|
+
|
|
1196
|
+
def __eq__(self, other: object) -> bool:
|
|
1197
|
+
return (
|
|
1198
|
+
type(other) is OperatorStratifiedStructuralPosteriorSlateDecision
|
|
1199
|
+
and self.decision_sha256 == other.decision_sha256
|
|
1200
|
+
)
|
|
1201
|
+
|
|
1202
|
+
__hash__ = None
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1206
|
+
class HorizonBoundedStructuralPosteriorSlateDecision(
|
|
1207
|
+
StructuralPosteriorSlateDecision
|
|
1208
|
+
):
|
|
1209
|
+
"""Replayable allocation under an authenticated finite-horizon schedule."""
|
|
1210
|
+
|
|
1211
|
+
family_exposure_phases: tuple[FamilyExposurePhase, ...] = ()
|
|
1212
|
+
family_exposure_violation_count: int = 0
|
|
1213
|
+
|
|
1214
|
+
policy_id: ClassVar[str] = HORIZON_BOUNDED_POLICY_ID
|
|
1215
|
+
policy_version: ClassVar[int] = HORIZON_BOUNDED_POLICY_VERSION
|
|
1216
|
+
policy_definition_sha256: ClassVar[str] = (
|
|
1217
|
+
HORIZON_BOUNDED_POLICY_DEFINITION_SHA256
|
|
1218
|
+
)
|
|
1219
|
+
|
|
1220
|
+
def __post_init__(self) -> None:
|
|
1221
|
+
if type(self) is not HorizonBoundedStructuralPosteriorSlateDecision:
|
|
1222
|
+
raise TypeError("decision must be exact horizon-bounded decision")
|
|
1223
|
+
_validate_family_exposure_phases(self.family_exposure_phases)
|
|
1224
|
+
active = _active_family_exposure_phase(
|
|
1225
|
+
self.family_exposure_phases,
|
|
1226
|
+
wave_index=self.request.slate.wave_index,
|
|
1227
|
+
)
|
|
1228
|
+
expected = _validate_structural_decision(
|
|
1229
|
+
self,
|
|
1230
|
+
required_family_bounds=active.bounds,
|
|
1231
|
+
minimize_family_bound_violation=True,
|
|
1232
|
+
)
|
|
1233
|
+
if (
|
|
1234
|
+
type(self.family_exposure_violation_count) is not int
|
|
1235
|
+
or self.family_exposure_violation_count < 0
|
|
1236
|
+
):
|
|
1237
|
+
raise ValueError(
|
|
1238
|
+
"family_exposure_violation_count must be a non-negative integer"
|
|
1239
|
+
)
|
|
1240
|
+
if self.family_exposure_violation_count != (
|
|
1241
|
+
expected.family_exposure_violation_count
|
|
1242
|
+
):
|
|
1243
|
+
raise ValueError("family exposure recourse differs from exact replay")
|
|
1244
|
+
|
|
1245
|
+
def revalidate(self) -> None:
|
|
1246
|
+
if type(self) is not HorizonBoundedStructuralPosteriorSlateDecision:
|
|
1247
|
+
raise TypeError("decision must be exact horizon-bounded decision")
|
|
1248
|
+
HorizonBoundedStructuralPosteriorSlateDecision.__post_init__(self)
|
|
1249
|
+
|
|
1250
|
+
@property
|
|
1251
|
+
def active_exposure_phase(self) -> FamilyExposurePhase:
|
|
1252
|
+
self.revalidate()
|
|
1253
|
+
return _active_family_exposure_phase(
|
|
1254
|
+
self.family_exposure_phases,
|
|
1255
|
+
wave_index=self.request.slate.wave_index,
|
|
1256
|
+
)
|
|
1257
|
+
|
|
1258
|
+
@property
|
|
1259
|
+
def policy_configuration_sha256(self) -> str:
|
|
1260
|
+
self.revalidate()
|
|
1261
|
+
return _hash(
|
|
1262
|
+
_HORIZON_BOUNDED_CONFIGURATION_DOMAIN,
|
|
1263
|
+
_horizon_bounded_configuration_record(self.family_exposure_phases),
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1267
|
+
record = StructuralPosteriorSlateDecision._unsigned_record(self)
|
|
1268
|
+
active = self.active_exposure_phase
|
|
1269
|
+
family_by_id = {
|
|
1270
|
+
value.option_id: value.family for value in self.request.slate.members
|
|
1271
|
+
}
|
|
1272
|
+
applied_counts = {
|
|
1273
|
+
bound.family: sum(
|
|
1274
|
+
family_by_id[value.option_id] == bound.family
|
|
1275
|
+
for value in self.selected
|
|
1276
|
+
)
|
|
1277
|
+
for bound in active.bounds
|
|
1278
|
+
}
|
|
1279
|
+
return {
|
|
1280
|
+
**record,
|
|
1281
|
+
"schema_version": 3,
|
|
1282
|
+
"event_type": "horizon_bounded_structural_posterior_allocated",
|
|
1283
|
+
"policy_configuration_sha256": self.policy_configuration_sha256,
|
|
1284
|
+
"finite_horizon_exposure": {
|
|
1285
|
+
"family_exposure_phases": [
|
|
1286
|
+
value.to_record() for value in self.family_exposure_phases
|
|
1287
|
+
],
|
|
1288
|
+
"active_phase": active.to_record(),
|
|
1289
|
+
"applied_family_counts": applied_counts,
|
|
1290
|
+
"family_exposure_violation_count": (
|
|
1291
|
+
self.family_exposure_violation_count
|
|
1292
|
+
),
|
|
1293
|
+
"requested_bounds_satisfied": (
|
|
1294
|
+
self.family_exposure_violation_count == 0
|
|
1295
|
+
),
|
|
1296
|
+
"structural_recourse": "minimum_l1_violation_then_role_score",
|
|
1297
|
+
"phase_authority": "sealed_wave_index",
|
|
1298
|
+
"semantics": "assay_exposure_not_quality_prior",
|
|
1299
|
+
"outcomes_consulted": False,
|
|
1300
|
+
},
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
@property
|
|
1304
|
+
def decision_sha256(self) -> str:
|
|
1305
|
+
return _hash(
|
|
1306
|
+
_HORIZON_BOUNDED_DECISION_DOMAIN,
|
|
1307
|
+
self._unsigned_record(),
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
def __eq__(self, other: object) -> bool:
|
|
1311
|
+
return (
|
|
1312
|
+
type(other) is HorizonBoundedStructuralPosteriorSlateDecision
|
|
1313
|
+
and self.decision_sha256 == other.decision_sha256
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
__hash__ = None
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
@dataclass(frozen=True, slots=True)
|
|
1320
|
+
class StructuralPosteriorSlatePolicy:
|
|
1321
|
+
"""Allocate K8 to K4 by exact prior-only role assignment."""
|
|
1322
|
+
|
|
1323
|
+
policy_id: ClassVar[str] = POLICY_ID
|
|
1324
|
+
policy_version: ClassVar[int] = POLICY_VERSION
|
|
1325
|
+
definition_sha256: ClassVar[str] = POLICY_DEFINITION_SHA256
|
|
1326
|
+
|
|
1327
|
+
def __post_init__(self) -> None:
|
|
1328
|
+
# The v1 mechanism deliberately has no workload-tuned parameters.
|
|
1329
|
+
if type(self) is not StructuralPosteriorSlatePolicy:
|
|
1330
|
+
raise TypeError("policy must be exact StructuralPosteriorSlatePolicy")
|
|
1331
|
+
|
|
1332
|
+
@property
|
|
1333
|
+
def configuration_sha256(self) -> str:
|
|
1334
|
+
self.__post_init__()
|
|
1335
|
+
return _hash(_CONFIGURATION_DOMAIN, _configuration_record())
|
|
1336
|
+
|
|
1337
|
+
def select(
|
|
1338
|
+
self,
|
|
1339
|
+
request: SlateAllocationRequest,
|
|
1340
|
+
) -> StructuralPosteriorSlateDecision:
|
|
1341
|
+
self.__post_init__()
|
|
1342
|
+
if type(request) is not SlateAllocationRequest:
|
|
1343
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
1344
|
+
request.revalidate()
|
|
1345
|
+
_validate_request(request)
|
|
1346
|
+
rows = tuple(_score_member(request, member) for member in request.slate.members)
|
|
1347
|
+
assignment = _best_assignment(request, rows)
|
|
1348
|
+
return StructuralPosteriorSlateDecision(
|
|
1349
|
+
request=request,
|
|
1350
|
+
score_rows=rows,
|
|
1351
|
+
selected=assignment.selected,
|
|
1352
|
+
joint_score=assignment.joint_score,
|
|
1353
|
+
diversity_score=assignment.diversity_score,
|
|
1354
|
+
distinct_family_count=assignment.distinct_family_count,
|
|
1355
|
+
distinct_locus_count=assignment.distinct_locus_count,
|
|
1356
|
+
distinct_phenotype_count=assignment.distinct_phenotype_count,
|
|
1357
|
+
administered_card_keys=assignment.administered_card_keys,
|
|
1358
|
+
feasible_role_assignment_count=(
|
|
1359
|
+
assignment.feasible_role_assignment_count
|
|
1360
|
+
),
|
|
1361
|
+
memory_dose_assessment=assignment.memory_dose_assessment,
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
def to_record(self) -> dict[str, object]:
|
|
1365
|
+
self.__post_init__()
|
|
1366
|
+
return {
|
|
1367
|
+
"policy_id": self.policy_id,
|
|
1368
|
+
"policy_version": self.policy_version,
|
|
1369
|
+
"definition_sha256": self.definition_sha256,
|
|
1370
|
+
"configuration": _configuration_record(),
|
|
1371
|
+
"configuration_sha256": self.configuration_sha256,
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
@dataclass(frozen=True, slots=True)
|
|
1376
|
+
class OperatorStratifiedStructuralPosteriorSlatePolicy:
|
|
1377
|
+
"""Reserve evaluator assay exposure while preserving prior-only ranking."""
|
|
1378
|
+
|
|
1379
|
+
required_family_minimums: tuple[tuple[str, int], ...] = (
|
|
1380
|
+
(COMPOSITE_OPTION_FAMILY, 1),
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
policy_id: ClassVar[str] = OPERATOR_STRATIFIED_POLICY_ID
|
|
1384
|
+
policy_version: ClassVar[int] = OPERATOR_STRATIFIED_POLICY_VERSION
|
|
1385
|
+
definition_sha256: ClassVar[str] = (
|
|
1386
|
+
OPERATOR_STRATIFIED_POLICY_DEFINITION_SHA256
|
|
1387
|
+
)
|
|
1388
|
+
|
|
1389
|
+
def __post_init__(self) -> None:
|
|
1390
|
+
if type(self) is not OperatorStratifiedStructuralPosteriorSlatePolicy:
|
|
1391
|
+
raise TypeError("policy must be exact operator-stratified policy")
|
|
1392
|
+
_validate_required_family_minimums(self.required_family_minimums)
|
|
1393
|
+
|
|
1394
|
+
@property
|
|
1395
|
+
def configuration_sha256(self) -> str:
|
|
1396
|
+
self.__post_init__()
|
|
1397
|
+
return _hash(
|
|
1398
|
+
_OPERATOR_STRATIFIED_CONFIGURATION_DOMAIN,
|
|
1399
|
+
self._configuration_record(),
|
|
1400
|
+
)
|
|
1401
|
+
|
|
1402
|
+
def _configuration_record(self) -> dict[str, object]:
|
|
1403
|
+
return {
|
|
1404
|
+
"schema_version": 1,
|
|
1405
|
+
"base_allocator": {
|
|
1406
|
+
"policy_id": POLICY_ID,
|
|
1407
|
+
"policy_version": POLICY_VERSION,
|
|
1408
|
+
"definition_sha256": POLICY_DEFINITION_SHA256,
|
|
1409
|
+
"configuration_sha256": _hash(
|
|
1410
|
+
_CONFIGURATION_DOMAIN,
|
|
1411
|
+
_configuration_record(),
|
|
1412
|
+
),
|
|
1413
|
+
},
|
|
1414
|
+
"required_family_minimums": [
|
|
1415
|
+
{"family": family, "minimum_evaluations": minimum}
|
|
1416
|
+
for family, minimum in self.required_family_minimums
|
|
1417
|
+
],
|
|
1418
|
+
"constraint_semantics": "assay_exposure_not_quality_prior",
|
|
1419
|
+
"outcomes_consulted": False,
|
|
1420
|
+
"benchmark_specific_parameters": [],
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
def select(
|
|
1424
|
+
self,
|
|
1425
|
+
request: SlateAllocationRequest,
|
|
1426
|
+
) -> OperatorStratifiedStructuralPosteriorSlateDecision:
|
|
1427
|
+
self.__post_init__()
|
|
1428
|
+
if type(request) is not SlateAllocationRequest:
|
|
1429
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
1430
|
+
request.revalidate()
|
|
1431
|
+
_validate_request(request)
|
|
1432
|
+
rows = tuple(_score_member(request, member) for member in request.slate.members)
|
|
1433
|
+
assignment = _best_assignment(
|
|
1434
|
+
request,
|
|
1435
|
+
rows,
|
|
1436
|
+
required_family_minimums=self.required_family_minimums,
|
|
1437
|
+
)
|
|
1438
|
+
return OperatorStratifiedStructuralPosteriorSlateDecision(
|
|
1439
|
+
request=request,
|
|
1440
|
+
score_rows=rows,
|
|
1441
|
+
selected=assignment.selected,
|
|
1442
|
+
joint_score=assignment.joint_score,
|
|
1443
|
+
diversity_score=assignment.diversity_score,
|
|
1444
|
+
distinct_family_count=assignment.distinct_family_count,
|
|
1445
|
+
distinct_locus_count=assignment.distinct_locus_count,
|
|
1446
|
+
distinct_phenotype_count=assignment.distinct_phenotype_count,
|
|
1447
|
+
administered_card_keys=assignment.administered_card_keys,
|
|
1448
|
+
feasible_role_assignment_count=(
|
|
1449
|
+
assignment.feasible_role_assignment_count
|
|
1450
|
+
),
|
|
1451
|
+
memory_dose_assessment=assignment.memory_dose_assessment,
|
|
1452
|
+
required_family_minimums=self.required_family_minimums,
|
|
1453
|
+
)
|
|
1454
|
+
|
|
1455
|
+
def to_record(self) -> dict[str, object]:
|
|
1456
|
+
self.__post_init__()
|
|
1457
|
+
return {
|
|
1458
|
+
"policy_id": self.policy_id,
|
|
1459
|
+
"policy_version": self.policy_version,
|
|
1460
|
+
"definition_sha256": self.definition_sha256,
|
|
1461
|
+
"configuration": self._configuration_record(),
|
|
1462
|
+
"configuration_sha256": self.configuration_sha256,
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
@dataclass(frozen=True, slots=True)
|
|
1467
|
+
class HorizonBoundedStructuralPosteriorSlatePolicy:
|
|
1468
|
+
"""Apply pre-registered action-family exposure bounds by campaign phase."""
|
|
1469
|
+
|
|
1470
|
+
family_exposure_phases: tuple[FamilyExposurePhase, ...]
|
|
1471
|
+
|
|
1472
|
+
policy_id: ClassVar[str] = HORIZON_BOUNDED_POLICY_ID
|
|
1473
|
+
policy_version: ClassVar[int] = HORIZON_BOUNDED_POLICY_VERSION
|
|
1474
|
+
definition_sha256: ClassVar[str] = HORIZON_BOUNDED_POLICY_DEFINITION_SHA256
|
|
1475
|
+
|
|
1476
|
+
def __post_init__(self) -> None:
|
|
1477
|
+
if type(self) is not HorizonBoundedStructuralPosteriorSlatePolicy:
|
|
1478
|
+
raise TypeError("policy must be exact horizon-bounded policy")
|
|
1479
|
+
_validate_family_exposure_phases(self.family_exposure_phases)
|
|
1480
|
+
|
|
1481
|
+
@property
|
|
1482
|
+
def configuration_sha256(self) -> str:
|
|
1483
|
+
self.__post_init__()
|
|
1484
|
+
return _hash(
|
|
1485
|
+
_HORIZON_BOUNDED_CONFIGURATION_DOMAIN,
|
|
1486
|
+
self._configuration_record(),
|
|
1487
|
+
)
|
|
1488
|
+
|
|
1489
|
+
def _configuration_record(self) -> dict[str, object]:
|
|
1490
|
+
return _horizon_bounded_configuration_record(self.family_exposure_phases)
|
|
1491
|
+
|
|
1492
|
+
def exposure_phase_for_wave(self, wave_index: int) -> FamilyExposurePhase:
|
|
1493
|
+
self.__post_init__()
|
|
1494
|
+
return _active_family_exposure_phase(
|
|
1495
|
+
self.family_exposure_phases,
|
|
1496
|
+
wave_index=wave_index,
|
|
1497
|
+
)
|
|
1498
|
+
|
|
1499
|
+
def select(
|
|
1500
|
+
self,
|
|
1501
|
+
request: SlateAllocationRequest,
|
|
1502
|
+
) -> HorizonBoundedStructuralPosteriorSlateDecision:
|
|
1503
|
+
self.__post_init__()
|
|
1504
|
+
if type(request) is not SlateAllocationRequest:
|
|
1505
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
1506
|
+
request.revalidate()
|
|
1507
|
+
_validate_request(request)
|
|
1508
|
+
active = self.exposure_phase_for_wave(request.slate.wave_index)
|
|
1509
|
+
rows = tuple(_score_member(request, member) for member in request.slate.members)
|
|
1510
|
+
assignment = _best_assignment(
|
|
1511
|
+
request,
|
|
1512
|
+
rows,
|
|
1513
|
+
required_family_bounds=active.bounds,
|
|
1514
|
+
minimize_family_bound_violation=True,
|
|
1515
|
+
)
|
|
1516
|
+
return HorizonBoundedStructuralPosteriorSlateDecision(
|
|
1517
|
+
request=request,
|
|
1518
|
+
score_rows=rows,
|
|
1519
|
+
selected=assignment.selected,
|
|
1520
|
+
joint_score=assignment.joint_score,
|
|
1521
|
+
diversity_score=assignment.diversity_score,
|
|
1522
|
+
distinct_family_count=assignment.distinct_family_count,
|
|
1523
|
+
distinct_locus_count=assignment.distinct_locus_count,
|
|
1524
|
+
distinct_phenotype_count=assignment.distinct_phenotype_count,
|
|
1525
|
+
administered_card_keys=assignment.administered_card_keys,
|
|
1526
|
+
feasible_role_assignment_count=(
|
|
1527
|
+
assignment.feasible_role_assignment_count
|
|
1528
|
+
),
|
|
1529
|
+
memory_dose_assessment=assignment.memory_dose_assessment,
|
|
1530
|
+
family_exposure_phases=self.family_exposure_phases,
|
|
1531
|
+
family_exposure_violation_count=(
|
|
1532
|
+
assignment.family_exposure_violation_count
|
|
1533
|
+
),
|
|
1534
|
+
)
|
|
1535
|
+
|
|
1536
|
+
def to_record(self) -> dict[str, object]:
|
|
1537
|
+
self.__post_init__()
|
|
1538
|
+
return {
|
|
1539
|
+
"policy_id": self.policy_id,
|
|
1540
|
+
"policy_version": self.policy_version,
|
|
1541
|
+
"definition_sha256": self.definition_sha256,
|
|
1542
|
+
"configuration": self._configuration_record(),
|
|
1543
|
+
"configuration_sha256": self.configuration_sha256,
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
__all__ = [
|
|
1548
|
+
"FamilyExposureBound",
|
|
1549
|
+
"FamilyExposurePhase",
|
|
1550
|
+
"HORIZON_BOUNDED_POLICY_DEFINITION_SHA256",
|
|
1551
|
+
"HORIZON_BOUNDED_POLICY_ID",
|
|
1552
|
+
"HORIZON_BOUNDED_POLICY_VERSION",
|
|
1553
|
+
"HorizonBoundedStructuralPosteriorSlateDecision",
|
|
1554
|
+
"HorizonBoundedStructuralPosteriorSlatePolicy",
|
|
1555
|
+
"POLICY_DEFINITION_SHA256",
|
|
1556
|
+
"POLICY_ID",
|
|
1557
|
+
"POLICY_VERSION",
|
|
1558
|
+
"OPERATOR_STRATIFIED_POLICY_DEFINITION_SHA256",
|
|
1559
|
+
"OPERATOR_STRATIFIED_POLICY_ID",
|
|
1560
|
+
"OPERATOR_STRATIFIED_POLICY_VERSION",
|
|
1561
|
+
"OperatorStratifiedStructuralPosteriorSlateDecision",
|
|
1562
|
+
"OperatorStratifiedStructuralPosteriorSlatePolicy",
|
|
1563
|
+
"StructuralPosteriorAllocatedMember",
|
|
1564
|
+
"StructuralPosteriorMemberScoreRow",
|
|
1565
|
+
"StructuralPosteriorMetricScoreRow",
|
|
1566
|
+
"StructuralPosteriorSlateDecision",
|
|
1567
|
+
"StructuralPosteriorSlatePolicy",
|
|
1568
|
+
"StructuralPosteriorSlateRole",
|
|
1569
|
+
"build_controller_owned_family_exposure_phases",
|
|
1570
|
+
"build_terminal_tapered_family_exposure_phases",
|
|
1571
|
+
]
|