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,1395 @@
|
|
|
1
|
+
"""Provider-free sequential replay over sealed markets with real outcomes.
|
|
2
|
+
|
|
3
|
+
A market record freezes one already-materialized candidate market together
|
|
4
|
+
with the archive it faced, an exact hypervolume reference, and the real
|
|
5
|
+
evaluated outcomes. The replay harness then re-runs any allocation policy
|
|
6
|
+
under the honest information boundary: at every step the policy sees ONLY
|
|
7
|
+
the outcomes of candidates it already selected, selects one next candidate,
|
|
8
|
+
and only then has that candidate's outcome revealed.
|
|
9
|
+
|
|
10
|
+
Outputs are causal receipts: per-step selections with propensities, exact
|
|
11
|
+
conditional marginal hypervolume gains, cumulative realized gain, and
|
|
12
|
+
regret against the exact oracle-K subset enumerated over evaluated
|
|
13
|
+
candidates only. Oracle ceilings are retrospective upper bounds over
|
|
14
|
+
already-generated candidates, never prospective performance claims.
|
|
15
|
+
|
|
16
|
+
Reference baseline policies (uniform random, native-rank round-robin,
|
|
17
|
+
frozen-score top-K, and the V70-style frozen lane-heads walk) are included
|
|
18
|
+
for comparison, plus an adapter that drives the composed V8-lite policy.
|
|
19
|
+
No provider, workload, objective-name, or model branch exists anywhere.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import hashlib
|
|
25
|
+
import itertools
|
|
26
|
+
import json
|
|
27
|
+
import math
|
|
28
|
+
import re
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
|
|
31
|
+
from agent_evolve.application.calibrated_positive_gain_opportunity import (
|
|
32
|
+
ObjectivePoint,
|
|
33
|
+
ObservedConversionOutcome,
|
|
34
|
+
PositiveGainForecast,
|
|
35
|
+
)
|
|
36
|
+
from agent_evolve.application.outcome_adaptive_action_racing import (
|
|
37
|
+
AdaptiveActionDescriptor,
|
|
38
|
+
AdaptiveActionOutcome,
|
|
39
|
+
)
|
|
40
|
+
from agent_evolve.application.v8lite_allocation_policy import (
|
|
41
|
+
V8LiteAllocationPolicy,
|
|
42
|
+
)
|
|
43
|
+
from agent_evolve.domain.patch import require_sha256
|
|
44
|
+
|
|
45
|
+
SEQUENTIAL_MARKET_REPLAY_ID = "sequential_market_replay"
|
|
46
|
+
SEQUENTIAL_MARKET_REPLAY_VERSION = 1
|
|
47
|
+
_TOKEN = re.compile(r"^[a-z][a-z0-9_.:-]{0,255}$")
|
|
48
|
+
_MARKET_DOMAIN = b"agent-evolve:sequential-replay-market:v1\x00"
|
|
49
|
+
_RESULT_DOMAIN = b"agent-evolve:sequential-replay-result:v1\x00"
|
|
50
|
+
|
|
51
|
+
#: Exhaustive oracle enumeration guard: beyond this many subsets the
|
|
52
|
+
#: harness refuses rather than silently approximating.
|
|
53
|
+
DEFAULT_ORACLE_SUBSET_LIMIT = 250_000
|
|
54
|
+
#: Union hypervolume is computed by float slicing, so a point lying
|
|
55
|
+
#: exactly on the archive frontier can yield a difference of a few
|
|
56
|
+
#: ulps instead of exact zero. Gains at or below this epsilon (in
|
|
57
|
+
#: normalized hypervolume units; real recorded gains are >= 1e-5) are
|
|
58
|
+
#: treated as exactly zero everywhere: replay marginals, oracle
|
|
59
|
+
#: positivity, and the exact gain port.
|
|
60
|
+
HYPERVOLUME_GAIN_EPSILON = 1.0e-12
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _clamped_gain(value: float) -> float:
|
|
64
|
+
return value if value > HYPERVOLUME_GAIN_EPSILON else 0.0
|
|
65
|
+
#: Neutral prior score used when a candidate carries no frozen score but
|
|
66
|
+
#: an adapter needs a probability-typed prior.
|
|
67
|
+
NEUTRAL_PRIOR_SCORE = 0.5
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _canonical_json(value: object) -> bytes:
|
|
71
|
+
return json.dumps(
|
|
72
|
+
value,
|
|
73
|
+
allow_nan=False,
|
|
74
|
+
ensure_ascii=True,
|
|
75
|
+
separators=(",", ":"),
|
|
76
|
+
sort_keys=True,
|
|
77
|
+
).encode("ascii", errors="strict")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _hash(domain: bytes, value: object) -> str:
|
|
81
|
+
return hashlib.sha256(domain + _canonical_json(value)).hexdigest()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _require_token(value: str, *, name: str) -> None:
|
|
85
|
+
if type(value) is not str or _TOKEN.fullmatch(value) is None:
|
|
86
|
+
raise ValueError(f"{name} must use the closed token grammar")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _stable_unit_interval(*parts: object) -> float:
|
|
90
|
+
payload = _canonical_json(list(parts))
|
|
91
|
+
numerator = int.from_bytes(hashlib.sha256(payload).digest()[:8], "big")
|
|
92
|
+
return numerator / float(2**64)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def exact_hypervolume(
|
|
96
|
+
points: tuple[tuple[float, ...], ...],
|
|
97
|
+
reference: tuple[float, ...],
|
|
98
|
+
) -> float:
|
|
99
|
+
"""Exact minimization hypervolume by recursive objective slicing."""
|
|
100
|
+
|
|
101
|
+
if type(reference) is not tuple or not reference:
|
|
102
|
+
raise ValueError("reference must be a non-empty exact tuple")
|
|
103
|
+
kept = sorted(
|
|
104
|
+
{
|
|
105
|
+
point
|
|
106
|
+
for point in points
|
|
107
|
+
if len(point) == len(reference)
|
|
108
|
+
and all(
|
|
109
|
+
value < bound
|
|
110
|
+
for value, bound in zip(point, reference, strict=True)
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
if not kept:
|
|
115
|
+
return 0.0
|
|
116
|
+
if len(reference) == 1:
|
|
117
|
+
return reference[0] - min(point[0] for point in kept)
|
|
118
|
+
kept.sort(key=lambda point: point[-1])
|
|
119
|
+
total = 0.0
|
|
120
|
+
for index, point in enumerate(kept):
|
|
121
|
+
z_low = point[-1]
|
|
122
|
+
z_high = (
|
|
123
|
+
kept[index + 1][-1]
|
|
124
|
+
if index + 1 < len(kept)
|
|
125
|
+
else reference[-1]
|
|
126
|
+
)
|
|
127
|
+
if z_high > z_low:
|
|
128
|
+
slab = exact_hypervolume(
|
|
129
|
+
tuple(value[:-1] for value in kept[: index + 1]),
|
|
130
|
+
reference[:-1],
|
|
131
|
+
)
|
|
132
|
+
total += slab * (z_high - z_low)
|
|
133
|
+
return total
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _point_values(
|
|
137
|
+
point: ObjectivePoint,
|
|
138
|
+
metric_ids: tuple[str, ...],
|
|
139
|
+
) -> tuple[float, ...]:
|
|
140
|
+
mapping = dict(point)
|
|
141
|
+
if tuple(sorted(mapping)) != metric_ids:
|
|
142
|
+
raise ValueError("objective point uses a foreign metric frame")
|
|
143
|
+
return tuple(mapping[metric_id] for metric_id in metric_ids)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass(frozen=True, slots=True)
|
|
147
|
+
class MarketCandidateRecord:
|
|
148
|
+
"""One frozen market candidate with its real outcome, if any."""
|
|
149
|
+
|
|
150
|
+
action_sha256: str
|
|
151
|
+
engine_id: str
|
|
152
|
+
native_rank: int
|
|
153
|
+
frozen_score: float | None
|
|
154
|
+
forecast: PositiveGainForecast | None
|
|
155
|
+
evaluated: bool
|
|
156
|
+
feasible: bool
|
|
157
|
+
objectives: ObjectivePoint | None
|
|
158
|
+
#: The candidate's parent objective point, known at proposal time.
|
|
159
|
+
anchor_point: ObjectivePoint | None = None
|
|
160
|
+
|
|
161
|
+
def __post_init__(self) -> None:
|
|
162
|
+
require_sha256(self.action_sha256, "action_sha256")
|
|
163
|
+
_require_token(self.engine_id, name="engine_id")
|
|
164
|
+
if type(self.native_rank) is not int or self.native_rank <= 0:
|
|
165
|
+
raise ValueError("native_rank must be positive")
|
|
166
|
+
if self.frozen_score is not None and (
|
|
167
|
+
type(self.frozen_score) is not float
|
|
168
|
+
or not math.isfinite(self.frozen_score)
|
|
169
|
+
):
|
|
170
|
+
raise ValueError("frozen_score must be finite or None")
|
|
171
|
+
if self.forecast is not None:
|
|
172
|
+
if type(self.forecast) is not PositiveGainForecast:
|
|
173
|
+
raise TypeError("forecast must be exact or None")
|
|
174
|
+
self.forecast.__post_init__()
|
|
175
|
+
if type(self.evaluated) is not bool or type(self.feasible) is not bool:
|
|
176
|
+
raise TypeError("evaluated and feasible must be exact")
|
|
177
|
+
if self.feasible and not self.evaluated:
|
|
178
|
+
raise ValueError("a feasible candidate must be evaluated")
|
|
179
|
+
if (self.objectives is not None) != self.feasible:
|
|
180
|
+
raise ValueError(
|
|
181
|
+
"objectives must be present exactly for feasible candidates"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def to_record(self) -> dict[str, object]:
|
|
185
|
+
self.__post_init__()
|
|
186
|
+
record = {
|
|
187
|
+
"action_sha256": self.action_sha256,
|
|
188
|
+
"engine_id": self.engine_id,
|
|
189
|
+
"native_rank": self.native_rank,
|
|
190
|
+
"frozen_score_hex": (
|
|
191
|
+
None
|
|
192
|
+
if self.frozen_score is None
|
|
193
|
+
else self.frozen_score.hex()
|
|
194
|
+
),
|
|
195
|
+
"forecast": (
|
|
196
|
+
None
|
|
197
|
+
if self.forecast is None
|
|
198
|
+
else self.forecast.to_record()
|
|
199
|
+
),
|
|
200
|
+
"evaluated": self.evaluated,
|
|
201
|
+
"feasible": self.feasible,
|
|
202
|
+
"objectives": (
|
|
203
|
+
None
|
|
204
|
+
if self.objectives is None
|
|
205
|
+
else [
|
|
206
|
+
{"metric_id": metric_id, "value_hex": value.hex()}
|
|
207
|
+
for metric_id, value in self.objectives
|
|
208
|
+
]
|
|
209
|
+
),
|
|
210
|
+
}
|
|
211
|
+
# Emitted only when present so a market recorded without parent
|
|
212
|
+
# anchors keeps its pre-existing bytes and market_sha256.
|
|
213
|
+
if self.anchor_point is not None:
|
|
214
|
+
record["anchor_point"] = [
|
|
215
|
+
{"metric_id": metric_id, "value_hex": value.hex()}
|
|
216
|
+
for metric_id, value in self.anchor_point
|
|
217
|
+
]
|
|
218
|
+
return record
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(frozen=True, slots=True)
|
|
222
|
+
class MarketRecord:
|
|
223
|
+
"""Minimal sealed-market view sufficient for allocator replay."""
|
|
224
|
+
|
|
225
|
+
market_id: str
|
|
226
|
+
archive_points: tuple[ObjectivePoint, ...]
|
|
227
|
+
hv_reference_point: ObjectivePoint
|
|
228
|
+
candidates: tuple[MarketCandidateRecord, ...]
|
|
229
|
+
market_sha256: str = field(init=False)
|
|
230
|
+
|
|
231
|
+
def __post_init__(self) -> None:
|
|
232
|
+
_require_token(self.market_id, name="market_id")
|
|
233
|
+
if (
|
|
234
|
+
type(self.hv_reference_point) is not tuple
|
|
235
|
+
or not self.hv_reference_point
|
|
236
|
+
or self.hv_reference_point
|
|
237
|
+
!= tuple(sorted(self.hv_reference_point))
|
|
238
|
+
):
|
|
239
|
+
raise ValueError(
|
|
240
|
+
"hv_reference_point must be non-empty and canonical"
|
|
241
|
+
)
|
|
242
|
+
metric_ids = self.metric_ids
|
|
243
|
+
if type(self.archive_points) is not tuple or not self.archive_points:
|
|
244
|
+
raise ValueError("archive_points must be non-empty")
|
|
245
|
+
for point in self.archive_points:
|
|
246
|
+
_point_values(point, metric_ids)
|
|
247
|
+
if type(self.candidates) is not tuple or not self.candidates:
|
|
248
|
+
raise ValueError("candidates must be non-empty")
|
|
249
|
+
seen: set[str] = set()
|
|
250
|
+
lanes: dict[str, list[int]] = {}
|
|
251
|
+
for value in self.candidates:
|
|
252
|
+
if type(value) is not MarketCandidateRecord:
|
|
253
|
+
raise TypeError("candidates must be exact market records")
|
|
254
|
+
value.__post_init__()
|
|
255
|
+
if value.action_sha256 in seen:
|
|
256
|
+
raise ValueError("candidate identities repeat")
|
|
257
|
+
seen.add(value.action_sha256)
|
|
258
|
+
lanes.setdefault(value.engine_id, []).append(
|
|
259
|
+
value.native_rank
|
|
260
|
+
)
|
|
261
|
+
if value.objectives is not None:
|
|
262
|
+
_point_values(value.objectives, metric_ids)
|
|
263
|
+
for engine_id, ranks in lanes.items():
|
|
264
|
+
if sorted(ranks) != list(range(1, len(ranks) + 1)):
|
|
265
|
+
raise ValueError(
|
|
266
|
+
f"engine {engine_id} ranks must be contiguous"
|
|
267
|
+
)
|
|
268
|
+
object.__setattr__(
|
|
269
|
+
self,
|
|
270
|
+
"market_sha256",
|
|
271
|
+
_hash(
|
|
272
|
+
_MARKET_DOMAIN,
|
|
273
|
+
{
|
|
274
|
+
"schema_version": 1,
|
|
275
|
+
"market_id": self.market_id,
|
|
276
|
+
"hv_reference_point": [
|
|
277
|
+
{
|
|
278
|
+
"metric_id": metric_id,
|
|
279
|
+
"value_hex": value.hex(),
|
|
280
|
+
}
|
|
281
|
+
for metric_id, value in self.hv_reference_point
|
|
282
|
+
],
|
|
283
|
+
"archive_points": [
|
|
284
|
+
[
|
|
285
|
+
{
|
|
286
|
+
"metric_id": metric_id,
|
|
287
|
+
"value_hex": value.hex(),
|
|
288
|
+
}
|
|
289
|
+
for metric_id, value in point
|
|
290
|
+
]
|
|
291
|
+
for point in self.archive_points
|
|
292
|
+
],
|
|
293
|
+
"candidates": [
|
|
294
|
+
value.to_record() for value in self.candidates
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def metric_ids(self) -> tuple[str, ...]:
|
|
302
|
+
return tuple(
|
|
303
|
+
metric_id for metric_id, _value in self.hv_reference_point
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def candidate(self, action_sha256: str) -> MarketCandidateRecord:
|
|
307
|
+
for value in self.candidates:
|
|
308
|
+
if value.action_sha256 == action_sha256:
|
|
309
|
+
return value
|
|
310
|
+
raise ValueError("candidate is outside the market")
|
|
311
|
+
|
|
312
|
+
def hypervolume(
|
|
313
|
+
self,
|
|
314
|
+
extra_points: tuple[ObjectivePoint, ...] = (),
|
|
315
|
+
) -> float:
|
|
316
|
+
metric_ids = self.metric_ids
|
|
317
|
+
reference = _point_values(self.hv_reference_point, metric_ids)
|
|
318
|
+
return exact_hypervolume(
|
|
319
|
+
tuple(
|
|
320
|
+
_point_values(point, metric_ids)
|
|
321
|
+
for point in (*self.archive_points, *extra_points)
|
|
322
|
+
),
|
|
323
|
+
reference,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class ExactHypervolumeGainPort:
|
|
328
|
+
"""Module-level gain port valuing points against arbitrary archives."""
|
|
329
|
+
|
|
330
|
+
utility_id = "exact_hypervolume_gain"
|
|
331
|
+
utility_version = 1
|
|
332
|
+
|
|
333
|
+
def __init__(self, hv_reference_point: ObjectivePoint) -> None:
|
|
334
|
+
self._reference = hv_reference_point
|
|
335
|
+
self._metric_ids = tuple(
|
|
336
|
+
metric_id for metric_id, _value in hv_reference_point
|
|
337
|
+
)
|
|
338
|
+
self.definition_sha256 = _hash(
|
|
339
|
+
_MARKET_DOMAIN,
|
|
340
|
+
{
|
|
341
|
+
"utility": self.utility_id,
|
|
342
|
+
"reference": [
|
|
343
|
+
{"metric_id": metric_id, "value_hex": value.hex()}
|
|
344
|
+
for metric_id, value in hv_reference_point
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def marginal_archive_gain(
|
|
350
|
+
self,
|
|
351
|
+
archive_points: tuple[ObjectivePoint, ...],
|
|
352
|
+
objective_point: ObjectivePoint,
|
|
353
|
+
) -> float:
|
|
354
|
+
reference = _point_values(self._reference, self._metric_ids)
|
|
355
|
+
base = tuple(
|
|
356
|
+
_point_values(point, self._metric_ids)
|
|
357
|
+
for point in archive_points
|
|
358
|
+
)
|
|
359
|
+
with_point = (
|
|
360
|
+
*base,
|
|
361
|
+
_point_values(objective_point, self._metric_ids),
|
|
362
|
+
)
|
|
363
|
+
return _clamped_gain(
|
|
364
|
+
float(
|
|
365
|
+
exact_hypervolume(with_point, reference)
|
|
366
|
+
- exact_hypervolume(base, reference)
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@dataclass(frozen=True, slots=True)
|
|
372
|
+
class ReplaySelection:
|
|
373
|
+
"""One policy answer: which candidate, at what propensity."""
|
|
374
|
+
|
|
375
|
+
action_sha256: str
|
|
376
|
+
selection_propensity: float
|
|
377
|
+
evidence: dict[str, object] = field(default_factory=dict)
|
|
378
|
+
|
|
379
|
+
def __post_init__(self) -> None:
|
|
380
|
+
require_sha256(self.action_sha256, "action_sha256")
|
|
381
|
+
if (
|
|
382
|
+
type(self.selection_propensity) is not float
|
|
383
|
+
or not math.isfinite(self.selection_propensity)
|
|
384
|
+
or not 0.0 < self.selection_propensity <= 1.0
|
|
385
|
+
):
|
|
386
|
+
raise ValueError(
|
|
387
|
+
"selection_propensity must be a positive probability"
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
@dataclass(frozen=True, slots=True)
|
|
392
|
+
class ReplayStepReceipt:
|
|
393
|
+
step_index: int
|
|
394
|
+
action_sha256: str
|
|
395
|
+
selection_propensity: float
|
|
396
|
+
evaluated: bool
|
|
397
|
+
feasible: bool
|
|
398
|
+
imputed_zero_outcome: bool
|
|
399
|
+
marginal_gain: float
|
|
400
|
+
cumulative_gain: float
|
|
401
|
+
|
|
402
|
+
def to_record(self) -> dict[str, object]:
|
|
403
|
+
return {
|
|
404
|
+
"step_index": self.step_index,
|
|
405
|
+
"action_sha256": self.action_sha256,
|
|
406
|
+
"selection_propensity_hex": (
|
|
407
|
+
self.selection_propensity.hex()
|
|
408
|
+
),
|
|
409
|
+
"evaluated": self.evaluated,
|
|
410
|
+
"feasible": self.feasible,
|
|
411
|
+
"imputed_zero_outcome": self.imputed_zero_outcome,
|
|
412
|
+
"marginal_gain_hex": self.marginal_gain.hex(),
|
|
413
|
+
"cumulative_gain_hex": self.cumulative_gain.hex(),
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
@dataclass(frozen=True, slots=True)
|
|
418
|
+
class ReplayResult:
|
|
419
|
+
market_sha256: str
|
|
420
|
+
policy_id: str
|
|
421
|
+
budget: int
|
|
422
|
+
restricted_to_evaluated: bool
|
|
423
|
+
receipts: tuple[ReplayStepReceipt, ...]
|
|
424
|
+
realized_gain: float
|
|
425
|
+
oracle_gain: float
|
|
426
|
+
oracle_subset: tuple[str, ...]
|
|
427
|
+
regret: float
|
|
428
|
+
result_sha256: str = field(init=False)
|
|
429
|
+
|
|
430
|
+
def __post_init__(self) -> None:
|
|
431
|
+
object.__setattr__(
|
|
432
|
+
self,
|
|
433
|
+
"result_sha256",
|
|
434
|
+
_hash(_RESULT_DOMAIN, self.to_record()),
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def selected_action_sha256s(self) -> tuple[str, ...]:
|
|
439
|
+
return tuple(
|
|
440
|
+
value.action_sha256 for value in self.receipts
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
def to_record(self) -> dict[str, object]:
|
|
444
|
+
return {
|
|
445
|
+
"schema_version": 1,
|
|
446
|
+
"market_sha256": self.market_sha256,
|
|
447
|
+
"policy_id": self.policy_id,
|
|
448
|
+
"budget": self.budget,
|
|
449
|
+
"restricted_to_evaluated": self.restricted_to_evaluated,
|
|
450
|
+
"receipts": [value.to_record() for value in self.receipts],
|
|
451
|
+
"realized_gain_hex": self.realized_gain.hex(),
|
|
452
|
+
"oracle_gain_hex": self.oracle_gain.hex(),
|
|
453
|
+
"oracle_subset": list(self.oracle_subset),
|
|
454
|
+
"regret_hex": self.regret.hex(),
|
|
455
|
+
"oracle_is_retrospective_upper_bound": True,
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
class UniformRandomReplayPolicy:
|
|
460
|
+
"""Seeded uniform selection over the selectable support."""
|
|
461
|
+
|
|
462
|
+
def __init__(self, seed: int) -> None:
|
|
463
|
+
self.policy_id = "replay_baseline.uniform_random"
|
|
464
|
+
self._seed = seed
|
|
465
|
+
|
|
466
|
+
def select(
|
|
467
|
+
self,
|
|
468
|
+
*,
|
|
469
|
+
record: MarketRecord,
|
|
470
|
+
revealed: tuple[ReplayStepReceipt, ...],
|
|
471
|
+
selectable_action_sha256s: tuple[str, ...],
|
|
472
|
+
step_index: int,
|
|
473
|
+
budget: int,
|
|
474
|
+
) -> ReplaySelection:
|
|
475
|
+
# The draw must not condition on unrevealed outcomes, so it
|
|
476
|
+
# hashes only the outcome-blind market identity and support.
|
|
477
|
+
draw = _stable_unit_interval(
|
|
478
|
+
self._seed,
|
|
479
|
+
record.market_id,
|
|
480
|
+
"uniform_random",
|
|
481
|
+
step_index,
|
|
482
|
+
list(selectable_action_sha256s),
|
|
483
|
+
)
|
|
484
|
+
support = tuple(sorted(selectable_action_sha256s))
|
|
485
|
+
chosen = support[
|
|
486
|
+
min(int(draw * len(support)), len(support) - 1)
|
|
487
|
+
]
|
|
488
|
+
return ReplaySelection(
|
|
489
|
+
action_sha256=chosen,
|
|
490
|
+
selection_propensity=1.0 / len(support),
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
class NativeRankRoundRobinReplayPolicy:
|
|
495
|
+
"""Round-robin engines in canonical order; best native rank first."""
|
|
496
|
+
|
|
497
|
+
def __init__(self) -> None:
|
|
498
|
+
self.policy_id = "replay_baseline.native_rank_round_robin"
|
|
499
|
+
|
|
500
|
+
def select(
|
|
501
|
+
self,
|
|
502
|
+
*,
|
|
503
|
+
record: MarketRecord,
|
|
504
|
+
revealed: tuple[ReplayStepReceipt, ...],
|
|
505
|
+
selectable_action_sha256s: tuple[str, ...],
|
|
506
|
+
step_index: int,
|
|
507
|
+
budget: int,
|
|
508
|
+
) -> ReplaySelection:
|
|
509
|
+
selectable = set(selectable_action_sha256s)
|
|
510
|
+
engines = sorted(
|
|
511
|
+
{
|
|
512
|
+
value.engine_id
|
|
513
|
+
for value in record.candidates
|
|
514
|
+
if value.action_sha256 in selectable
|
|
515
|
+
}
|
|
516
|
+
)
|
|
517
|
+
engine_id = engines[step_index % len(engines)]
|
|
518
|
+
chosen = min(
|
|
519
|
+
(
|
|
520
|
+
value
|
|
521
|
+
for value in record.candidates
|
|
522
|
+
if value.action_sha256 in selectable
|
|
523
|
+
and value.engine_id == engine_id
|
|
524
|
+
),
|
|
525
|
+
key=lambda value: (value.native_rank, value.action_sha256),
|
|
526
|
+
)
|
|
527
|
+
return ReplaySelection(
|
|
528
|
+
action_sha256=chosen.action_sha256,
|
|
529
|
+
selection_propensity=1.0,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class FrozenScoreTopKReplayPolicy:
|
|
534
|
+
"""Descending global frozen score; native rank breaks ties."""
|
|
535
|
+
|
|
536
|
+
def __init__(self) -> None:
|
|
537
|
+
self.policy_id = "replay_baseline.frozen_score_topk"
|
|
538
|
+
|
|
539
|
+
def select(
|
|
540
|
+
self,
|
|
541
|
+
*,
|
|
542
|
+
record: MarketRecord,
|
|
543
|
+
revealed: tuple[ReplayStepReceipt, ...],
|
|
544
|
+
selectable_action_sha256s: tuple[str, ...],
|
|
545
|
+
step_index: int,
|
|
546
|
+
budget: int,
|
|
547
|
+
) -> ReplaySelection:
|
|
548
|
+
selectable = set(selectable_action_sha256s)
|
|
549
|
+
chosen = min(
|
|
550
|
+
(
|
|
551
|
+
value
|
|
552
|
+
for value in record.candidates
|
|
553
|
+
if value.action_sha256 in selectable
|
|
554
|
+
),
|
|
555
|
+
key=lambda value: (
|
|
556
|
+
-(
|
|
557
|
+
value.frozen_score
|
|
558
|
+
if value.frozen_score is not None
|
|
559
|
+
else NEUTRAL_PRIOR_SCORE
|
|
560
|
+
),
|
|
561
|
+
value.native_rank,
|
|
562
|
+
value.action_sha256,
|
|
563
|
+
),
|
|
564
|
+
)
|
|
565
|
+
return ReplaySelection(
|
|
566
|
+
action_sha256=chosen.action_sha256,
|
|
567
|
+
selection_propensity=1.0,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
class LaneHeadsReplayPolicy:
|
|
572
|
+
"""V70 D4 behavior: one frozen-score lane head per engine, cycled."""
|
|
573
|
+
|
|
574
|
+
def __init__(self) -> None:
|
|
575
|
+
self.policy_id = "replay_baseline.lane_heads"
|
|
576
|
+
|
|
577
|
+
def select(
|
|
578
|
+
self,
|
|
579
|
+
*,
|
|
580
|
+
record: MarketRecord,
|
|
581
|
+
revealed: tuple[ReplayStepReceipt, ...],
|
|
582
|
+
selectable_action_sha256s: tuple[str, ...],
|
|
583
|
+
step_index: int,
|
|
584
|
+
budget: int,
|
|
585
|
+
) -> ReplaySelection:
|
|
586
|
+
def frozen(value: MarketCandidateRecord) -> float:
|
|
587
|
+
return (
|
|
588
|
+
value.frozen_score
|
|
589
|
+
if value.frozen_score is not None
|
|
590
|
+
else NEUTRAL_PRIOR_SCORE
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
universe = tuple(
|
|
594
|
+
sorted(
|
|
595
|
+
(
|
|
596
|
+
value
|
|
597
|
+
for value in record.candidates
|
|
598
|
+
if value.action_sha256
|
|
599
|
+
in set(selectable_action_sha256s)
|
|
600
|
+
or any(
|
|
601
|
+
value.action_sha256 == item.action_sha256
|
|
602
|
+
for item in revealed
|
|
603
|
+
)
|
|
604
|
+
),
|
|
605
|
+
key=lambda value: value.action_sha256,
|
|
606
|
+
)
|
|
607
|
+
)
|
|
608
|
+
by_engine: dict[str, list[MarketCandidateRecord]] = {}
|
|
609
|
+
for value in universe:
|
|
610
|
+
by_engine.setdefault(value.engine_id, []).append(value)
|
|
611
|
+
for values in by_engine.values():
|
|
612
|
+
values.sort(
|
|
613
|
+
key=lambda value: (
|
|
614
|
+
-frozen(value),
|
|
615
|
+
value.native_rank,
|
|
616
|
+
value.action_sha256,
|
|
617
|
+
)
|
|
618
|
+
)
|
|
619
|
+
engine_order = sorted(
|
|
620
|
+
by_engine,
|
|
621
|
+
key=lambda engine_id: (
|
|
622
|
+
-frozen(by_engine[engine_id][0]),
|
|
623
|
+
engine_id,
|
|
624
|
+
),
|
|
625
|
+
)
|
|
626
|
+
walk = []
|
|
627
|
+
depth = 0
|
|
628
|
+
while len(walk) < len(universe):
|
|
629
|
+
for engine_id in engine_order:
|
|
630
|
+
if depth < len(by_engine[engine_id]):
|
|
631
|
+
walk.append(by_engine[engine_id][depth])
|
|
632
|
+
depth += 1
|
|
633
|
+
selectable = set(selectable_action_sha256s)
|
|
634
|
+
chosen = next(
|
|
635
|
+
value
|
|
636
|
+
for value in walk
|
|
637
|
+
if value.action_sha256 in selectable
|
|
638
|
+
)
|
|
639
|
+
return ReplaySelection(
|
|
640
|
+
action_sha256=chosen.action_sha256,
|
|
641
|
+
selection_propensity=1.0,
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
class V8LiteReplayPolicy:
|
|
646
|
+
"""Drive the composed V8-lite policy inside the replay boundary.
|
|
647
|
+
|
|
648
|
+
The eligible universe is the harness's fixed selectable support plus
|
|
649
|
+
already-selected candidates; native ranks are re-ranked densely
|
|
650
|
+
within that universe so descriptor lane invariants hold. Candidates
|
|
651
|
+
without a frozen score receive the neutral prior score.
|
|
652
|
+
"""
|
|
653
|
+
|
|
654
|
+
def __init__(
|
|
655
|
+
self,
|
|
656
|
+
policy: V8LiteAllocationPolicy,
|
|
657
|
+
*,
|
|
658
|
+
frozen_fit_training_run_count: int = 0,
|
|
659
|
+
prior_conversion_outcomes: tuple[
|
|
660
|
+
ObservedConversionOutcome,
|
|
661
|
+
...,
|
|
662
|
+
] = (),
|
|
663
|
+
) -> None:
|
|
664
|
+
if type(policy) is not V8LiteAllocationPolicy:
|
|
665
|
+
raise TypeError("policy must be an exact V8-lite policy")
|
|
666
|
+
policy.__post_init__()
|
|
667
|
+
self.policy_id = "replay_adapter.v8lite"
|
|
668
|
+
self._policy = policy
|
|
669
|
+
self._frozen_fit_training_run_count = (
|
|
670
|
+
frozen_fit_training_run_count
|
|
671
|
+
)
|
|
672
|
+
self._prior_conversion_outcomes = prior_conversion_outcomes
|
|
673
|
+
|
|
674
|
+
@staticmethod
|
|
675
|
+
def _phenotype(action_sha256: str) -> str:
|
|
676
|
+
return hashlib.sha256(
|
|
677
|
+
f"phenotype:{action_sha256}".encode("ascii")
|
|
678
|
+
).hexdigest()
|
|
679
|
+
|
|
680
|
+
@staticmethod
|
|
681
|
+
def _outcome_blind_request_sha256(
|
|
682
|
+
record: MarketRecord,
|
|
683
|
+
universe_ids: tuple[str, ...],
|
|
684
|
+
) -> str:
|
|
685
|
+
"""Cutoff identity that never conditions on unrevealed outcomes."""
|
|
686
|
+
|
|
687
|
+
return hashlib.sha256(
|
|
688
|
+
_canonical_json(
|
|
689
|
+
[
|
|
690
|
+
"replay-request",
|
|
691
|
+
record.market_id,
|
|
692
|
+
list(universe_ids),
|
|
693
|
+
]
|
|
694
|
+
)
|
|
695
|
+
).hexdigest()
|
|
696
|
+
|
|
697
|
+
def _descriptors(
|
|
698
|
+
self,
|
|
699
|
+
record: MarketRecord,
|
|
700
|
+
universe_ids: tuple[str, ...],
|
|
701
|
+
) -> tuple[AdaptiveActionDescriptor, ...]:
|
|
702
|
+
universe = tuple(
|
|
703
|
+
record.candidate(value) for value in universe_ids
|
|
704
|
+
)
|
|
705
|
+
by_engine: dict[str, list[MarketCandidateRecord]] = {}
|
|
706
|
+
for value in universe:
|
|
707
|
+
by_engine.setdefault(value.engine_id, []).append(value)
|
|
708
|
+
descriptors: list[AdaptiveActionDescriptor] = []
|
|
709
|
+
for engine_id in sorted(by_engine):
|
|
710
|
+
lane = sorted(
|
|
711
|
+
by_engine[engine_id],
|
|
712
|
+
key=lambda value: (
|
|
713
|
+
value.native_rank,
|
|
714
|
+
value.action_sha256,
|
|
715
|
+
),
|
|
716
|
+
)
|
|
717
|
+
for dense_rank, value in enumerate(lane, start=1):
|
|
718
|
+
prior = (
|
|
719
|
+
value.frozen_score
|
|
720
|
+
if value.frozen_score is not None
|
|
721
|
+
else NEUTRAL_PRIOR_SCORE
|
|
722
|
+
)
|
|
723
|
+
descriptors.append(
|
|
724
|
+
AdaptiveActionDescriptor(
|
|
725
|
+
action_sha256=value.action_sha256,
|
|
726
|
+
phenotype_sha256=self._phenotype(
|
|
727
|
+
value.action_sha256
|
|
728
|
+
),
|
|
729
|
+
lane_id=engine_id,
|
|
730
|
+
operator_id=engine_id,
|
|
731
|
+
native_rank=dense_rank,
|
|
732
|
+
lane_size=len(lane),
|
|
733
|
+
prior_score=float(
|
|
734
|
+
min(max(prior, 0.0), 1.0)
|
|
735
|
+
),
|
|
736
|
+
parent_generated_in_current_run=False,
|
|
737
|
+
)
|
|
738
|
+
)
|
|
739
|
+
return tuple(descriptors)
|
|
740
|
+
|
|
741
|
+
def select(
|
|
742
|
+
self,
|
|
743
|
+
*,
|
|
744
|
+
record: MarketRecord,
|
|
745
|
+
revealed: tuple[ReplayStepReceipt, ...],
|
|
746
|
+
selectable_action_sha256s: tuple[str, ...],
|
|
747
|
+
step_index: int,
|
|
748
|
+
budget: int,
|
|
749
|
+
) -> ReplaySelection:
|
|
750
|
+
universe_ids = tuple(
|
|
751
|
+
sorted(
|
|
752
|
+
{
|
|
753
|
+
*selectable_action_sha256s,
|
|
754
|
+
*(value.action_sha256 for value in revealed),
|
|
755
|
+
}
|
|
756
|
+
)
|
|
757
|
+
)
|
|
758
|
+
descriptors = self._descriptors(record, universe_ids)
|
|
759
|
+
request_sha256 = self._outcome_blind_request_sha256(
|
|
760
|
+
record,
|
|
761
|
+
universe_ids,
|
|
762
|
+
)
|
|
763
|
+
pilot_width = self._policy.pilot_width_for(
|
|
764
|
+
evaluation_slots=budget,
|
|
765
|
+
engine_count=len(
|
|
766
|
+
{value.lane_id for value in descriptors}
|
|
767
|
+
),
|
|
768
|
+
)
|
|
769
|
+
if pilot_width <= 0:
|
|
770
|
+
raise ValueError("replay budget leaves no pilot")
|
|
771
|
+
selected_ids = tuple(
|
|
772
|
+
sorted(value.action_sha256 for value in revealed)
|
|
773
|
+
)
|
|
774
|
+
outcome_by_action = {
|
|
775
|
+
value.action_sha256: value for value in revealed
|
|
776
|
+
}
|
|
777
|
+
outcomes = tuple(
|
|
778
|
+
AdaptiveActionOutcome(
|
|
779
|
+
action_sha256=action_sha256,
|
|
780
|
+
evaluation_sha256=hashlib.sha256(
|
|
781
|
+
f"replay-evaluation:{action_sha256}".encode(
|
|
782
|
+
"ascii"
|
|
783
|
+
)
|
|
784
|
+
).hexdigest(),
|
|
785
|
+
feasible=outcome_by_action[action_sha256].feasible,
|
|
786
|
+
marginal_archive_gain=(
|
|
787
|
+
outcome_by_action[action_sha256].marginal_gain
|
|
788
|
+
),
|
|
789
|
+
)
|
|
790
|
+
for action_sha256 in selected_ids
|
|
791
|
+
)
|
|
792
|
+
if step_index < pilot_width:
|
|
793
|
+
if self._policy.revision_2:
|
|
794
|
+
decision = self._policy.design_pilot_seat(
|
|
795
|
+
residual_request_sha256=request_sha256,
|
|
796
|
+
actions=descriptors,
|
|
797
|
+
evaluation_slots=budget,
|
|
798
|
+
selected_action_sha256s=selected_ids,
|
|
799
|
+
outcomes=outcomes,
|
|
800
|
+
)
|
|
801
|
+
return ReplaySelection(
|
|
802
|
+
action_sha256=(
|
|
803
|
+
decision.selected_action_sha256s[0]
|
|
804
|
+
),
|
|
805
|
+
selection_propensity=(
|
|
806
|
+
decision.selection_propensity
|
|
807
|
+
),
|
|
808
|
+
evidence={"phase": decision.phase},
|
|
809
|
+
)
|
|
810
|
+
decision = self._policy.design_pilot(
|
|
811
|
+
residual_request_sha256=request_sha256,
|
|
812
|
+
actions=descriptors,
|
|
813
|
+
evaluation_slots=budget,
|
|
814
|
+
)
|
|
815
|
+
seat = decision.pilot_design.seats[step_index]
|
|
816
|
+
return ReplaySelection(
|
|
817
|
+
action_sha256=seat.selected_action_sha256,
|
|
818
|
+
selection_propensity=seat.selection_propensity,
|
|
819
|
+
evidence={"phase": decision.phase},
|
|
820
|
+
)
|
|
821
|
+
pilot_ids = tuple(
|
|
822
|
+
sorted(
|
|
823
|
+
value.action_sha256
|
|
824
|
+
for value in revealed[:pilot_width]
|
|
825
|
+
)
|
|
826
|
+
)
|
|
827
|
+
pilot_points = tuple(
|
|
828
|
+
record.candidate(value).objectives
|
|
829
|
+
for value in pilot_ids
|
|
830
|
+
if record.candidate(value).objectives is not None
|
|
831
|
+
)
|
|
832
|
+
diagnostic_joint_gain = _clamped_gain(
|
|
833
|
+
float(
|
|
834
|
+
record.hypervolume(pilot_points)
|
|
835
|
+
- record.hypervolume()
|
|
836
|
+
)
|
|
837
|
+
)
|
|
838
|
+
forecasts = tuple(
|
|
839
|
+
(value.action_sha256, value.forecast)
|
|
840
|
+
for value in (
|
|
841
|
+
record.candidate(item) for item in universe_ids
|
|
842
|
+
)
|
|
843
|
+
if value.forecast is not None
|
|
844
|
+
)
|
|
845
|
+
anchors = tuple(
|
|
846
|
+
(value.action_sha256, value.anchor_point)
|
|
847
|
+
for value in (
|
|
848
|
+
record.candidate(item) for item in universe_ids
|
|
849
|
+
)
|
|
850
|
+
if value.anchor_point is not None
|
|
851
|
+
)
|
|
852
|
+
# The archive-geometry channel is defined against the CURRENT
|
|
853
|
+
# archive, so a revision that declares it is handed the sealed
|
|
854
|
+
# archive plus every point already revealed at this cutoff. Every
|
|
855
|
+
# earlier revision keeps the sealed archive exactly, so its replay
|
|
856
|
+
# bytes are unchanged.
|
|
857
|
+
archive_points = record.archive_points
|
|
858
|
+
if getattr(self._policy, "revision_3", False):
|
|
859
|
+
revealed_points = tuple(
|
|
860
|
+
point
|
|
861
|
+
for point in (
|
|
862
|
+
record.candidate(value.action_sha256).objectives
|
|
863
|
+
for value in revealed
|
|
864
|
+
)
|
|
865
|
+
if point is not None
|
|
866
|
+
)
|
|
867
|
+
archive_points = (*archive_points, *revealed_points)
|
|
868
|
+
decision = self._policy.select_next(
|
|
869
|
+
residual_request_sha256=request_sha256,
|
|
870
|
+
actions=descriptors,
|
|
871
|
+
evaluation_slots=budget,
|
|
872
|
+
diagnostic_action_sha256s=pilot_ids,
|
|
873
|
+
diagnostic_joint_gain=diagnostic_joint_gain,
|
|
874
|
+
selected_action_sha256s=selected_ids,
|
|
875
|
+
outcomes=outcomes,
|
|
876
|
+
archive_points=archive_points,
|
|
877
|
+
forecasts=forecasts,
|
|
878
|
+
anchor_points=anchors,
|
|
879
|
+
frozen_fit_training_run_count=(
|
|
880
|
+
self._frozen_fit_training_run_count
|
|
881
|
+
),
|
|
882
|
+
prior_conversion_outcomes=(
|
|
883
|
+
self._prior_conversion_outcomes
|
|
884
|
+
),
|
|
885
|
+
)
|
|
886
|
+
return ReplaySelection(
|
|
887
|
+
action_sha256=decision.selected_action_sha256s[0],
|
|
888
|
+
selection_propensity=decision.selection_propensity,
|
|
889
|
+
evidence={"phase": decision.phase},
|
|
890
|
+
)
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
@dataclass(frozen=True, slots=True)
|
|
894
|
+
class SequentialMarketReplay:
|
|
895
|
+
"""Run one policy over one sealed market under the honest boundary."""
|
|
896
|
+
|
|
897
|
+
oracle_subset_limit: int = DEFAULT_ORACLE_SUBSET_LIMIT
|
|
898
|
+
|
|
899
|
+
def __post_init__(self) -> None:
|
|
900
|
+
if (
|
|
901
|
+
type(self.oracle_subset_limit) is not int
|
|
902
|
+
or self.oracle_subset_limit <= 0
|
|
903
|
+
):
|
|
904
|
+
raise ValueError("oracle_subset_limit must be positive")
|
|
905
|
+
|
|
906
|
+
def oracle(
|
|
907
|
+
self,
|
|
908
|
+
record: MarketRecord,
|
|
909
|
+
budget: int,
|
|
910
|
+
) -> tuple[float, tuple[str, ...]]:
|
|
911
|
+
"""Exact best size-<=budget subset over evaluated candidates.
|
|
912
|
+
|
|
913
|
+
Enumeration runs over the deduplicated non-dominated positive
|
|
914
|
+
subset, which is exact: a candidate with zero marginal
|
|
915
|
+
hypervolume adds nothing to any union, and any member of an
|
|
916
|
+
optimal subset can be replaced by a candidate that weakly
|
|
917
|
+
dominates it without reducing the union. The subset limit
|
|
918
|
+
therefore applies to the reduced frontier, not the raw market.
|
|
919
|
+
"""
|
|
920
|
+
|
|
921
|
+
record.__post_init__()
|
|
922
|
+
if type(budget) is not int or budget <= 0:
|
|
923
|
+
raise ValueError("budget must be positive")
|
|
924
|
+
metric_ids = record.metric_ids
|
|
925
|
+
base = record.hypervolume()
|
|
926
|
+
unique_by_point: dict[
|
|
927
|
+
tuple[float, ...],
|
|
928
|
+
MarketCandidateRecord,
|
|
929
|
+
] = {}
|
|
930
|
+
for value in record.candidates:
|
|
931
|
+
if not value.feasible or value.objectives is None:
|
|
932
|
+
continue
|
|
933
|
+
if (
|
|
934
|
+
_clamped_gain(
|
|
935
|
+
record.hypervolume((value.objectives,)) - base
|
|
936
|
+
)
|
|
937
|
+
<= 0.0
|
|
938
|
+
):
|
|
939
|
+
continue
|
|
940
|
+
point = _point_values(value.objectives, metric_ids)
|
|
941
|
+
incumbent = unique_by_point.get(point)
|
|
942
|
+
if (
|
|
943
|
+
incumbent is None
|
|
944
|
+
or value.action_sha256 < incumbent.action_sha256
|
|
945
|
+
):
|
|
946
|
+
unique_by_point[point] = value
|
|
947
|
+
frontier = tuple(
|
|
948
|
+
candidate
|
|
949
|
+
for point, candidate in unique_by_point.items()
|
|
950
|
+
if not any(
|
|
951
|
+
other != point
|
|
952
|
+
and all(
|
|
953
|
+
o <= p
|
|
954
|
+
for o, p in zip(other, point, strict=True)
|
|
955
|
+
)
|
|
956
|
+
for other in unique_by_point
|
|
957
|
+
)
|
|
958
|
+
)
|
|
959
|
+
subset_size = min(budget, len(frontier))
|
|
960
|
+
if subset_size == 0:
|
|
961
|
+
return 0.0, ()
|
|
962
|
+
if (
|
|
963
|
+
math.comb(len(frontier), subset_size)
|
|
964
|
+
> self.oracle_subset_limit
|
|
965
|
+
):
|
|
966
|
+
raise ValueError(
|
|
967
|
+
"oracle enumeration exceeds the configured subset limit"
|
|
968
|
+
)
|
|
969
|
+
best_gain = 0.0
|
|
970
|
+
best_subset: tuple[str, ...] = ()
|
|
971
|
+
for subset in itertools.combinations(frontier, subset_size):
|
|
972
|
+
gain = (
|
|
973
|
+
record.hypervolume(
|
|
974
|
+
tuple(value.objectives for value in subset)
|
|
975
|
+
)
|
|
976
|
+
- base
|
|
977
|
+
)
|
|
978
|
+
key = tuple(
|
|
979
|
+
sorted(value.action_sha256 for value in subset)
|
|
980
|
+
)
|
|
981
|
+
if gain > best_gain or (
|
|
982
|
+
gain == best_gain and best_subset and key < best_subset
|
|
983
|
+
):
|
|
984
|
+
best_gain = gain
|
|
985
|
+
best_subset = key
|
|
986
|
+
best_gain = _clamped_gain(float(best_gain))
|
|
987
|
+
if best_gain == 0.0:
|
|
988
|
+
return 0.0, ()
|
|
989
|
+
return best_gain, best_subset
|
|
990
|
+
|
|
991
|
+
def run(
|
|
992
|
+
self,
|
|
993
|
+
*,
|
|
994
|
+
record: MarketRecord,
|
|
995
|
+
policy: object,
|
|
996
|
+
budget: int,
|
|
997
|
+
restrict_to_evaluated: bool = True,
|
|
998
|
+
) -> ReplayResult:
|
|
999
|
+
record.__post_init__()
|
|
1000
|
+
policy_id = getattr(policy, "policy_id", None)
|
|
1001
|
+
_require_token(str(policy_id), name="policy_id")
|
|
1002
|
+
if type(budget) is not int or budget <= 0:
|
|
1003
|
+
raise ValueError("budget must be positive")
|
|
1004
|
+
selectable = {
|
|
1005
|
+
value.action_sha256
|
|
1006
|
+
for value in record.candidates
|
|
1007
|
+
if not restrict_to_evaluated or value.evaluated
|
|
1008
|
+
}
|
|
1009
|
+
if budget > len(selectable):
|
|
1010
|
+
raise ValueError("budget exceeds the selectable market")
|
|
1011
|
+
receipts: list[ReplayStepReceipt] = []
|
|
1012
|
+
revealed_points: list[ObjectivePoint] = []
|
|
1013
|
+
cumulative = 0.0
|
|
1014
|
+
base_hv = record.hypervolume()
|
|
1015
|
+
for step_index in range(budget):
|
|
1016
|
+
selection = policy.select(
|
|
1017
|
+
record=record,
|
|
1018
|
+
revealed=tuple(receipts),
|
|
1019
|
+
selectable_action_sha256s=tuple(sorted(selectable)),
|
|
1020
|
+
step_index=step_index,
|
|
1021
|
+
budget=budget,
|
|
1022
|
+
)
|
|
1023
|
+
if type(selection) is not ReplaySelection:
|
|
1024
|
+
raise TypeError("policy must return an exact selection")
|
|
1025
|
+
selection.__post_init__()
|
|
1026
|
+
if selection.action_sha256 not in selectable:
|
|
1027
|
+
raise ValueError(
|
|
1028
|
+
"policy selected outside the selectable support"
|
|
1029
|
+
)
|
|
1030
|
+
candidate = record.candidate(selection.action_sha256)
|
|
1031
|
+
imputed = not candidate.evaluated
|
|
1032
|
+
if candidate.feasible and candidate.objectives is not None:
|
|
1033
|
+
with_candidate = record.hypervolume(
|
|
1034
|
+
(*revealed_points, candidate.objectives)
|
|
1035
|
+
)
|
|
1036
|
+
without_candidate = record.hypervolume(
|
|
1037
|
+
tuple(revealed_points)
|
|
1038
|
+
)
|
|
1039
|
+
# Union hypervolume is monotone; sub-epsilon float
|
|
1040
|
+
# differences from a zero-add candidate are exact zero.
|
|
1041
|
+
marginal = _clamped_gain(
|
|
1042
|
+
float(with_candidate - without_candidate)
|
|
1043
|
+
)
|
|
1044
|
+
revealed_points.append(candidate.objectives)
|
|
1045
|
+
else:
|
|
1046
|
+
marginal = 0.0
|
|
1047
|
+
cumulative = _clamped_gain(
|
|
1048
|
+
float(
|
|
1049
|
+
record.hypervolume(tuple(revealed_points))
|
|
1050
|
+
- base_hv
|
|
1051
|
+
)
|
|
1052
|
+
)
|
|
1053
|
+
selectable.discard(selection.action_sha256)
|
|
1054
|
+
receipts.append(
|
|
1055
|
+
ReplayStepReceipt(
|
|
1056
|
+
step_index=step_index,
|
|
1057
|
+
action_sha256=selection.action_sha256,
|
|
1058
|
+
selection_propensity=(
|
|
1059
|
+
selection.selection_propensity
|
|
1060
|
+
),
|
|
1061
|
+
evaluated=candidate.evaluated,
|
|
1062
|
+
feasible=candidate.feasible,
|
|
1063
|
+
imputed_zero_outcome=imputed,
|
|
1064
|
+
marginal_gain=marginal,
|
|
1065
|
+
cumulative_gain=cumulative,
|
|
1066
|
+
)
|
|
1067
|
+
)
|
|
1068
|
+
oracle_gain, oracle_subset = self.oracle(record, budget)
|
|
1069
|
+
return ReplayResult(
|
|
1070
|
+
market_sha256=record.market_sha256,
|
|
1071
|
+
policy_id=str(policy_id),
|
|
1072
|
+
budget=budget,
|
|
1073
|
+
restricted_to_evaluated=restrict_to_evaluated,
|
|
1074
|
+
receipts=tuple(receipts),
|
|
1075
|
+
realized_gain=float(cumulative),
|
|
1076
|
+
oracle_gain=float(oracle_gain),
|
|
1077
|
+
oracle_subset=oracle_subset,
|
|
1078
|
+
regret=float(oracle_gain - cumulative),
|
|
1079
|
+
)
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
def _normalized_point(
|
|
1083
|
+
raw: dict[str, float],
|
|
1084
|
+
axes: tuple[dict[str, object], ...],
|
|
1085
|
+
) -> ObjectivePoint:
|
|
1086
|
+
values: list[tuple[str, float]] = []
|
|
1087
|
+
for axis in axes:
|
|
1088
|
+
metric_id = str(axis["metric_id"])
|
|
1089
|
+
reference = float(axis["reference"])
|
|
1090
|
+
ideal = float(axis["ideal"])
|
|
1091
|
+
if reference == ideal:
|
|
1092
|
+
raise ValueError("axis reference must differ from ideal")
|
|
1093
|
+
values.append(
|
|
1094
|
+
(
|
|
1095
|
+
metric_id,
|
|
1096
|
+
float(
|
|
1097
|
+
(float(raw[metric_id]) - ideal)
|
|
1098
|
+
/ (reference - ideal)
|
|
1099
|
+
),
|
|
1100
|
+
)
|
|
1101
|
+
)
|
|
1102
|
+
return tuple(sorted(values))
|
|
1103
|
+
|
|
1104
|
+
|
|
1105
|
+
#: Frozen-score keys consumed from corpus records, in priority order.
|
|
1106
|
+
#: Values are min-max normalized per market (higher is better) so they
|
|
1107
|
+
#: are usable both as an ordering and as a probability-typed prior.
|
|
1108
|
+
CORPUS_FROZEN_SCORE_KEYS = ("frozen_prior_score", "raw")
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
def _corpus_engine_id(raw: dict[str, object]) -> str:
|
|
1112
|
+
lane = raw.get("lane")
|
|
1113
|
+
if isinstance(lane, dict) and lane.get("lane_id"):
|
|
1114
|
+
return str(lane["lane_id"])
|
|
1115
|
+
for key in ("expert_id", "family"):
|
|
1116
|
+
if raw.get(key):
|
|
1117
|
+
return str(raw[key])
|
|
1118
|
+
return "engine.unattributed"
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
def _corpus_action_sha256(
|
|
1122
|
+
market_id: str,
|
|
1123
|
+
raw: dict[str, object],
|
|
1124
|
+
) -> str:
|
|
1125
|
+
value = raw.get("action_sha256")
|
|
1126
|
+
if isinstance(value, str) and len(value) == 64:
|
|
1127
|
+
return value
|
|
1128
|
+
identity = [
|
|
1129
|
+
raw.get(key)
|
|
1130
|
+
for key in (
|
|
1131
|
+
"candidate_id",
|
|
1132
|
+
"proposal_id",
|
|
1133
|
+
"locus_key",
|
|
1134
|
+
"option_id",
|
|
1135
|
+
"native_rank",
|
|
1136
|
+
)
|
|
1137
|
+
if raw.get(key) is not None
|
|
1138
|
+
]
|
|
1139
|
+
if not identity:
|
|
1140
|
+
raise ValueError("corpus candidate carries no identity")
|
|
1141
|
+
return hashlib.sha256(
|
|
1142
|
+
_canonical_json(
|
|
1143
|
+
[
|
|
1144
|
+
"replay-candidate-id",
|
|
1145
|
+
market_id,
|
|
1146
|
+
[str(part) for part in identity],
|
|
1147
|
+
]
|
|
1148
|
+
)
|
|
1149
|
+
).hexdigest()
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
def _corpus_frozen_raw(raw: dict[str, object]) -> float | None:
|
|
1153
|
+
frozen_scores = raw.get("frozen_scores")
|
|
1154
|
+
if not isinstance(frozen_scores, dict):
|
|
1155
|
+
return None
|
|
1156
|
+
for key in CORPUS_FROZEN_SCORE_KEYS:
|
|
1157
|
+
value = frozen_scores.get(key)
|
|
1158
|
+
if isinstance(value, (int, float)) and math.isfinite(
|
|
1159
|
+
float(value)
|
|
1160
|
+
):
|
|
1161
|
+
return float(value)
|
|
1162
|
+
return None
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
def _corpus_forecast(
|
|
1166
|
+
raw: dict[str, object],
|
|
1167
|
+
axes: tuple[dict[str, object], ...],
|
|
1168
|
+
metric_ids: tuple[str, ...],
|
|
1169
|
+
) -> PositiveGainForecast | None:
|
|
1170
|
+
"""Absolute p10/p50/p90 points from parent objectives plus deltas.
|
|
1171
|
+
|
|
1172
|
+
Corpus forecasts are per-metric DELTAS relative to the candidate's
|
|
1173
|
+
parent, so they are usable only when parent objectives cover every
|
|
1174
|
+
metric. Direction-only forecasts degrade to no forecast.
|
|
1175
|
+
"""
|
|
1176
|
+
|
|
1177
|
+
forecast_raw = raw.get("forecast")
|
|
1178
|
+
parent = raw.get("parent")
|
|
1179
|
+
if not isinstance(forecast_raw, list) or not isinstance(
|
|
1180
|
+
parent,
|
|
1181
|
+
dict,
|
|
1182
|
+
):
|
|
1183
|
+
return None
|
|
1184
|
+
parent_objectives = parent.get("objectives")
|
|
1185
|
+
if not isinstance(parent_objectives, dict) or not set(
|
|
1186
|
+
metric_ids
|
|
1187
|
+
) <= set(parent_objectives):
|
|
1188
|
+
return None
|
|
1189
|
+
deltas: dict[str, dict[str, float]] = {}
|
|
1190
|
+
confidences: list[float] = []
|
|
1191
|
+
for row in forecast_raw:
|
|
1192
|
+
if not isinstance(row, dict) or "metric_id" not in row:
|
|
1193
|
+
return None
|
|
1194
|
+
metric_id = str(row["metric_id"])
|
|
1195
|
+
try:
|
|
1196
|
+
deltas[metric_id] = {
|
|
1197
|
+
quantile: float(row[f"{quantile}_delta"])
|
|
1198
|
+
for quantile in ("p10", "p50", "p90")
|
|
1199
|
+
}
|
|
1200
|
+
except (KeyError, TypeError, ValueError):
|
|
1201
|
+
return None
|
|
1202
|
+
confidence = row.get("confidence")
|
|
1203
|
+
if isinstance(confidence, (int, float)) and math.isfinite(
|
|
1204
|
+
float(confidence)
|
|
1205
|
+
):
|
|
1206
|
+
confidences.append(min(max(float(confidence), 0.0), 1.0))
|
|
1207
|
+
if set(deltas) != set(metric_ids):
|
|
1208
|
+
return None
|
|
1209
|
+
quantile_points = tuple(
|
|
1210
|
+
(
|
|
1211
|
+
quantile,
|
|
1212
|
+
_normalized_point(
|
|
1213
|
+
{
|
|
1214
|
+
metric_id: float(parent_objectives[metric_id])
|
|
1215
|
+
+ deltas[metric_id][quantile]
|
|
1216
|
+
for metric_id in metric_ids
|
|
1217
|
+
},
|
|
1218
|
+
axes,
|
|
1219
|
+
),
|
|
1220
|
+
)
|
|
1221
|
+
for quantile in ("p10", "p50", "p90")
|
|
1222
|
+
)
|
|
1223
|
+
reliability = (
|
|
1224
|
+
sum(confidences) / len(confidences) if confidences else 1.0
|
|
1225
|
+
)
|
|
1226
|
+
return PositiveGainForecast(
|
|
1227
|
+
quantile_points=quantile_points,
|
|
1228
|
+
reliability=float(reliability),
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
def _corpus_anchor(
|
|
1233
|
+
raw: dict[str, object],
|
|
1234
|
+
axes: tuple[dict[str, object], ...],
|
|
1235
|
+
metric_ids: tuple[str, ...],
|
|
1236
|
+
) -> ObjectivePoint | None:
|
|
1237
|
+
"""The candidate's parent objective point, when the corpus records it."""
|
|
1238
|
+
|
|
1239
|
+
parent = raw.get("parent")
|
|
1240
|
+
if not isinstance(parent, dict):
|
|
1241
|
+
return None
|
|
1242
|
+
objectives = parent.get("objectives")
|
|
1243
|
+
if not isinstance(objectives, dict) or not set(metric_ids) <= set(
|
|
1244
|
+
objectives
|
|
1245
|
+
):
|
|
1246
|
+
return None
|
|
1247
|
+
return _normalized_point(
|
|
1248
|
+
{
|
|
1249
|
+
metric_id: float(objectives[metric_id])
|
|
1250
|
+
for metric_id in metric_ids
|
|
1251
|
+
},
|
|
1252
|
+
axes,
|
|
1253
|
+
)
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
def market_record_from_corpus(payload: dict[str, object]) -> MarketRecord:
|
|
1257
|
+
"""Build a MarketRecord from one jul27 replay-corpus JSON payload.
|
|
1258
|
+
|
|
1259
|
+
Objective values are normalized onto each axis's ideal-to-reference
|
|
1260
|
+
interval, so the unit point is the hypervolume reference and the
|
|
1261
|
+
stored exact hypervolumes match the corpus's normalized values.
|
|
1262
|
+
Engine identity falls back lane_id -> expert_id -> family; frozen
|
|
1263
|
+
scores are min-max normalized per market; forecasts are rebuilt as
|
|
1264
|
+
absolute quantile points from parent objectives plus recorded
|
|
1265
|
+
deltas, degrading to no forecast when either half is missing.
|
|
1266
|
+
"""
|
|
1267
|
+
|
|
1268
|
+
market_id = str(payload["market_id"])
|
|
1269
|
+
axes = tuple(payload["hv_reference_point"]["axes"])
|
|
1270
|
+
metric_ids = tuple(
|
|
1271
|
+
sorted(str(axis["metric_id"]) for axis in axes)
|
|
1272
|
+
)
|
|
1273
|
+
reference = tuple(
|
|
1274
|
+
sorted((metric_id, 1.0) for metric_id in metric_ids)
|
|
1275
|
+
)
|
|
1276
|
+
archive_points = tuple(
|
|
1277
|
+
_normalized_point(point, axes)
|
|
1278
|
+
for point in payload["archive_at_market"]["points"]
|
|
1279
|
+
)
|
|
1280
|
+
raw_candidates = tuple(payload["candidates"])
|
|
1281
|
+
frozen_values = [
|
|
1282
|
+
value
|
|
1283
|
+
for value in (
|
|
1284
|
+
_corpus_frozen_raw(raw) for raw in raw_candidates
|
|
1285
|
+
)
|
|
1286
|
+
if value is not None
|
|
1287
|
+
]
|
|
1288
|
+
frozen_low = min(frozen_values) if frozen_values else 0.0
|
|
1289
|
+
frozen_high = max(frozen_values) if frozen_values else 0.0
|
|
1290
|
+
candidates: list[MarketCandidateRecord] = []
|
|
1291
|
+
for raw in raw_candidates:
|
|
1292
|
+
objectives_raw = raw.get("objectives")
|
|
1293
|
+
evaluated = bool(raw.get("evaluated", False))
|
|
1294
|
+
feasible = (
|
|
1295
|
+
evaluated
|
|
1296
|
+
and raw.get("valid") is not False
|
|
1297
|
+
and objectives_raw is not None
|
|
1298
|
+
)
|
|
1299
|
+
frozen_raw = _corpus_frozen_raw(raw)
|
|
1300
|
+
if frozen_raw is None:
|
|
1301
|
+
frozen_score = None
|
|
1302
|
+
elif frozen_high == frozen_low:
|
|
1303
|
+
frozen_score = NEUTRAL_PRIOR_SCORE
|
|
1304
|
+
else:
|
|
1305
|
+
frozen_score = float(
|
|
1306
|
+
(frozen_raw - frozen_low)
|
|
1307
|
+
/ (frozen_high - frozen_low)
|
|
1308
|
+
)
|
|
1309
|
+
candidates.append(
|
|
1310
|
+
(
|
|
1311
|
+
_corpus_engine_id(raw),
|
|
1312
|
+
int(raw["native_rank"]),
|
|
1313
|
+
MarketCandidateRecord(
|
|
1314
|
+
action_sha256=_corpus_action_sha256(
|
|
1315
|
+
market_id,
|
|
1316
|
+
raw,
|
|
1317
|
+
),
|
|
1318
|
+
engine_id=_corpus_engine_id(raw),
|
|
1319
|
+
native_rank=1,
|
|
1320
|
+
frozen_score=frozen_score,
|
|
1321
|
+
forecast=_corpus_forecast(
|
|
1322
|
+
raw,
|
|
1323
|
+
axes,
|
|
1324
|
+
metric_ids,
|
|
1325
|
+
),
|
|
1326
|
+
evaluated=evaluated,
|
|
1327
|
+
feasible=feasible,
|
|
1328
|
+
objectives=(
|
|
1329
|
+
None
|
|
1330
|
+
if not feasible
|
|
1331
|
+
else _normalized_point(objectives_raw, axes)
|
|
1332
|
+
),
|
|
1333
|
+
anchor_point=_corpus_anchor(raw, axes, metric_ids),
|
|
1334
|
+
),
|
|
1335
|
+
)
|
|
1336
|
+
)
|
|
1337
|
+
# A failed materialization can leave native-rank gaps inside an
|
|
1338
|
+
# engine, so ranks are densified per engine preserving order.
|
|
1339
|
+
by_engine: dict[str, list[tuple[int, MarketCandidateRecord]]] = {}
|
|
1340
|
+
for engine_id, native_rank, candidate in candidates:
|
|
1341
|
+
by_engine.setdefault(engine_id, []).append(
|
|
1342
|
+
(native_rank, candidate)
|
|
1343
|
+
)
|
|
1344
|
+
dense: list[MarketCandidateRecord] = []
|
|
1345
|
+
for engine_id in sorted(by_engine):
|
|
1346
|
+
lane = sorted(
|
|
1347
|
+
by_engine[engine_id],
|
|
1348
|
+
key=lambda item: (item[0], item[1].action_sha256),
|
|
1349
|
+
)
|
|
1350
|
+
for rank, (_original_rank, candidate) in enumerate(
|
|
1351
|
+
lane,
|
|
1352
|
+
start=1,
|
|
1353
|
+
):
|
|
1354
|
+
dense.append(
|
|
1355
|
+
MarketCandidateRecord(
|
|
1356
|
+
action_sha256=candidate.action_sha256,
|
|
1357
|
+
engine_id=candidate.engine_id,
|
|
1358
|
+
native_rank=rank,
|
|
1359
|
+
frozen_score=candidate.frozen_score,
|
|
1360
|
+
forecast=candidate.forecast,
|
|
1361
|
+
evaluated=candidate.evaluated,
|
|
1362
|
+
feasible=candidate.feasible,
|
|
1363
|
+
objectives=candidate.objectives,
|
|
1364
|
+
anchor_point=candidate.anchor_point,
|
|
1365
|
+
)
|
|
1366
|
+
)
|
|
1367
|
+
return MarketRecord(
|
|
1368
|
+
market_id=str(payload["market_id"]),
|
|
1369
|
+
archive_points=archive_points,
|
|
1370
|
+
hv_reference_point=reference,
|
|
1371
|
+
candidates=tuple(dense),
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
|
|
1375
|
+
__all__ = [
|
|
1376
|
+
"DEFAULT_ORACLE_SUBSET_LIMIT",
|
|
1377
|
+
"HYPERVOLUME_GAIN_EPSILON",
|
|
1378
|
+
"NEUTRAL_PRIOR_SCORE",
|
|
1379
|
+
"SEQUENTIAL_MARKET_REPLAY_ID",
|
|
1380
|
+
"SEQUENTIAL_MARKET_REPLAY_VERSION",
|
|
1381
|
+
"ExactHypervolumeGainPort",
|
|
1382
|
+
"FrozenScoreTopKReplayPolicy",
|
|
1383
|
+
"LaneHeadsReplayPolicy",
|
|
1384
|
+
"MarketCandidateRecord",
|
|
1385
|
+
"MarketRecord",
|
|
1386
|
+
"NativeRankRoundRobinReplayPolicy",
|
|
1387
|
+
"ReplayResult",
|
|
1388
|
+
"ReplaySelection",
|
|
1389
|
+
"ReplayStepReceipt",
|
|
1390
|
+
"SequentialMarketReplay",
|
|
1391
|
+
"UniformRandomReplayPolicy",
|
|
1392
|
+
"V8LiteReplayPolicy",
|
|
1393
|
+
"exact_hypervolume",
|
|
1394
|
+
"market_record_from_corpus",
|
|
1395
|
+
]
|