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,1527 @@
|
|
|
1
|
+
"""Generic target-conditioned, evaluator-updated portfolio acquisition.
|
|
2
|
+
|
|
3
|
+
The policy is deliberately split from workload adapters and provider code. A
|
|
4
|
+
caller supplies an authenticated affine frontier target, a portable numeric
|
|
5
|
+
feature row for every member of a sealed slate, a complete realizable-set
|
|
6
|
+
receipt, and an immutable linear-Gaussian state. The policy scores and exactly
|
|
7
|
+
selects one feasible portfolio. A separate generation-barrier update consumes
|
|
8
|
+
outcomes for selected members only.
|
|
9
|
+
|
|
10
|
+
No workload, model, provider, option-name, or evaluator implementation is part
|
|
11
|
+
of the scoring interface. Optional workload knowledge belongs in a separately
|
|
12
|
+
authenticated pre-outcome feature projector, not in this policy.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import hashlib
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from enum import Enum
|
|
20
|
+
from itertools import combinations
|
|
21
|
+
import json
|
|
22
|
+
import math
|
|
23
|
+
import re
|
|
24
|
+
from statistics import fmean
|
|
25
|
+
from typing import ClassVar, Sequence
|
|
26
|
+
|
|
27
|
+
from agent_evolve.domain.patch import require_sha256
|
|
28
|
+
from agent_evolve.policies.selection.calibrated_slate import (
|
|
29
|
+
CalibratedSlateMember,
|
|
30
|
+
SlateAllocationRequest,
|
|
31
|
+
assess_allocated_slate_memory_dose,
|
|
32
|
+
)
|
|
33
|
+
from agent_evolve.ports.frontier_target import CampaignPortfolioFrontierTarget
|
|
34
|
+
from agent_evolve.ports.portfolio_memory_dose import PortfolioMemoryDoseAssessment
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
POLICY_ID = "target_conditioned_prequential_realizable_portfolio"
|
|
38
|
+
POLICY_VERSION = 1
|
|
39
|
+
POLICY_DEFINITION_SHA256 = hashlib.sha256(
|
|
40
|
+
b"agent-evolve:target-conditioned-prequential-realizable-portfolio:v1;"
|
|
41
|
+
b"input=sealed-slate,prior-only-affine-target,portable-features,"
|
|
42
|
+
b"immutable-linear-gaussian-state,complete-realizable-set;"
|
|
43
|
+
b"heads=normalized-marginal-archive-utility,target-achievement;"
|
|
44
|
+
b"scoring=within-slate-z-marginal-plus-bounded-direction-plus-"
|
|
45
|
+
b"finite-horizon-epistemic;selection=exact-set-sum-canonical-ties;"
|
|
46
|
+
b"updates=selected-outcomes-only-at-generation-barrier;"
|
|
47
|
+
b"workload-model-provider-option-name-fields=false"
|
|
48
|
+
).hexdigest()
|
|
49
|
+
|
|
50
|
+
BASE_REALIZABILITY_PROJECTOR_ID = "sealed_slate_base_realizability"
|
|
51
|
+
BASE_REALIZABILITY_PROJECTOR_VERSION = 1
|
|
52
|
+
BASE_REALIZABILITY_DEFINITION_SHA256 = hashlib.sha256(
|
|
53
|
+
b"agent-evolve:sealed-slate-base-realizability:v1;"
|
|
54
|
+
b"constraints=cardinality,required-members,pairwise-compatibility,"
|
|
55
|
+
b"minimum-distinct-families,bounded-memory-dose;"
|
|
56
|
+
b"enumeration=complete;ties=canonical-option-id-set"
|
|
57
|
+
).hexdigest()
|
|
58
|
+
|
|
59
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
60
|
+
_FEATURE = re.compile(r"^[a-z][a-z0-9_]{0,127}$")
|
|
61
|
+
_HEAD_DOMAIN = b"agent-evolve:prequential-linear-gaussian-head:v1\x00"
|
|
62
|
+
_META_PRIOR_DOMAIN = b"agent-evolve:trap-portable-meta-prior:v1\x00"
|
|
63
|
+
_STATE_DOMAIN = b"agent-evolve:target-conditioned-acquisition-state:v1\x00"
|
|
64
|
+
_PROFILE_DOMAIN = b"agent-evolve:target-conditioned-acquisition-profile:v1\x00"
|
|
65
|
+
_REALIZABLE_DOMAIN = b"agent-evolve:realizable-portfolio-set:v1\x00"
|
|
66
|
+
_FEATURE_ROW_DOMAIN = b"agent-evolve:target-conditioned-feature-row:v1\x00"
|
|
67
|
+
_REQUEST_DOMAIN = b"agent-evolve:target-conditioned-slate-request:v1\x00"
|
|
68
|
+
_DECISION_DOMAIN = b"agent-evolve:target-conditioned-slate-decision:v1\x00"
|
|
69
|
+
_OBSERVATION_DOMAIN = b"agent-evolve:target-conditioned-observation:v1\x00"
|
|
70
|
+
_UPDATE_DOMAIN = b"agent-evolve:target-conditioned-state-update:v1\x00"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _canonical_json(value: object) -> bytes:
|
|
74
|
+
return json.dumps(
|
|
75
|
+
value,
|
|
76
|
+
allow_nan=False,
|
|
77
|
+
ensure_ascii=True,
|
|
78
|
+
separators=(",", ":"),
|
|
79
|
+
sort_keys=True,
|
|
80
|
+
).encode("ascii", errors="strict")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
84
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _finite(value: float, *, name: str) -> None:
|
|
88
|
+
if type(value) is not float or not math.isfinite(value):
|
|
89
|
+
raise TypeError(f"{name} must be an exact finite float")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _hex_float(value: object, *, name: str) -> float:
|
|
93
|
+
if type(value) is not str:
|
|
94
|
+
raise TypeError(f"{name} must be a binary64 hexadecimal string")
|
|
95
|
+
try:
|
|
96
|
+
result = float.fromhex(value)
|
|
97
|
+
except ValueError as error:
|
|
98
|
+
raise ValueError(f"{name} is not valid binary64 hexadecimal text") from error
|
|
99
|
+
_finite(result, name=name)
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _exact_record_keys(
|
|
104
|
+
value: object, *, expected: set[str], name: str
|
|
105
|
+
) -> dict[str, object]:
|
|
106
|
+
if type(value) is not dict:
|
|
107
|
+
raise TypeError(f"{name} must be an exact JSON object")
|
|
108
|
+
keys = set(value)
|
|
109
|
+
if keys != expected:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"{name} fields differ from the closed schema: "
|
|
112
|
+
f"missing={sorted(expected - keys)}, extra={sorted(keys - expected)}"
|
|
113
|
+
)
|
|
114
|
+
return value
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _feature_names(values: tuple[str, ...]) -> None:
|
|
118
|
+
if type(values) is not tuple or not values:
|
|
119
|
+
raise ValueError("feature_names must be a non-empty exact tuple")
|
|
120
|
+
if any(type(value) is not str or _FEATURE.fullmatch(value) is None for value in values):
|
|
121
|
+
raise ValueError("feature names must use the closed lowercase grammar")
|
|
122
|
+
if values != tuple(dict.fromkeys(values)):
|
|
123
|
+
raise ValueError("feature names must be unique and ordered")
|
|
124
|
+
if "bias" not in values:
|
|
125
|
+
raise ValueError("feature schema must include bias")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _cholesky(matrix: tuple[tuple[float, ...], ...]) -> tuple[tuple[float, ...], ...]:
|
|
129
|
+
size = len(matrix)
|
|
130
|
+
lower = [[0.0 for _ in range(size)] for _ in range(size)]
|
|
131
|
+
for row in range(size):
|
|
132
|
+
for column in range(row + 1):
|
|
133
|
+
value = matrix[row][column] - sum(
|
|
134
|
+
lower[row][index] * lower[column][index]
|
|
135
|
+
for index in range(column)
|
|
136
|
+
)
|
|
137
|
+
if row == column:
|
|
138
|
+
if value <= 1e-14 or not math.isfinite(value):
|
|
139
|
+
raise ValueError("head precision must be positive definite")
|
|
140
|
+
lower[row][column] = math.sqrt(value)
|
|
141
|
+
else:
|
|
142
|
+
lower[row][column] = value / lower[column][column]
|
|
143
|
+
return tuple(tuple(value for value in row) for row in lower)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _solve_cholesky(
|
|
147
|
+
lower: tuple[tuple[float, ...], ...], rhs: Sequence[float]
|
|
148
|
+
) -> tuple[float, ...]:
|
|
149
|
+
size = len(lower)
|
|
150
|
+
forward = [0.0 for _ in range(size)]
|
|
151
|
+
for row in range(size):
|
|
152
|
+
forward[row] = (
|
|
153
|
+
float(rhs[row])
|
|
154
|
+
- sum(lower[row][index] * forward[index] for index in range(row))
|
|
155
|
+
) / lower[row][row]
|
|
156
|
+
result = [0.0 for _ in range(size)]
|
|
157
|
+
for row in range(size - 1, -1, -1):
|
|
158
|
+
result[row] = (
|
|
159
|
+
forward[row]
|
|
160
|
+
- sum(
|
|
161
|
+
lower[index][row] * result[index]
|
|
162
|
+
for index in range(row + 1, size)
|
|
163
|
+
)
|
|
164
|
+
) / lower[row][row]
|
|
165
|
+
return tuple(result)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@dataclass(frozen=True, slots=True)
|
|
169
|
+
class _HeadProjection:
|
|
170
|
+
head: PrequentialLinearGaussianHead
|
|
171
|
+
lower: tuple[tuple[float, ...], ...]
|
|
172
|
+
coefficients: tuple[float, ...]
|
|
173
|
+
|
|
174
|
+
def standardized(self, values: Sequence[float]) -> tuple[float, ...]:
|
|
175
|
+
if len(values) != len(self.head.feature_names):
|
|
176
|
+
raise ValueError("feature vector differs from the head schema")
|
|
177
|
+
return tuple(
|
|
178
|
+
(float(value) - mean) / scale
|
|
179
|
+
for value, mean, scale in zip(
|
|
180
|
+
values, self.head.means, self.head.scales, strict=True
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def predict(self, values: Sequence[float]) -> float:
|
|
185
|
+
row = self.standardized(values)
|
|
186
|
+
return sum(
|
|
187
|
+
value * coefficient
|
|
188
|
+
for value, coefficient in zip(row, self.coefficients, strict=True)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def uncertainty(self, values: Sequence[float]) -> float:
|
|
192
|
+
row = self.standardized(values)
|
|
193
|
+
# x' P^-1 x == ||L^-1 x||^2 for P = L L'.
|
|
194
|
+
whitened: list[float] = []
|
|
195
|
+
for index in range(len(row)):
|
|
196
|
+
whitened.append(
|
|
197
|
+
(
|
|
198
|
+
row[index]
|
|
199
|
+
- sum(
|
|
200
|
+
self.lower[index][prior] * whitened[prior]
|
|
201
|
+
for prior in range(index)
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
/ self.lower[index][index]
|
|
205
|
+
)
|
|
206
|
+
leverage = max(0.0, sum(value * value for value in whitened))
|
|
207
|
+
return math.sqrt(self.head.residual_variance * leverage)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
211
|
+
class PrequentialLinearGaussianHead:
|
|
212
|
+
"""Serializable regularized head represented by sufficient statistics."""
|
|
213
|
+
|
|
214
|
+
feature_names: tuple[str, ...]
|
|
215
|
+
means: tuple[float, ...]
|
|
216
|
+
scales: tuple[float, ...]
|
|
217
|
+
precision: tuple[tuple[float, ...], ...]
|
|
218
|
+
rhs: tuple[float, ...]
|
|
219
|
+
residual_variance: float
|
|
220
|
+
|
|
221
|
+
def __post_init__(self) -> None:
|
|
222
|
+
_feature_names(self.feature_names)
|
|
223
|
+
size = len(self.feature_names)
|
|
224
|
+
for name, values in (
|
|
225
|
+
("means", self.means),
|
|
226
|
+
("scales", self.scales),
|
|
227
|
+
("rhs", self.rhs),
|
|
228
|
+
):
|
|
229
|
+
if type(values) is not tuple or len(values) != size:
|
|
230
|
+
raise ValueError(f"{name} must match the feature schema")
|
|
231
|
+
for value in values:
|
|
232
|
+
_finite(value, name=name)
|
|
233
|
+
if any(value <= 0.0 for value in self.scales):
|
|
234
|
+
raise ValueError("feature scales must be strictly positive")
|
|
235
|
+
if type(self.precision) is not tuple or len(self.precision) != size:
|
|
236
|
+
raise ValueError("precision must be a square exact tuple matrix")
|
|
237
|
+
for row in self.precision:
|
|
238
|
+
if type(row) is not tuple or len(row) != size:
|
|
239
|
+
raise ValueError("precision must be a square exact tuple matrix")
|
|
240
|
+
for value in row:
|
|
241
|
+
_finite(value, name="precision")
|
|
242
|
+
for row in range(size):
|
|
243
|
+
for column in range(row):
|
|
244
|
+
if not math.isclose(
|
|
245
|
+
self.precision[row][column],
|
|
246
|
+
self.precision[column][row],
|
|
247
|
+
rel_tol=0.0,
|
|
248
|
+
abs_tol=1e-10,
|
|
249
|
+
):
|
|
250
|
+
raise ValueError("precision must be symmetric")
|
|
251
|
+
_finite(self.residual_variance, name="residual_variance")
|
|
252
|
+
if self.residual_variance <= 0.0:
|
|
253
|
+
raise ValueError("residual_variance must be strictly positive")
|
|
254
|
+
_cholesky(self.precision)
|
|
255
|
+
|
|
256
|
+
def project(self) -> _HeadProjection:
|
|
257
|
+
self.__post_init__()
|
|
258
|
+
lower = _cholesky(self.precision)
|
|
259
|
+
return _HeadProjection(
|
|
260
|
+
head=self,
|
|
261
|
+
lower=lower,
|
|
262
|
+
coefficients=_solve_cholesky(lower, self.rhs),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
def update(
|
|
266
|
+
self,
|
|
267
|
+
rows: Sequence[Sequence[float]],
|
|
268
|
+
targets: Sequence[float],
|
|
269
|
+
) -> PrequentialLinearGaussianHead:
|
|
270
|
+
self.__post_init__()
|
|
271
|
+
if not rows or len(rows) != len(targets):
|
|
272
|
+
raise ValueError("head update requires aligned non-empty observations")
|
|
273
|
+
projection = self.project()
|
|
274
|
+
design = [projection.standardized(row) for row in rows]
|
|
275
|
+
for target in targets:
|
|
276
|
+
_finite(float(target), name="target")
|
|
277
|
+
size = len(self.feature_names)
|
|
278
|
+
precision = [list(row) for row in self.precision]
|
|
279
|
+
rhs = list(self.rhs)
|
|
280
|
+
for row, target in zip(design, targets, strict=True):
|
|
281
|
+
for left in range(size):
|
|
282
|
+
rhs[left] += row[left] * float(target)
|
|
283
|
+
for right in range(size):
|
|
284
|
+
precision[left][right] += row[left] * row[right]
|
|
285
|
+
return PrequentialLinearGaussianHead(
|
|
286
|
+
feature_names=self.feature_names,
|
|
287
|
+
means=self.means,
|
|
288
|
+
scales=self.scales,
|
|
289
|
+
precision=tuple(tuple(value for value in row) for row in precision),
|
|
290
|
+
rhs=tuple(rhs),
|
|
291
|
+
residual_variance=self.residual_variance,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
295
|
+
self.__post_init__()
|
|
296
|
+
return {
|
|
297
|
+
"schema_version": 1,
|
|
298
|
+
"feature_names": list(self.feature_names),
|
|
299
|
+
"means_hex": [value.hex() for value in self.means],
|
|
300
|
+
"scales_hex": [value.hex() for value in self.scales],
|
|
301
|
+
"precision_hex": [
|
|
302
|
+
[value.hex() for value in row] for row in self.precision
|
|
303
|
+
],
|
|
304
|
+
"rhs_hex": [value.hex() for value in self.rhs],
|
|
305
|
+
"residual_variance_hex": self.residual_variance.hex(),
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def head_sha256(self) -> str:
|
|
310
|
+
return _hash(_HEAD_DOMAIN, self._unsigned_record())
|
|
311
|
+
|
|
312
|
+
def to_record(self) -> dict[str, object]:
|
|
313
|
+
return {**self._unsigned_record(), "head_sha256": self.head_sha256}
|
|
314
|
+
|
|
315
|
+
@classmethod
|
|
316
|
+
def from_record(
|
|
317
|
+
cls, record: object
|
|
318
|
+
) -> PrequentialLinearGaussianHead:
|
|
319
|
+
value = _exact_record_keys(
|
|
320
|
+
record,
|
|
321
|
+
expected={
|
|
322
|
+
"schema_version",
|
|
323
|
+
"feature_names",
|
|
324
|
+
"means_hex",
|
|
325
|
+
"scales_hex",
|
|
326
|
+
"precision_hex",
|
|
327
|
+
"rhs_hex",
|
|
328
|
+
"residual_variance_hex",
|
|
329
|
+
"head_sha256",
|
|
330
|
+
},
|
|
331
|
+
name="linear-Gaussian head record",
|
|
332
|
+
)
|
|
333
|
+
if value["schema_version"] != 1:
|
|
334
|
+
raise ValueError("unsupported linear-Gaussian head schema")
|
|
335
|
+
raw_names = value["feature_names"]
|
|
336
|
+
raw_means = value["means_hex"]
|
|
337
|
+
raw_scales = value["scales_hex"]
|
|
338
|
+
raw_precision = value["precision_hex"]
|
|
339
|
+
raw_rhs = value["rhs_hex"]
|
|
340
|
+
if type(raw_names) is not list or any(type(item) is not str for item in raw_names):
|
|
341
|
+
raise TypeError("feature_names must be an exact JSON string array")
|
|
342
|
+
for name, raw in (
|
|
343
|
+
("means_hex", raw_means),
|
|
344
|
+
("scales_hex", raw_scales),
|
|
345
|
+
("rhs_hex", raw_rhs),
|
|
346
|
+
):
|
|
347
|
+
if type(raw) is not list:
|
|
348
|
+
raise TypeError(f"{name} must be an exact JSON array")
|
|
349
|
+
if type(raw_precision) is not list or any(
|
|
350
|
+
type(row) is not list for row in raw_precision
|
|
351
|
+
):
|
|
352
|
+
raise TypeError("precision_hex must be an exact JSON matrix")
|
|
353
|
+
result = cls(
|
|
354
|
+
feature_names=tuple(raw_names),
|
|
355
|
+
means=tuple(
|
|
356
|
+
_hex_float(item, name=f"means_hex[{index}]")
|
|
357
|
+
for index, item in enumerate(raw_means)
|
|
358
|
+
),
|
|
359
|
+
scales=tuple(
|
|
360
|
+
_hex_float(item, name=f"scales_hex[{index}]")
|
|
361
|
+
for index, item in enumerate(raw_scales)
|
|
362
|
+
),
|
|
363
|
+
precision=tuple(
|
|
364
|
+
tuple(
|
|
365
|
+
_hex_float(
|
|
366
|
+
item,
|
|
367
|
+
name=f"precision_hex[{row_index}][{column_index}]",
|
|
368
|
+
)
|
|
369
|
+
for column_index, item in enumerate(row)
|
|
370
|
+
)
|
|
371
|
+
for row_index, row in enumerate(raw_precision)
|
|
372
|
+
),
|
|
373
|
+
rhs=tuple(
|
|
374
|
+
_hex_float(item, name=f"rhs_hex[{index}]")
|
|
375
|
+
for index, item in enumerate(raw_rhs)
|
|
376
|
+
),
|
|
377
|
+
residual_variance=_hex_float(
|
|
378
|
+
value["residual_variance_hex"],
|
|
379
|
+
name="residual_variance_hex",
|
|
380
|
+
),
|
|
381
|
+
)
|
|
382
|
+
if value["head_sha256"] != result.head_sha256:
|
|
383
|
+
raise ValueError("linear-Gaussian head identity mismatch")
|
|
384
|
+
return result
|
|
385
|
+
|
|
386
|
+
def __eq__(self, other: object) -> bool:
|
|
387
|
+
return (
|
|
388
|
+
type(other) is PrequentialLinearGaussianHead
|
|
389
|
+
and self.head_sha256 == other.head_sha256
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
__hash__ = None
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
396
|
+
class TargetConditionedMetaPrior:
|
|
397
|
+
"""Frozen cross-campaign prior from an authenticated training panel."""
|
|
398
|
+
|
|
399
|
+
training_data_sha256: str
|
|
400
|
+
marginal_head: PrequentialLinearGaussianHead
|
|
401
|
+
direction_head: PrequentialLinearGaussianHead
|
|
402
|
+
|
|
403
|
+
def __post_init__(self) -> None:
|
|
404
|
+
require_sha256(self.training_data_sha256, "training_data_sha256")
|
|
405
|
+
if type(self.marginal_head) is not PrequentialLinearGaussianHead:
|
|
406
|
+
raise TypeError("marginal_head must be exact")
|
|
407
|
+
if type(self.direction_head) is not PrequentialLinearGaussianHead:
|
|
408
|
+
raise TypeError("direction_head must be exact")
|
|
409
|
+
self.marginal_head.__post_init__()
|
|
410
|
+
self.direction_head.__post_init__()
|
|
411
|
+
if self.marginal_head.feature_names != self.direction_head.feature_names:
|
|
412
|
+
raise ValueError("meta-prior heads must share one feature schema")
|
|
413
|
+
if (
|
|
414
|
+
self.marginal_head.means != self.direction_head.means
|
|
415
|
+
or self.marginal_head.scales != self.direction_head.scales
|
|
416
|
+
):
|
|
417
|
+
raise ValueError("meta-prior heads must share one standardizer")
|
|
418
|
+
|
|
419
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
420
|
+
self.__post_init__()
|
|
421
|
+
return {
|
|
422
|
+
"schema_version": 1,
|
|
423
|
+
"training_data_sha256": self.training_data_sha256,
|
|
424
|
+
"marginal_head": self.marginal_head.to_record(),
|
|
425
|
+
"direction_head": self.direction_head.to_record(),
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def meta_prior_sha256(self) -> str:
|
|
430
|
+
return _hash(_META_PRIOR_DOMAIN, self._unsigned_record())
|
|
431
|
+
|
|
432
|
+
def to_record(self) -> dict[str, object]:
|
|
433
|
+
return {
|
|
434
|
+
**self._unsigned_record(),
|
|
435
|
+
"meta_prior_sha256": self.meta_prior_sha256,
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
@classmethod
|
|
439
|
+
def from_record(cls, record: object) -> TargetConditionedMetaPrior:
|
|
440
|
+
value = _exact_record_keys(
|
|
441
|
+
record,
|
|
442
|
+
expected={
|
|
443
|
+
"schema_version",
|
|
444
|
+
"training_data_sha256",
|
|
445
|
+
"marginal_head",
|
|
446
|
+
"direction_head",
|
|
447
|
+
"meta_prior_sha256",
|
|
448
|
+
},
|
|
449
|
+
name="target-conditioned meta-prior record",
|
|
450
|
+
)
|
|
451
|
+
if value["schema_version"] != 1:
|
|
452
|
+
raise ValueError("unsupported target-conditioned meta-prior schema")
|
|
453
|
+
result = cls(
|
|
454
|
+
training_data_sha256=value["training_data_sha256"],
|
|
455
|
+
marginal_head=PrequentialLinearGaussianHead.from_record(
|
|
456
|
+
value["marginal_head"]
|
|
457
|
+
),
|
|
458
|
+
direction_head=PrequentialLinearGaussianHead.from_record(
|
|
459
|
+
value["direction_head"]
|
|
460
|
+
),
|
|
461
|
+
)
|
|
462
|
+
if value["meta_prior_sha256"] != result.meta_prior_sha256:
|
|
463
|
+
raise ValueError("target-conditioned meta-prior identity mismatch")
|
|
464
|
+
return result
|
|
465
|
+
|
|
466
|
+
def initial_state(
|
|
467
|
+
self, *, campaign_scope_sha256: str
|
|
468
|
+
) -> TargetConditionedAcquisitionState:
|
|
469
|
+
self.__post_init__()
|
|
470
|
+
return TargetConditionedAcquisitionState(
|
|
471
|
+
campaign_scope_sha256=campaign_scope_sha256,
|
|
472
|
+
training_data_sha256=self.training_data_sha256,
|
|
473
|
+
marginal_head=self.marginal_head,
|
|
474
|
+
direction_head=self.direction_head,
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
def __eq__(self, other: object) -> bool:
|
|
478
|
+
return (
|
|
479
|
+
type(other) is TargetConditionedMetaPrior
|
|
480
|
+
and self.meta_prior_sha256 == other.meta_prior_sha256
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
__hash__ = None
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
487
|
+
class TargetConditionedAcquisitionState:
|
|
488
|
+
"""Branch-local immutable posterior state at one generation cutoff."""
|
|
489
|
+
|
|
490
|
+
campaign_scope_sha256: str
|
|
491
|
+
training_data_sha256: str
|
|
492
|
+
marginal_head: PrequentialLinearGaussianHead
|
|
493
|
+
direction_head: PrequentialLinearGaussianHead
|
|
494
|
+
cutoff_generation: int = 0
|
|
495
|
+
selected_observation_count: int = 0
|
|
496
|
+
|
|
497
|
+
def __post_init__(self) -> None:
|
|
498
|
+
require_sha256(self.campaign_scope_sha256, "campaign_scope_sha256")
|
|
499
|
+
require_sha256(self.training_data_sha256, "training_data_sha256")
|
|
500
|
+
if type(self.marginal_head) is not PrequentialLinearGaussianHead:
|
|
501
|
+
raise TypeError("marginal_head must be exact")
|
|
502
|
+
if type(self.direction_head) is not PrequentialLinearGaussianHead:
|
|
503
|
+
raise TypeError("direction_head must be exact")
|
|
504
|
+
self.marginal_head.__post_init__()
|
|
505
|
+
self.direction_head.__post_init__()
|
|
506
|
+
if self.marginal_head.feature_names != self.direction_head.feature_names:
|
|
507
|
+
raise ValueError("acquisition heads must use one feature schema")
|
|
508
|
+
if (
|
|
509
|
+
self.marginal_head.means != self.direction_head.means
|
|
510
|
+
or self.marginal_head.scales != self.direction_head.scales
|
|
511
|
+
):
|
|
512
|
+
raise ValueError("acquisition heads must share one standardizer")
|
|
513
|
+
if type(self.cutoff_generation) is not int or self.cutoff_generation < 0:
|
|
514
|
+
raise ValueError("cutoff_generation must be non-negative")
|
|
515
|
+
if (
|
|
516
|
+
type(self.selected_observation_count) is not int
|
|
517
|
+
or self.selected_observation_count < 0
|
|
518
|
+
):
|
|
519
|
+
raise ValueError("selected_observation_count must be non-negative")
|
|
520
|
+
|
|
521
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
522
|
+
self.__post_init__()
|
|
523
|
+
return {
|
|
524
|
+
"schema_version": 1,
|
|
525
|
+
"campaign_scope_sha256": self.campaign_scope_sha256,
|
|
526
|
+
"training_data_sha256": self.training_data_sha256,
|
|
527
|
+
"marginal_head": self.marginal_head.to_record(),
|
|
528
|
+
"direction_head": self.direction_head.to_record(),
|
|
529
|
+
"cutoff_generation": self.cutoff_generation,
|
|
530
|
+
"selected_observation_count": self.selected_observation_count,
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def state_sha256(self) -> str:
|
|
535
|
+
return _hash(_STATE_DOMAIN, self._unsigned_record())
|
|
536
|
+
|
|
537
|
+
def to_record(self) -> dict[str, object]:
|
|
538
|
+
return {**self._unsigned_record(), "state_sha256": self.state_sha256}
|
|
539
|
+
|
|
540
|
+
def __eq__(self, other: object) -> bool:
|
|
541
|
+
return (
|
|
542
|
+
type(other) is TargetConditionedAcquisitionState
|
|
543
|
+
and self.state_sha256 == other.state_sha256
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
__hash__ = None
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
550
|
+
class TargetConditionedAcquisitionProfile:
|
|
551
|
+
"""Frozen portable coefficients; no route-specific test-time tuning."""
|
|
552
|
+
|
|
553
|
+
direction_weight: float
|
|
554
|
+
uncertainty_weight: float
|
|
555
|
+
maximum_remaining_horizon: int
|
|
556
|
+
profile_id: str = "trap_portable_v1"
|
|
557
|
+
profile_version: int = 1
|
|
558
|
+
|
|
559
|
+
def __post_init__(self) -> None:
|
|
560
|
+
if type(self.profile_id) is not str or _TOKEN.fullmatch(self.profile_id) is None:
|
|
561
|
+
raise ValueError("profile_id must use the closed token grammar")
|
|
562
|
+
if type(self.profile_version) is not int or self.profile_version <= 0:
|
|
563
|
+
raise ValueError("profile_version must be positive")
|
|
564
|
+
for name in ("direction_weight", "uncertainty_weight"):
|
|
565
|
+
value = getattr(self, name)
|
|
566
|
+
_finite(value, name=name)
|
|
567
|
+
if not 0.0 <= value <= 1.0:
|
|
568
|
+
raise ValueError(f"{name} must lie in [0, 1]")
|
|
569
|
+
if (
|
|
570
|
+
type(self.maximum_remaining_horizon) is not int
|
|
571
|
+
or self.maximum_remaining_horizon <= 0
|
|
572
|
+
):
|
|
573
|
+
raise ValueError("maximum_remaining_horizon must be positive")
|
|
574
|
+
|
|
575
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
576
|
+
self.__post_init__()
|
|
577
|
+
return {
|
|
578
|
+
"schema_version": 1,
|
|
579
|
+
"profile_id": self.profile_id,
|
|
580
|
+
"profile_version": self.profile_version,
|
|
581
|
+
"direction_weight_hex": self.direction_weight.hex(),
|
|
582
|
+
"uncertainty_weight_hex": self.uncertainty_weight.hex(),
|
|
583
|
+
"maximum_remaining_horizon": self.maximum_remaining_horizon,
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
@property
|
|
587
|
+
def profile_sha256(self) -> str:
|
|
588
|
+
return _hash(_PROFILE_DOMAIN, self._unsigned_record())
|
|
589
|
+
|
|
590
|
+
def to_record(self) -> dict[str, object]:
|
|
591
|
+
return {**self._unsigned_record(), "profile_sha256": self.profile_sha256}
|
|
592
|
+
|
|
593
|
+
@classmethod
|
|
594
|
+
def from_record(
|
|
595
|
+
cls, record: object
|
|
596
|
+
) -> TargetConditionedAcquisitionProfile:
|
|
597
|
+
value = _exact_record_keys(
|
|
598
|
+
record,
|
|
599
|
+
expected={
|
|
600
|
+
"schema_version",
|
|
601
|
+
"profile_id",
|
|
602
|
+
"profile_version",
|
|
603
|
+
"direction_weight_hex",
|
|
604
|
+
"uncertainty_weight_hex",
|
|
605
|
+
"maximum_remaining_horizon",
|
|
606
|
+
"profile_sha256",
|
|
607
|
+
},
|
|
608
|
+
name="target-conditioned acquisition profile record",
|
|
609
|
+
)
|
|
610
|
+
if value["schema_version"] != 1:
|
|
611
|
+
raise ValueError("unsupported target-conditioned profile schema")
|
|
612
|
+
result = cls(
|
|
613
|
+
profile_id=value["profile_id"],
|
|
614
|
+
profile_version=value["profile_version"],
|
|
615
|
+
direction_weight=_hex_float(
|
|
616
|
+
value["direction_weight_hex"], name="direction_weight_hex"
|
|
617
|
+
),
|
|
618
|
+
uncertainty_weight=_hex_float(
|
|
619
|
+
value["uncertainty_weight_hex"], name="uncertainty_weight_hex"
|
|
620
|
+
),
|
|
621
|
+
maximum_remaining_horizon=value["maximum_remaining_horizon"],
|
|
622
|
+
)
|
|
623
|
+
if value["profile_sha256"] != result.profile_sha256:
|
|
624
|
+
raise ValueError("target-conditioned profile identity mismatch")
|
|
625
|
+
return result
|
|
626
|
+
|
|
627
|
+
def __eq__(self, other: object) -> bool:
|
|
628
|
+
return (
|
|
629
|
+
type(other) is TargetConditionedAcquisitionProfile
|
|
630
|
+
and self.profile_sha256 == other.profile_sha256
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
__hash__ = None
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
637
|
+
class TargetConditionedMemberFeatures:
|
|
638
|
+
"""Authenticated numeric projection for one exact sealed option."""
|
|
639
|
+
|
|
640
|
+
option_id: str
|
|
641
|
+
option_identity_sha256: str
|
|
642
|
+
feature_names: tuple[str, ...]
|
|
643
|
+
values: tuple[float, ...]
|
|
644
|
+
projector_id: str
|
|
645
|
+
projector_version: int
|
|
646
|
+
projector_definition_sha256: str
|
|
647
|
+
|
|
648
|
+
def __post_init__(self) -> None:
|
|
649
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
650
|
+
raise ValueError("option_id must be non-empty")
|
|
651
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
652
|
+
_feature_names(self.feature_names)
|
|
653
|
+
if type(self.values) is not tuple or len(self.values) != len(
|
|
654
|
+
self.feature_names
|
|
655
|
+
):
|
|
656
|
+
raise ValueError("feature values must match the feature schema")
|
|
657
|
+
for value in self.values:
|
|
658
|
+
_finite(value, name="feature value")
|
|
659
|
+
if type(self.projector_id) is not str or _TOKEN.fullmatch(self.projector_id) is None:
|
|
660
|
+
raise ValueError("projector_id must use the closed token grammar")
|
|
661
|
+
if type(self.projector_version) is not int or self.projector_version <= 0:
|
|
662
|
+
raise ValueError("projector_version must be positive")
|
|
663
|
+
require_sha256(
|
|
664
|
+
self.projector_definition_sha256, "projector_definition_sha256"
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
668
|
+
self.__post_init__()
|
|
669
|
+
return {
|
|
670
|
+
"option_id": self.option_id,
|
|
671
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
672
|
+
"feature_names": list(self.feature_names),
|
|
673
|
+
"values_hex": [value.hex() for value in self.values],
|
|
674
|
+
"projector": {
|
|
675
|
+
"projector_id": self.projector_id,
|
|
676
|
+
"projector_version": self.projector_version,
|
|
677
|
+
"definition_sha256": self.projector_definition_sha256,
|
|
678
|
+
},
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
@property
|
|
682
|
+
def feature_row_sha256(self) -> str:
|
|
683
|
+
return _hash(_FEATURE_ROW_DOMAIN, self._unsigned_record())
|
|
684
|
+
|
|
685
|
+
def to_record(self) -> dict[str, object]:
|
|
686
|
+
return {
|
|
687
|
+
**self._unsigned_record(),
|
|
688
|
+
"feature_row_sha256": self.feature_row_sha256,
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
def __eq__(self, other: object) -> bool:
|
|
692
|
+
return (
|
|
693
|
+
type(other) is TargetConditionedMemberFeatures
|
|
694
|
+
and self.feature_row_sha256 == other.feature_row_sha256
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
__hash__ = None
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
701
|
+
class RealizablePortfolioSet:
|
|
702
|
+
"""Complete finite portfolio universe certified by a generic projector."""
|
|
703
|
+
|
|
704
|
+
source_request_sha256: str
|
|
705
|
+
projector_id: str
|
|
706
|
+
projector_version: int
|
|
707
|
+
projector_definition_sha256: str
|
|
708
|
+
option_id_sets: tuple[tuple[str, ...], ...]
|
|
709
|
+
|
|
710
|
+
def __post_init__(self) -> None:
|
|
711
|
+
require_sha256(self.source_request_sha256, "source_request_sha256")
|
|
712
|
+
if type(self.projector_id) is not str or _TOKEN.fullmatch(self.projector_id) is None:
|
|
713
|
+
raise ValueError("projector_id must use the closed token grammar")
|
|
714
|
+
if type(self.projector_version) is not int or self.projector_version <= 0:
|
|
715
|
+
raise ValueError("projector_version must be positive")
|
|
716
|
+
require_sha256(
|
|
717
|
+
self.projector_definition_sha256, "projector_definition_sha256"
|
|
718
|
+
)
|
|
719
|
+
if type(self.option_id_sets) is not tuple or not self.option_id_sets:
|
|
720
|
+
raise ValueError("option_id_sets must be a non-empty exact tuple")
|
|
721
|
+
for values in self.option_id_sets:
|
|
722
|
+
if type(values) is not tuple or not values:
|
|
723
|
+
raise ValueError("each realizable portfolio must be non-empty")
|
|
724
|
+
if values != tuple(sorted(set(values))):
|
|
725
|
+
raise ValueError("realizable option IDs must be unique and canonical")
|
|
726
|
+
if self.option_id_sets != tuple(sorted(set(self.option_id_sets))):
|
|
727
|
+
raise ValueError("realizable portfolios must be unique and canonical")
|
|
728
|
+
|
|
729
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
730
|
+
self.__post_init__()
|
|
731
|
+
return {
|
|
732
|
+
"schema_version": 1,
|
|
733
|
+
"source_request_sha256": self.source_request_sha256,
|
|
734
|
+
"projector": {
|
|
735
|
+
"projector_id": self.projector_id,
|
|
736
|
+
"projector_version": self.projector_version,
|
|
737
|
+
"definition_sha256": self.projector_definition_sha256,
|
|
738
|
+
},
|
|
739
|
+
"option_id_sets": [list(value) for value in self.option_id_sets],
|
|
740
|
+
"complete_for_projector_contract": True,
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
@property
|
|
744
|
+
def realizable_set_sha256(self) -> str:
|
|
745
|
+
return _hash(_REALIZABLE_DOMAIN, self._unsigned_record())
|
|
746
|
+
|
|
747
|
+
def to_record(self) -> dict[str, object]:
|
|
748
|
+
return {
|
|
749
|
+
**self._unsigned_record(),
|
|
750
|
+
"realizable_set_sha256": self.realizable_set_sha256,
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
def __eq__(self, other: object) -> bool:
|
|
754
|
+
return (
|
|
755
|
+
type(other) is RealizablePortfolioSet
|
|
756
|
+
and self.realizable_set_sha256 == other.realizable_set_sha256
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
__hash__ = None
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def _members_for_ids(
|
|
763
|
+
request: SlateAllocationRequest, option_ids: Sequence[str]
|
|
764
|
+
) -> tuple[CalibratedSlateMember, ...]:
|
|
765
|
+
selected = set(option_ids)
|
|
766
|
+
return tuple(
|
|
767
|
+
member for member in request.slate.members if member.option_id in selected
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def _base_feasible(
|
|
772
|
+
request: SlateAllocationRequest, option_ids: tuple[str, ...]
|
|
773
|
+
) -> tuple[bool, PortfolioMemoryDoseAssessment | None]:
|
|
774
|
+
if len(option_ids) != request.portfolio_size:
|
|
775
|
+
return False, None
|
|
776
|
+
if not set(request.required_option_ids).issubset(option_ids):
|
|
777
|
+
return False, None
|
|
778
|
+
members = _members_for_ids(request, option_ids)
|
|
779
|
+
if len(members) != request.portfolio_size:
|
|
780
|
+
return False, None
|
|
781
|
+
if request.pairwise_disjoint_option_id_pairs is not None:
|
|
782
|
+
allowed = {frozenset(value) for value in request.pairwise_disjoint_option_id_pairs}
|
|
783
|
+
if any(
|
|
784
|
+
frozenset(value) not in allowed for value in combinations(option_ids, 2)
|
|
785
|
+
):
|
|
786
|
+
return False, None
|
|
787
|
+
if request.min_distinct_families is not None and len(
|
|
788
|
+
{value.family for value in members}
|
|
789
|
+
) < request.min_distinct_families:
|
|
790
|
+
return False, None
|
|
791
|
+
assessment = (
|
|
792
|
+
None
|
|
793
|
+
if request.memory_dose_contract is None
|
|
794
|
+
else assess_allocated_slate_memory_dose(request, members)
|
|
795
|
+
)
|
|
796
|
+
if assessment is not None and not assessment.passed:
|
|
797
|
+
return False, assessment
|
|
798
|
+
return True, assessment
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def enumerate_base_realizable_portfolios(
|
|
802
|
+
request: SlateAllocationRequest,
|
|
803
|
+
) -> RealizablePortfolioSet:
|
|
804
|
+
"""Exactly enumerate the constraints represented by ``SlateAllocationRequest``."""
|
|
805
|
+
|
|
806
|
+
if type(request) is not SlateAllocationRequest:
|
|
807
|
+
raise TypeError("request must be exact SlateAllocationRequest")
|
|
808
|
+
request.revalidate()
|
|
809
|
+
values = []
|
|
810
|
+
for members in combinations(request.slate.members, request.portfolio_size):
|
|
811
|
+
option_ids = tuple(sorted(value.option_id for value in members))
|
|
812
|
+
feasible, _ = _base_feasible(request, option_ids)
|
|
813
|
+
if feasible:
|
|
814
|
+
values.append(option_ids)
|
|
815
|
+
if not values:
|
|
816
|
+
raise ValueError("sealed slate has no base-realizable portfolio")
|
|
817
|
+
return RealizablePortfolioSet(
|
|
818
|
+
source_request_sha256=request.request_sha256,
|
|
819
|
+
projector_id=BASE_REALIZABILITY_PROJECTOR_ID,
|
|
820
|
+
projector_version=BASE_REALIZABILITY_PROJECTOR_VERSION,
|
|
821
|
+
projector_definition_sha256=BASE_REALIZABILITY_DEFINITION_SHA256,
|
|
822
|
+
option_id_sets=tuple(sorted(set(values))),
|
|
823
|
+
)
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
827
|
+
class TargetConditionedSlateRequest:
|
|
828
|
+
"""All authenticated inputs for one prior-only T-RAP decision."""
|
|
829
|
+
|
|
830
|
+
allocation_request: SlateAllocationRequest
|
|
831
|
+
frontier_target: CampaignPortfolioFrontierTarget
|
|
832
|
+
state: TargetConditionedAcquisitionState
|
|
833
|
+
member_features: tuple[TargetConditionedMemberFeatures, ...]
|
|
834
|
+
realizable_portfolios: RealizablePortfolioSet
|
|
835
|
+
campaign_generation: int
|
|
836
|
+
remaining_proposal_horizon: int
|
|
837
|
+
|
|
838
|
+
def __post_init__(self) -> None:
|
|
839
|
+
if type(self.allocation_request) is not SlateAllocationRequest:
|
|
840
|
+
raise TypeError("allocation_request must be exact")
|
|
841
|
+
self.allocation_request.revalidate()
|
|
842
|
+
if type(self.frontier_target) is not CampaignPortfolioFrontierTarget:
|
|
843
|
+
raise TypeError("frontier_target must be exact")
|
|
844
|
+
self.frontier_target.__post_init__()
|
|
845
|
+
if type(self.state) is not TargetConditionedAcquisitionState:
|
|
846
|
+
raise TypeError("state must be exact")
|
|
847
|
+
self.state.__post_init__()
|
|
848
|
+
if (
|
|
849
|
+
type(self.member_features) is not tuple
|
|
850
|
+
or any(
|
|
851
|
+
type(value) is not TargetConditionedMemberFeatures
|
|
852
|
+
for value in self.member_features
|
|
853
|
+
)
|
|
854
|
+
):
|
|
855
|
+
raise TypeError("member_features must contain exact rows")
|
|
856
|
+
for value in self.member_features:
|
|
857
|
+
value.__post_init__()
|
|
858
|
+
if type(self.realizable_portfolios) is not RealizablePortfolioSet:
|
|
859
|
+
raise TypeError("realizable_portfolios must be exact")
|
|
860
|
+
self.realizable_portfolios.__post_init__()
|
|
861
|
+
if (
|
|
862
|
+
self.realizable_portfolios.source_request_sha256
|
|
863
|
+
!= self.allocation_request.request_sha256
|
|
864
|
+
):
|
|
865
|
+
raise ValueError("realizable set names a foreign allocation request")
|
|
866
|
+
base_projector_identity = (
|
|
867
|
+
BASE_REALIZABILITY_PROJECTOR_ID,
|
|
868
|
+
BASE_REALIZABILITY_PROJECTOR_VERSION,
|
|
869
|
+
BASE_REALIZABILITY_DEFINITION_SHA256,
|
|
870
|
+
)
|
|
871
|
+
supplied_projector_identity = (
|
|
872
|
+
self.realizable_portfolios.projector_id,
|
|
873
|
+
self.realizable_portfolios.projector_version,
|
|
874
|
+
self.realizable_portfolios.projector_definition_sha256,
|
|
875
|
+
)
|
|
876
|
+
if self.realizable_portfolios.projector_id == BASE_REALIZABILITY_PROJECTOR_ID:
|
|
877
|
+
if supplied_projector_identity != base_projector_identity:
|
|
878
|
+
raise ValueError("reserved base projector ID has a foreign identity")
|
|
879
|
+
expected = enumerate_base_realizable_portfolios(self.allocation_request)
|
|
880
|
+
if self.realizable_portfolios != expected:
|
|
881
|
+
raise ValueError("base realizability receipt is not complete")
|
|
882
|
+
if type(self.campaign_generation) is not int or self.campaign_generation <= 0:
|
|
883
|
+
raise ValueError("campaign_generation must be positive")
|
|
884
|
+
if (
|
|
885
|
+
type(self.remaining_proposal_horizon) is not int
|
|
886
|
+
or self.remaining_proposal_horizon < 0
|
|
887
|
+
):
|
|
888
|
+
raise ValueError("remaining_proposal_horizon must be non-negative")
|
|
889
|
+
if self.state.cutoff_generation >= self.campaign_generation:
|
|
890
|
+
raise ValueError("posterior cutoff reaches current/future generation")
|
|
891
|
+
slate = self.allocation_request.slate
|
|
892
|
+
if (
|
|
893
|
+
self.frontier_target.parent_configuration_sha256
|
|
894
|
+
!= slate.parent_candidate_identity_sha256
|
|
895
|
+
):
|
|
896
|
+
raise ValueError("frontier target names a foreign parent")
|
|
897
|
+
feature_by_id = {value.option_id: value for value in self.member_features}
|
|
898
|
+
member_by_id = {value.option_id: value for value in slate.members}
|
|
899
|
+
if len(feature_by_id) != len(self.member_features) or set(feature_by_id) != set(
|
|
900
|
+
member_by_id
|
|
901
|
+
):
|
|
902
|
+
raise ValueError("feature rows must cover every slate member exactly")
|
|
903
|
+
expected_order = tuple(sorted(feature_by_id))
|
|
904
|
+
if tuple(value.option_id for value in self.member_features) != expected_order:
|
|
905
|
+
raise ValueError("feature rows must use canonical option order")
|
|
906
|
+
for option_id, row in feature_by_id.items():
|
|
907
|
+
if row.option_identity_sha256 != member_by_id[option_id].option_identity_sha256:
|
|
908
|
+
raise ValueError("feature row names a foreign option identity")
|
|
909
|
+
if row.feature_names != self.state.marginal_head.feature_names:
|
|
910
|
+
raise ValueError("feature row differs from the posterior schema")
|
|
911
|
+
projectors = {
|
|
912
|
+
(
|
|
913
|
+
value.projector_id,
|
|
914
|
+
value.projector_version,
|
|
915
|
+
value.projector_definition_sha256,
|
|
916
|
+
)
|
|
917
|
+
for value in self.member_features
|
|
918
|
+
}
|
|
919
|
+
if len(projectors) != 1:
|
|
920
|
+
raise ValueError("feature rows must share one projector identity")
|
|
921
|
+
slate_ids = set(member_by_id)
|
|
922
|
+
for option_ids in self.realizable_portfolios.option_id_sets:
|
|
923
|
+
if not set(option_ids).issubset(slate_ids):
|
|
924
|
+
raise ValueError("realizable portfolio escapes the sealed slate")
|
|
925
|
+
feasible, _ = _base_feasible(self.allocation_request, option_ids)
|
|
926
|
+
if not feasible:
|
|
927
|
+
raise ValueError("realizable projector admitted a base-infeasible set")
|
|
928
|
+
|
|
929
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
930
|
+
self.__post_init__()
|
|
931
|
+
return {
|
|
932
|
+
"schema_version": 1,
|
|
933
|
+
"allocation_request_sha256": self.allocation_request.request_sha256,
|
|
934
|
+
"frontier_target": self.frontier_target.to_record(),
|
|
935
|
+
"state": self.state.to_record(),
|
|
936
|
+
"member_features": [value.to_record() for value in self.member_features],
|
|
937
|
+
"realizable_portfolios": self.realizable_portfolios.to_record(),
|
|
938
|
+
"campaign_generation": self.campaign_generation,
|
|
939
|
+
"remaining_proposal_horizon": self.remaining_proposal_horizon,
|
|
940
|
+
"current_or_future_outcomes_consulted": False,
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
@property
|
|
944
|
+
def request_sha256(self) -> str:
|
|
945
|
+
return _hash(_REQUEST_DOMAIN, self._unsigned_record())
|
|
946
|
+
|
|
947
|
+
def to_record(self) -> dict[str, object]:
|
|
948
|
+
return {**self._unsigned_record(), "request_sha256": self.request_sha256}
|
|
949
|
+
|
|
950
|
+
def __eq__(self, other: object) -> bool:
|
|
951
|
+
return (
|
|
952
|
+
type(other) is TargetConditionedSlateRequest
|
|
953
|
+
and self.request_sha256 == other.request_sha256
|
|
954
|
+
)
|
|
955
|
+
|
|
956
|
+
__hash__ = None
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def _z_scores(values: dict[str, float]) -> dict[str, float]:
|
|
960
|
+
mean = fmean(values.values())
|
|
961
|
+
scale = math.sqrt(fmean((value - mean) ** 2 for value in values.values()))
|
|
962
|
+
if scale < 1e-12:
|
|
963
|
+
return {key: 0.0 for key in values}
|
|
964
|
+
return {key: (value - mean) / scale for key, value in values.items()}
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
@dataclass(frozen=True, slots=True)
|
|
968
|
+
class TargetConditionedMemberScore:
|
|
969
|
+
option_id: str
|
|
970
|
+
option_identity_sha256: str
|
|
971
|
+
model_rank: int
|
|
972
|
+
predicted_marginal: float
|
|
973
|
+
predicted_direction: float
|
|
974
|
+
epistemic_uncertainty: float
|
|
975
|
+
marginal_z: float
|
|
976
|
+
direction_z: float
|
|
977
|
+
uncertainty_z: float
|
|
978
|
+
final_score: float
|
|
979
|
+
|
|
980
|
+
def __post_init__(self) -> None:
|
|
981
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
982
|
+
raise ValueError("option_id must be non-empty")
|
|
983
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
984
|
+
if type(self.model_rank) is not int or self.model_rank <= 0:
|
|
985
|
+
raise ValueError("model_rank must be positive")
|
|
986
|
+
for name in (
|
|
987
|
+
"predicted_marginal",
|
|
988
|
+
"predicted_direction",
|
|
989
|
+
"epistemic_uncertainty",
|
|
990
|
+
"marginal_z",
|
|
991
|
+
"direction_z",
|
|
992
|
+
"uncertainty_z",
|
|
993
|
+
"final_score",
|
|
994
|
+
):
|
|
995
|
+
_finite(getattr(self, name), name=name)
|
|
996
|
+
if self.epistemic_uncertainty < 0.0:
|
|
997
|
+
raise ValueError("epistemic_uncertainty must be non-negative")
|
|
998
|
+
|
|
999
|
+
def to_record(self) -> dict[str, object]:
|
|
1000
|
+
self.__post_init__()
|
|
1001
|
+
return {
|
|
1002
|
+
"option_id": self.option_id,
|
|
1003
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
1004
|
+
"model_rank": self.model_rank,
|
|
1005
|
+
"predicted_marginal_hex": self.predicted_marginal.hex(),
|
|
1006
|
+
"predicted_direction_hex": self.predicted_direction.hex(),
|
|
1007
|
+
"epistemic_uncertainty_hex": self.epistemic_uncertainty.hex(),
|
|
1008
|
+
"marginal_z_hex": self.marginal_z.hex(),
|
|
1009
|
+
"direction_z_hex": self.direction_z.hex(),
|
|
1010
|
+
"uncertainty_z_hex": self.uncertainty_z.hex(),
|
|
1011
|
+
"final_score_hex": self.final_score.hex(),
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
class TargetConditionedSlateRole(str, Enum):
|
|
1016
|
+
"""Engine-owned role for each evaluator exposure selected by T-RAP."""
|
|
1017
|
+
|
|
1018
|
+
ACQUISITION = "target_conditioned_acquisition"
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
@dataclass(frozen=True, slots=True)
|
|
1022
|
+
class TargetConditionedAllocatedMember:
|
|
1023
|
+
option_id: str
|
|
1024
|
+
option_identity_sha256: str
|
|
1025
|
+
model_rank: int
|
|
1026
|
+
acquisition_score: float
|
|
1027
|
+
role: TargetConditionedSlateRole = TargetConditionedSlateRole.ACQUISITION
|
|
1028
|
+
|
|
1029
|
+
def __post_init__(self) -> None:
|
|
1030
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
1031
|
+
raise ValueError("option_id must be non-empty")
|
|
1032
|
+
require_sha256(self.option_identity_sha256, "option_identity_sha256")
|
|
1033
|
+
if type(self.model_rank) is not int or self.model_rank <= 0:
|
|
1034
|
+
raise ValueError("model_rank must be positive")
|
|
1035
|
+
_finite(self.acquisition_score, name="acquisition_score")
|
|
1036
|
+
if type(self.role) is not TargetConditionedSlateRole:
|
|
1037
|
+
raise TypeError("role must be exact TargetConditionedSlateRole")
|
|
1038
|
+
|
|
1039
|
+
def to_record(self) -> dict[str, object]:
|
|
1040
|
+
self.__post_init__()
|
|
1041
|
+
return {
|
|
1042
|
+
"option_id": self.option_id,
|
|
1043
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
1044
|
+
"model_rank": self.model_rank,
|
|
1045
|
+
"acquisition_score_hex": self.acquisition_score.hex(),
|
|
1046
|
+
"role": self.role.value,
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
|
|
1050
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1051
|
+
class TargetConditionedSlateDecision:
|
|
1052
|
+
request: TargetConditionedSlateRequest
|
|
1053
|
+
profile: TargetConditionedAcquisitionProfile
|
|
1054
|
+
score_rows: tuple[TargetConditionedMemberScore, ...]
|
|
1055
|
+
selected: tuple[TargetConditionedAllocatedMember, ...]
|
|
1056
|
+
joint_score: float
|
|
1057
|
+
feasible_subset_count: int
|
|
1058
|
+
administered_card_keys: tuple[str, ...]
|
|
1059
|
+
memory_dose_assessment: PortfolioMemoryDoseAssessment | None
|
|
1060
|
+
|
|
1061
|
+
policy_id: ClassVar[str] = POLICY_ID
|
|
1062
|
+
policy_version: ClassVar[int] = POLICY_VERSION
|
|
1063
|
+
policy_definition_sha256: ClassVar[str] = POLICY_DEFINITION_SHA256
|
|
1064
|
+
|
|
1065
|
+
def __post_init__(self) -> None:
|
|
1066
|
+
if type(self.request) is not TargetConditionedSlateRequest:
|
|
1067
|
+
raise TypeError("request must be exact")
|
|
1068
|
+
self.request.__post_init__()
|
|
1069
|
+
if type(self.profile) is not TargetConditionedAcquisitionProfile:
|
|
1070
|
+
raise TypeError("profile must be exact")
|
|
1071
|
+
self.profile.__post_init__()
|
|
1072
|
+
if type(self.score_rows) is not tuple or any(
|
|
1073
|
+
type(value) is not TargetConditionedMemberScore for value in self.score_rows
|
|
1074
|
+
):
|
|
1075
|
+
raise TypeError("score_rows must contain exact values")
|
|
1076
|
+
if type(self.selected) is not tuple or len(self.selected) != (
|
|
1077
|
+
self.request.allocation_request.portfolio_size
|
|
1078
|
+
):
|
|
1079
|
+
raise ValueError("selected must have the exact portfolio size")
|
|
1080
|
+
for value in (*self.score_rows, *self.selected):
|
|
1081
|
+
value.__post_init__()
|
|
1082
|
+
score_ids = tuple(value.option_id for value in self.score_rows)
|
|
1083
|
+
expected_score_ids = tuple(
|
|
1084
|
+
value.option_id for value in self.request.member_features
|
|
1085
|
+
)
|
|
1086
|
+
if score_ids != expected_score_ids:
|
|
1087
|
+
raise ValueError("score rows differ from the canonical feature rows")
|
|
1088
|
+
selected_ids = tuple(sorted(value.option_id for value in self.selected))
|
|
1089
|
+
if selected_ids not in self.request.realizable_portfolios.option_id_sets:
|
|
1090
|
+
raise ValueError("selected set is not realizable")
|
|
1091
|
+
_finite(self.joint_score, name="joint_score")
|
|
1092
|
+
if (
|
|
1093
|
+
type(self.feasible_subset_count) is not int
|
|
1094
|
+
or self.feasible_subset_count
|
|
1095
|
+
!= len(self.request.realizable_portfolios.option_id_sets)
|
|
1096
|
+
):
|
|
1097
|
+
raise ValueError("feasible_subset_count differs from the sealed universe")
|
|
1098
|
+
if self.administered_card_keys != tuple(sorted(set(self.administered_card_keys))):
|
|
1099
|
+
raise ValueError("administered_card_keys must be unique and canonical")
|
|
1100
|
+
if self.memory_dose_assessment is not None:
|
|
1101
|
+
if type(self.memory_dose_assessment) is not PortfolioMemoryDoseAssessment:
|
|
1102
|
+
raise TypeError("memory_dose_assessment must be exact or None")
|
|
1103
|
+
self.memory_dose_assessment.__post_init__()
|
|
1104
|
+
if not self.memory_dose_assessment.passed:
|
|
1105
|
+
raise ValueError("selected memory dose must pass")
|
|
1106
|
+
|
|
1107
|
+
def revalidate(self) -> None:
|
|
1108
|
+
if type(self) is not TargetConditionedSlateDecision:
|
|
1109
|
+
raise TypeError("decision must be exact")
|
|
1110
|
+
TargetConditionedSlateDecision.__post_init__(self)
|
|
1111
|
+
|
|
1112
|
+
@property
|
|
1113
|
+
def prior_only(self) -> bool:
|
|
1114
|
+
return self.request.state.cutoff_generation < self.request.campaign_generation
|
|
1115
|
+
|
|
1116
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1117
|
+
self.revalidate()
|
|
1118
|
+
return {
|
|
1119
|
+
"schema_version": 1,
|
|
1120
|
+
"event_type": "target_conditioned_prequential_portfolio_allocated",
|
|
1121
|
+
"policy_id": self.policy_id,
|
|
1122
|
+
"policy_version": self.policy_version,
|
|
1123
|
+
"policy_definition_sha256": self.policy_definition_sha256,
|
|
1124
|
+
"request": self.request.to_record(),
|
|
1125
|
+
"profile": self.profile.to_record(),
|
|
1126
|
+
"score_rows": [value.to_record() for value in self.score_rows],
|
|
1127
|
+
"selected": [value.to_record() for value in self.selected],
|
|
1128
|
+
"joint_score_hex": self.joint_score.hex(),
|
|
1129
|
+
"feasible_subset_count": self.feasible_subset_count,
|
|
1130
|
+
"administered_card_keys": list(self.administered_card_keys),
|
|
1131
|
+
"memory_dose_assessment": (
|
|
1132
|
+
None
|
|
1133
|
+
if self.memory_dose_assessment is None
|
|
1134
|
+
else self.memory_dose_assessment.to_record()
|
|
1135
|
+
),
|
|
1136
|
+
"prior_only": self.prior_only,
|
|
1137
|
+
"claim_scope": "allocation_receipt_not_efficacy_or_outcome_claim",
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
@property
|
|
1141
|
+
def decision_sha256(self) -> str:
|
|
1142
|
+
return _hash(_DECISION_DOMAIN, self._unsigned_record())
|
|
1143
|
+
|
|
1144
|
+
def to_record(self) -> dict[str, object]:
|
|
1145
|
+
return {**self._unsigned_record(), "decision_sha256": self.decision_sha256}
|
|
1146
|
+
|
|
1147
|
+
def __eq__(self, other: object) -> bool:
|
|
1148
|
+
return (
|
|
1149
|
+
type(other) is TargetConditionedSlateDecision
|
|
1150
|
+
and self.decision_sha256 == other.decision_sha256
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
__hash__ = None
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
@dataclass(frozen=True, slots=True)
|
|
1157
|
+
class TargetConditionedPrequentialSlatePolicy:
|
|
1158
|
+
"""Select the exact highest-scoring realizable set under a frozen profile."""
|
|
1159
|
+
|
|
1160
|
+
profile: TargetConditionedAcquisitionProfile
|
|
1161
|
+
|
|
1162
|
+
policy_id: ClassVar[str] = POLICY_ID
|
|
1163
|
+
policy_version: ClassVar[int] = POLICY_VERSION
|
|
1164
|
+
definition_sha256: ClassVar[str] = POLICY_DEFINITION_SHA256
|
|
1165
|
+
|
|
1166
|
+
def __post_init__(self) -> None:
|
|
1167
|
+
if type(self.profile) is not TargetConditionedAcquisitionProfile:
|
|
1168
|
+
raise TypeError("profile must be exact")
|
|
1169
|
+
self.profile.__post_init__()
|
|
1170
|
+
|
|
1171
|
+
def select(self, request: TargetConditionedSlateRequest) -> TargetConditionedSlateDecision:
|
|
1172
|
+
self.__post_init__()
|
|
1173
|
+
if type(request) is not TargetConditionedSlateRequest:
|
|
1174
|
+
raise TypeError("request must be exact")
|
|
1175
|
+
request.__post_init__()
|
|
1176
|
+
if request.remaining_proposal_horizon > self.profile.maximum_remaining_horizon:
|
|
1177
|
+
raise ValueError("remaining horizon exceeds the frozen profile")
|
|
1178
|
+
marginal_head = request.state.marginal_head.project()
|
|
1179
|
+
direction_head = request.state.direction_head.project()
|
|
1180
|
+
marginal = {
|
|
1181
|
+
row.option_id: marginal_head.predict(row.values)
|
|
1182
|
+
for row in request.member_features
|
|
1183
|
+
}
|
|
1184
|
+
direction = {
|
|
1185
|
+
row.option_id: direction_head.predict(row.values)
|
|
1186
|
+
for row in request.member_features
|
|
1187
|
+
}
|
|
1188
|
+
uncertainty = {
|
|
1189
|
+
row.option_id: marginal_head.uncertainty(row.values)
|
|
1190
|
+
for row in request.member_features
|
|
1191
|
+
}
|
|
1192
|
+
marginal_z = _z_scores(marginal)
|
|
1193
|
+
direction_z = _z_scores(direction)
|
|
1194
|
+
uncertainty_z = _z_scores(uncertainty)
|
|
1195
|
+
horizon_fraction = (
|
|
1196
|
+
request.remaining_proposal_horizon
|
|
1197
|
+
/ self.profile.maximum_remaining_horizon
|
|
1198
|
+
)
|
|
1199
|
+
scores = {
|
|
1200
|
+
option_id: (
|
|
1201
|
+
marginal_z[option_id]
|
|
1202
|
+
+ self.profile.direction_weight * direction_z[option_id]
|
|
1203
|
+
+ self.profile.uncertainty_weight
|
|
1204
|
+
* horizon_fraction
|
|
1205
|
+
* uncertainty_z[option_id]
|
|
1206
|
+
)
|
|
1207
|
+
for option_id in marginal
|
|
1208
|
+
}
|
|
1209
|
+
selected_set = min(
|
|
1210
|
+
request.realizable_portfolios.option_id_sets,
|
|
1211
|
+
key=lambda option_ids: (
|
|
1212
|
+
-sum(scores[option_id] for option_id in option_ids),
|
|
1213
|
+
option_ids,
|
|
1214
|
+
),
|
|
1215
|
+
)
|
|
1216
|
+
selected_members = _members_for_ids(
|
|
1217
|
+
request.allocation_request, selected_set
|
|
1218
|
+
)
|
|
1219
|
+
feasible, dose = _base_feasible(request.allocation_request, selected_set)
|
|
1220
|
+
if not feasible: # Defensive after request validation.
|
|
1221
|
+
raise RuntimeError("winning realizable set became base-infeasible")
|
|
1222
|
+
feature_by_id = {value.option_id: value for value in request.member_features}
|
|
1223
|
+
member_by_id = {
|
|
1224
|
+
value.option_id: value for value in request.allocation_request.slate.members
|
|
1225
|
+
}
|
|
1226
|
+
score_rows = tuple(
|
|
1227
|
+
TargetConditionedMemberScore(
|
|
1228
|
+
option_id=option_id,
|
|
1229
|
+
option_identity_sha256=feature_by_id[
|
|
1230
|
+
option_id
|
|
1231
|
+
].option_identity_sha256,
|
|
1232
|
+
model_rank=member_by_id[option_id].model_rank,
|
|
1233
|
+
predicted_marginal=marginal[option_id],
|
|
1234
|
+
predicted_direction=direction[option_id],
|
|
1235
|
+
epistemic_uncertainty=uncertainty[option_id],
|
|
1236
|
+
marginal_z=marginal_z[option_id],
|
|
1237
|
+
direction_z=direction_z[option_id],
|
|
1238
|
+
uncertainty_z=uncertainty_z[option_id],
|
|
1239
|
+
final_score=scores[option_id],
|
|
1240
|
+
)
|
|
1241
|
+
for option_id in sorted(member_by_id)
|
|
1242
|
+
)
|
|
1243
|
+
selected = tuple(
|
|
1244
|
+
TargetConditionedAllocatedMember(
|
|
1245
|
+
option_id=member.option_id,
|
|
1246
|
+
option_identity_sha256=member.option_identity_sha256,
|
|
1247
|
+
model_rank=member.model_rank,
|
|
1248
|
+
acquisition_score=scores[member.option_id],
|
|
1249
|
+
)
|
|
1250
|
+
for member in selected_members
|
|
1251
|
+
)
|
|
1252
|
+
administered = tuple(
|
|
1253
|
+
sorted(
|
|
1254
|
+
{
|
|
1255
|
+
card
|
|
1256
|
+
for member in selected_members
|
|
1257
|
+
for card in member.supporting_card_keys
|
|
1258
|
+
if card in request.allocation_request.assigned_card_keys
|
|
1259
|
+
}
|
|
1260
|
+
)
|
|
1261
|
+
)
|
|
1262
|
+
return TargetConditionedSlateDecision(
|
|
1263
|
+
request=request,
|
|
1264
|
+
profile=self.profile,
|
|
1265
|
+
score_rows=score_rows,
|
|
1266
|
+
selected=selected,
|
|
1267
|
+
joint_score=sum(scores[value] for value in selected_set),
|
|
1268
|
+
feasible_subset_count=len(
|
|
1269
|
+
request.realizable_portfolios.option_id_sets
|
|
1270
|
+
),
|
|
1271
|
+
administered_card_keys=administered,
|
|
1272
|
+
memory_dose_assessment=dose,
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1275
|
+
def to_record(self) -> dict[str, object]:
|
|
1276
|
+
self.__post_init__()
|
|
1277
|
+
return {
|
|
1278
|
+
"policy_id": self.policy_id,
|
|
1279
|
+
"policy_version": self.policy_version,
|
|
1280
|
+
"definition_sha256": self.definition_sha256,
|
|
1281
|
+
"profile": self.profile.to_record(),
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1286
|
+
class TargetConditionedSelectedObservation:
|
|
1287
|
+
"""Two evaluator-derived labels for one actually selected member."""
|
|
1288
|
+
|
|
1289
|
+
decision_sha256: str
|
|
1290
|
+
campaign_generation: int
|
|
1291
|
+
option_id: str
|
|
1292
|
+
option_identity_sha256: str
|
|
1293
|
+
feature_row_sha256: str
|
|
1294
|
+
feature_values: tuple[float, ...]
|
|
1295
|
+
normalized_marginal_utility: float
|
|
1296
|
+
normalized_target_improvement: float
|
|
1297
|
+
evaluator_receipt_sha256: str
|
|
1298
|
+
|
|
1299
|
+
def __post_init__(self) -> None:
|
|
1300
|
+
for name in (
|
|
1301
|
+
"decision_sha256",
|
|
1302
|
+
"option_identity_sha256",
|
|
1303
|
+
"feature_row_sha256",
|
|
1304
|
+
"evaluator_receipt_sha256",
|
|
1305
|
+
):
|
|
1306
|
+
require_sha256(getattr(self, name), name)
|
|
1307
|
+
if type(self.campaign_generation) is not int or self.campaign_generation <= 0:
|
|
1308
|
+
raise ValueError("campaign_generation must be positive")
|
|
1309
|
+
if type(self.option_id) is not str or not self.option_id:
|
|
1310
|
+
raise ValueError("option_id must be non-empty")
|
|
1311
|
+
if type(self.feature_values) is not tuple or not self.feature_values:
|
|
1312
|
+
raise ValueError("feature_values must be non-empty")
|
|
1313
|
+
for value in self.feature_values:
|
|
1314
|
+
_finite(value, name="feature value")
|
|
1315
|
+
for name in (
|
|
1316
|
+
"normalized_marginal_utility",
|
|
1317
|
+
"normalized_target_improvement",
|
|
1318
|
+
):
|
|
1319
|
+
_finite(getattr(self, name), name=name)
|
|
1320
|
+
if not -1.0 <= self.normalized_target_improvement <= 1.0:
|
|
1321
|
+
raise ValueError("normalized_target_improvement must lie in [-1, 1]")
|
|
1322
|
+
|
|
1323
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1324
|
+
self.__post_init__()
|
|
1325
|
+
return {
|
|
1326
|
+
"decision_sha256": self.decision_sha256,
|
|
1327
|
+
"campaign_generation": self.campaign_generation,
|
|
1328
|
+
"option_id": self.option_id,
|
|
1329
|
+
"option_identity_sha256": self.option_identity_sha256,
|
|
1330
|
+
"feature_row_sha256": self.feature_row_sha256,
|
|
1331
|
+
"feature_values_hex": [value.hex() for value in self.feature_values],
|
|
1332
|
+
"normalized_marginal_utility_hex": (
|
|
1333
|
+
self.normalized_marginal_utility.hex()
|
|
1334
|
+
),
|
|
1335
|
+
"normalized_target_improvement_hex": (
|
|
1336
|
+
self.normalized_target_improvement.hex()
|
|
1337
|
+
),
|
|
1338
|
+
"evaluator_receipt_sha256": self.evaluator_receipt_sha256,
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
@property
|
|
1342
|
+
def observation_sha256(self) -> str:
|
|
1343
|
+
return _hash(_OBSERVATION_DOMAIN, self._unsigned_record())
|
|
1344
|
+
|
|
1345
|
+
def to_record(self) -> dict[str, object]:
|
|
1346
|
+
return {
|
|
1347
|
+
**self._unsigned_record(),
|
|
1348
|
+
"observation_sha256": self.observation_sha256,
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
def __eq__(self, other: object) -> bool:
|
|
1352
|
+
return (
|
|
1353
|
+
type(other) is TargetConditionedSelectedObservation
|
|
1354
|
+
and self.observation_sha256 == other.observation_sha256
|
|
1355
|
+
)
|
|
1356
|
+
|
|
1357
|
+
__hash__ = None
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
@dataclass(frozen=True, slots=True, eq=False)
|
|
1361
|
+
class TargetConditionedStateUpdateReceipt:
|
|
1362
|
+
prior_state_sha256: str
|
|
1363
|
+
next_state: TargetConditionedAcquisitionState
|
|
1364
|
+
campaign_generation: int
|
|
1365
|
+
decision_sha256s: tuple[str, ...]
|
|
1366
|
+
observation_sha256s: tuple[str, ...]
|
|
1367
|
+
|
|
1368
|
+
def __post_init__(self) -> None:
|
|
1369
|
+
require_sha256(self.prior_state_sha256, "prior_state_sha256")
|
|
1370
|
+
if type(self.next_state) is not TargetConditionedAcquisitionState:
|
|
1371
|
+
raise TypeError("next_state must be exact")
|
|
1372
|
+
self.next_state.__post_init__()
|
|
1373
|
+
if type(self.campaign_generation) is not int or self.campaign_generation <= 0:
|
|
1374
|
+
raise ValueError("campaign_generation must be positive")
|
|
1375
|
+
for name in ("decision_sha256s", "observation_sha256s"):
|
|
1376
|
+
values = getattr(self, name)
|
|
1377
|
+
if type(values) is not tuple or not values:
|
|
1378
|
+
raise ValueError(f"{name} must be non-empty")
|
|
1379
|
+
for value in values:
|
|
1380
|
+
require_sha256(value, name)
|
|
1381
|
+
if values != tuple(sorted(set(values))):
|
|
1382
|
+
raise ValueError(f"{name} must be unique and canonical")
|
|
1383
|
+
|
|
1384
|
+
def _unsigned_record(self) -> dict[str, object]:
|
|
1385
|
+
self.__post_init__()
|
|
1386
|
+
return {
|
|
1387
|
+
"schema_version": 1,
|
|
1388
|
+
"prior_state_sha256": self.prior_state_sha256,
|
|
1389
|
+
"next_state": self.next_state.to_record(),
|
|
1390
|
+
"campaign_generation": self.campaign_generation,
|
|
1391
|
+
"decision_sha256s": list(self.decision_sha256s),
|
|
1392
|
+
"observation_sha256s": list(self.observation_sha256s),
|
|
1393
|
+
"update_boundary": "all_concurrent_lanes_complete",
|
|
1394
|
+
"rejected_outcomes_consulted": False,
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
@property
|
|
1398
|
+
def update_sha256(self) -> str:
|
|
1399
|
+
return _hash(_UPDATE_DOMAIN, self._unsigned_record())
|
|
1400
|
+
|
|
1401
|
+
def to_record(self) -> dict[str, object]:
|
|
1402
|
+
return {**self._unsigned_record(), "update_sha256": self.update_sha256}
|
|
1403
|
+
|
|
1404
|
+
def __eq__(self, other: object) -> bool:
|
|
1405
|
+
return (
|
|
1406
|
+
type(other) is TargetConditionedStateUpdateReceipt
|
|
1407
|
+
and self.update_sha256 == other.update_sha256
|
|
1408
|
+
)
|
|
1409
|
+
|
|
1410
|
+
__hash__ = None
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
def update_target_conditioned_state(
|
|
1414
|
+
state: TargetConditionedAcquisitionState,
|
|
1415
|
+
*,
|
|
1416
|
+
decisions: tuple[TargetConditionedSlateDecision, ...],
|
|
1417
|
+
observations: tuple[TargetConditionedSelectedObservation, ...],
|
|
1418
|
+
) -> TargetConditionedStateUpdateReceipt:
|
|
1419
|
+
"""Update once after every concurrent lane in one generation completes."""
|
|
1420
|
+
|
|
1421
|
+
if type(state) is not TargetConditionedAcquisitionState:
|
|
1422
|
+
raise TypeError("state must be exact")
|
|
1423
|
+
state.__post_init__()
|
|
1424
|
+
if type(decisions) is not tuple or not decisions or any(
|
|
1425
|
+
type(value) is not TargetConditionedSlateDecision for value in decisions
|
|
1426
|
+
):
|
|
1427
|
+
raise TypeError("decisions must contain exact decisions")
|
|
1428
|
+
if type(observations) is not tuple or not observations or any(
|
|
1429
|
+
type(value) is not TargetConditionedSelectedObservation
|
|
1430
|
+
for value in observations
|
|
1431
|
+
):
|
|
1432
|
+
raise TypeError("observations must contain exact selected observations")
|
|
1433
|
+
for value in decisions:
|
|
1434
|
+
value.revalidate()
|
|
1435
|
+
if value.request.state != state:
|
|
1436
|
+
raise ValueError("concurrent decisions must share the exact prior state")
|
|
1437
|
+
for value in observations:
|
|
1438
|
+
value.__post_init__()
|
|
1439
|
+
generations = {value.request.campaign_generation for value in decisions}
|
|
1440
|
+
generations.update(value.campaign_generation for value in observations)
|
|
1441
|
+
if len(generations) != 1:
|
|
1442
|
+
raise ValueError("one update cannot mix campaign generations")
|
|
1443
|
+
generation = next(iter(generations))
|
|
1444
|
+
if state.cutoff_generation >= generation:
|
|
1445
|
+
raise ValueError("state cutoff reaches the update generation")
|
|
1446
|
+
decisions_by_sha = {value.decision_sha256: value for value in decisions}
|
|
1447
|
+
if len(decisions_by_sha) != len(decisions):
|
|
1448
|
+
raise ValueError("decisions must be unique")
|
|
1449
|
+
expected: dict[tuple[str, str], tuple[str, str, tuple[float, ...]]] = {}
|
|
1450
|
+
for decision in decisions:
|
|
1451
|
+
features = {
|
|
1452
|
+
value.option_id: value for value in decision.request.member_features
|
|
1453
|
+
}
|
|
1454
|
+
for selected in decision.selected:
|
|
1455
|
+
row = features[selected.option_id]
|
|
1456
|
+
expected[(decision.decision_sha256, selected.option_id)] = (
|
|
1457
|
+
selected.option_identity_sha256,
|
|
1458
|
+
row.feature_row_sha256,
|
|
1459
|
+
row.values,
|
|
1460
|
+
)
|
|
1461
|
+
observed = {
|
|
1462
|
+
(value.decision_sha256, value.option_id): value for value in observations
|
|
1463
|
+
}
|
|
1464
|
+
if len(observed) != len(observations) or set(observed) != set(expected):
|
|
1465
|
+
raise ValueError("updates must cover exactly every selected member once")
|
|
1466
|
+
ordered = []
|
|
1467
|
+
for key in sorted(expected):
|
|
1468
|
+
observation = observed[key]
|
|
1469
|
+
option_identity, feature_sha, feature_values = expected[key]
|
|
1470
|
+
if (
|
|
1471
|
+
observation.option_identity_sha256 != option_identity
|
|
1472
|
+
or observation.feature_row_sha256 != feature_sha
|
|
1473
|
+
or observation.feature_values != feature_values
|
|
1474
|
+
):
|
|
1475
|
+
raise ValueError("observation differs from its selected feature row")
|
|
1476
|
+
ordered.append(observation)
|
|
1477
|
+
next_state = TargetConditionedAcquisitionState(
|
|
1478
|
+
campaign_scope_sha256=state.campaign_scope_sha256,
|
|
1479
|
+
training_data_sha256=state.training_data_sha256,
|
|
1480
|
+
marginal_head=state.marginal_head.update(
|
|
1481
|
+
[value.feature_values for value in ordered],
|
|
1482
|
+
[value.normalized_marginal_utility for value in ordered],
|
|
1483
|
+
),
|
|
1484
|
+
direction_head=state.direction_head.update(
|
|
1485
|
+
[value.feature_values for value in ordered],
|
|
1486
|
+
[value.normalized_target_improvement for value in ordered],
|
|
1487
|
+
),
|
|
1488
|
+
cutoff_generation=generation,
|
|
1489
|
+
selected_observation_count=(
|
|
1490
|
+
state.selected_observation_count + len(ordered)
|
|
1491
|
+
),
|
|
1492
|
+
)
|
|
1493
|
+
return TargetConditionedStateUpdateReceipt(
|
|
1494
|
+
prior_state_sha256=state.state_sha256,
|
|
1495
|
+
next_state=next_state,
|
|
1496
|
+
campaign_generation=generation,
|
|
1497
|
+
decision_sha256s=tuple(sorted(decisions_by_sha)),
|
|
1498
|
+
observation_sha256s=tuple(
|
|
1499
|
+
sorted(value.observation_sha256 for value in ordered)
|
|
1500
|
+
),
|
|
1501
|
+
)
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
__all__ = [
|
|
1505
|
+
"BASE_REALIZABILITY_DEFINITION_SHA256",
|
|
1506
|
+
"BASE_REALIZABILITY_PROJECTOR_ID",
|
|
1507
|
+
"BASE_REALIZABILITY_PROJECTOR_VERSION",
|
|
1508
|
+
"POLICY_DEFINITION_SHA256",
|
|
1509
|
+
"POLICY_ID",
|
|
1510
|
+
"POLICY_VERSION",
|
|
1511
|
+
"PrequentialLinearGaussianHead",
|
|
1512
|
+
"RealizablePortfolioSet",
|
|
1513
|
+
"TargetConditionedAcquisitionProfile",
|
|
1514
|
+
"TargetConditionedAcquisitionState",
|
|
1515
|
+
"TargetConditionedAllocatedMember",
|
|
1516
|
+
"TargetConditionedMemberFeatures",
|
|
1517
|
+
"TargetConditionedMemberScore",
|
|
1518
|
+
"TargetConditionedMetaPrior",
|
|
1519
|
+
"TargetConditionedPrequentialSlatePolicy",
|
|
1520
|
+
"TargetConditionedSelectedObservation",
|
|
1521
|
+
"TargetConditionedSlateDecision",
|
|
1522
|
+
"TargetConditionedSlateRequest",
|
|
1523
|
+
"TargetConditionedSlateRole",
|
|
1524
|
+
"TargetConditionedStateUpdateReceipt",
|
|
1525
|
+
"enumerate_base_realizable_portfolios",
|
|
1526
|
+
"update_target_conditioned_state",
|
|
1527
|
+
]
|