prodigy-ppl 0.1.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.
- prodigy/__init__.py +0 -0
- prodigy/analysis/__init__.py +36 -0
- prodigy/analysis/ai_candidates.py +227 -0
- prodigy/analysis/analyzer.py +161 -0
- prodigy/analysis/config.py +98 -0
- prodigy/analysis/equivalence/__init__.py +9 -0
- prodigy/analysis/equivalence/equivalence_check.py +151 -0
- prodigy/analysis/evtinvariants/__init__.py +0 -0
- prodigy/analysis/evtinvariants/heuristics/__init__.py +0 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/__init__.py +0 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/dominat_root_rejection.py +58 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/heuristics_factory.py +25 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/individual_sum_terms.py +31 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/iterate_terms.py +39 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/logical.py +51 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/polynomial_positivity.py +24 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/positivity.py +31 -0
- prodigy/analysis/evtinvariants/heuristics/positivity/rational_function.py +116 -0
- prodigy/analysis/evtinvariants/heuristics/strategies.py +115 -0
- prodigy/analysis/evtinvariants/heuristics/templates/__init__.py +0 -0
- prodigy/analysis/evtinvariants/heuristics/templates/polynomial_function.py +40 -0
- prodigy/analysis/evtinvariants/heuristics/templates/rational_function.py +36 -0
- prodigy/analysis/evtinvariants/heuristics/templates/templates.py +13 -0
- prodigy/analysis/evtinvariants/heuristics/templates/templates_factory.py +14 -0
- prodigy/analysis/evtinvariants/invariant_synthesis.py +100 -0
- prodigy/analysis/evtinvariants/invariant_verification.py +95 -0
- prodigy/analysis/exceptions.py +26 -0
- prodigy/analysis/independence/__init__.py +10 -0
- prodigy/analysis/independence/independence.py +170 -0
- prodigy/analysis/independence/utils.py +50 -0
- prodigy/analysis/instructionhandler/__init__.py +9 -0
- prodigy/analysis/instructionhandler/assignment_handler.py +50 -0
- prodigy/analysis/instructionhandler/function_handler.py +67 -0
- prodigy/analysis/instructionhandler/instruction_handler.py +29 -0
- prodigy/analysis/instructionhandler/ite_handler.py +58 -0
- prodigy/analysis/instructionhandler/loop_handler.py +36 -0
- prodigy/analysis/instructionhandler/observe_handler.py +39 -0
- prodigy/analysis/instructionhandler/probchoice_handler.py +38 -0
- prodigy/analysis/instructionhandler/program_info.py +28 -0
- prodigy/analysis/instructionhandler/query_handler.py +208 -0
- prodigy/analysis/instructionhandler/queryblock_handler.py +62 -0
- prodigy/analysis/instructionhandler/sample_handler.py +91 -0
- prodigy/analysis/instructionhandler/while_handler.py +793 -0
- prodigy/analysis/loop_strategies.py +326 -0
- prodigy/analysis/lowerbounds/__init__.py +0 -0
- prodigy/analysis/lowerbounds/egcalc.py +621 -0
- prodigy/analysis/lowerbounds/explicit.py +207 -0
- prodigy/analysis/lowerbounds/ir.py +152 -0
- prodigy/analysis/lowerbounds/parameters.py +539 -0
- prodigy/analysis/lowerbounds/problem.py +168 -0
- prodigy/analysis/lowerbounds/sandwich.py +418 -0
- prodigy/analysis/lowerbounds/subinvariant.py +207 -0
- prodigy/analysis/lowerbounds/synthesis.py +344 -0
- prodigy/analysis/optimization/__init__.py +9 -0
- prodigy/analysis/optimization/gf_optimizer.py +59 -0
- prodigy/analysis/optimization/optimizer.py +32 -0
- prodigy/analysis/result.py +628 -0
- prodigy/analysis/session.py +846 -0
- prodigy/analysis/solver/__init__.py +0 -0
- prodigy/analysis/solver/mock_solver.py +29 -0
- prodigy/analysis/solver/smtz3_solver.py +131 -0
- prodigy/analysis/solver/solver.py +13 -0
- prodigy/analysis/solver/solver_type.py +18 -0
- prodigy/analysis/solver/sympy_solver.py +65 -0
- prodigy/cli.py +587 -0
- prodigy/distribution/__init__.py +20 -0
- prodigy/distribution/distribution.py +777 -0
- prodigy/distribution/fast_generating_function.py +518 -0
- prodigy/distribution/generating_function.py +1522 -0
- prodigy/distribution/symengine_distribution.py +1142 -0
- prodigy/pgcl/LICENSE +202 -0
- prodigy/pgcl/NOTICE +9 -0
- prodigy/pgcl/__init__.py +49 -0
- prodigy/pgcl/ast/__init__.py +138 -0
- prodigy/pgcl/ast/ast.py +10 -0
- prodigy/pgcl/ast/declarations.py +148 -0
- prodigy/pgcl/ast/expressions.py +425 -0
- prodigy/pgcl/ast/instructions.py +218 -0
- prodigy/pgcl/ast/program.py +145 -0
- prodigy/pgcl/ast/types.py +68 -0
- prodigy/pgcl/ast/walk.py +170 -0
- prodigy/pgcl/check.py +729 -0
- prodigy/pgcl/compiler.py +107 -0
- prodigy/pgcl/parser.py +615 -0
- prodigy/pgcl/pgcl_checks.py +55 -0
- prodigy/pgcl/pgcl_operations.py +67 -0
- prodigy/pgcl/pysmt/__init__.py +12 -0
- prodigy/pgcl/pysmt/context.py +66 -0
- prodigy/pgcl/pysmt/expr.py +124 -0
- prodigy/pgcl/substitute.py +350 -0
- prodigy/pgcl/util/__init__.py +32 -0
- prodigy/pgcl/util/lark_expr_parser.py +134 -0
- prodigy/pgcl/util/ref.py +166 -0
- prodigy/pgfexamples/README.md +48 -0
- prodigy/pgfexamples/equivalence/loop_free/bernoulli.pgcl +4 -0
- prodigy/pgfexamples/equivalence/loop_free/bernoulli2.pgcl +4 -0
- prodigy/pgfexamples/equivalence/loop_free/dice_sampling.pgcl +9 -0
- prodigy/pgfexamples/equivalence/loop_free/dice_sampling2.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loop_free/uniform_sampling.pgcl +4 -0
- prodigy/pgfexamples/equivalence/loop_free/uniform_sampling2.pgcl +12 -0
- prodigy/pgfexamples/equivalence/loopy/17_die_even.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/bit_flip_conditioning.pgcl +17 -0
- prodigy/pgfexamples/equivalence/loopy/condand.pgcl +10 -0
- prodigy/pgfexamples/equivalence/loopy/dep_bern.pgcl +12 -0
- prodigy/pgfexamples/equivalence/loopy/endless_conditioning.pgcl +7 -0
- prodigy/pgfexamples/equivalence/loopy/geometric.pgcl +9 -0
- prodigy/pgfexamples/equivalence/loopy/geometric_observe.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/geometric_shifted.pgcl +10 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/17_die_even_invariant.pgcl +15 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/bit_flip_conditioning_invariant.pgcl +33 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/condand_invariant.pgcl +16 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/dep_bern_invariant.pgcl +15 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/endless_conditioning_invariant.pgcl +6 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/geometric_invariant.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/geometric_observe_invariant.pgcl +12 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/geometric_shifted_invariant.pgcl +12 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/ky_die_2_invariant.pgcl +20 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/ky_die_invariant.pgcl +38 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/n_geometric_invariant.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/nested_while_invariant1.pgcl +16 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/nested_while_invariant2.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/random_walk_invariant.pgcl +13 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/rejection_sampling_invariant.pgcl +7 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/running_paper_example_invariant.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/sequential_loops_invariant1.pgcl +12 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/sequential_loops_invariant2.pgcl +13 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/skip_rejection_sampling_invariant.pgcl +7 -0
- prodigy/pgfexamples/equivalence/loopy/invariants/trivial_iid_invariant.pgcl +11 -0
- prodigy/pgfexamples/equivalence/loopy/ky_die.pgcl +35 -0
- prodigy/pgfexamples/equivalence/loopy/ky_die_2.pgcl +19 -0
- prodigy/pgfexamples/equivalence/loopy/n_geometric.pgcl +9 -0
- prodigy/pgfexamples/equivalence/loopy/nested_while.pgcl +15 -0
- prodigy/pgfexamples/equivalence/loopy/random_walk.pgcl +10 -0
- prodigy/pgfexamples/equivalence/loopy/running_paper_example.pgcl +9 -0
- prodigy/pgfexamples/equivalence/loopy/sequential_loops.pgcl +17 -0
- prodigy/pgfexamples/equivalence/loopy/skip_rejection_sampling.pgcl +6 -0
- prodigy/pgfexamples/equivalence/loopy/trivial_iid.pgcl +12 -0
- prodigy/pgfexamples/independence/fuzzy_or.pgcl +47 -0
- prodigy/pgfexamples/independence/infinite_multiplication.pgcl +11 -0
- prodigy/pgfexamples/independence/many_variables.pgcl +72 -0
- prodigy/pgfexamples/independence/modgrade_fullyisolated.pgcl +26 -0
- prodigy/pgfexamples/independence/oblivious_transfer.pgcl +53 -0
- prodigy/pgfexamples/independence/private_information_retriveal.pgcl +68 -0
- prodigy/pgfexamples/independence/twocoins_fail.pgcl +10 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/burgler_alarm.pgcl +36 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/caesar.pgcl +25 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/conditioning_divergence.pgcl +10 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/digitRecognition.pgcl +15755 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/evidence1.pgcl +10 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/evidence2.pgcl +15 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/grass.pgcl +31 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/infer_geom_mix.pgcl +27 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/lin_regression_unbiased.pgcl +25 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/lucky_throw.pgcl +19 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/monty_hall_nested.pgcl +20 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/murder_mystery.pgcl +16 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/skip_lin_regression_biased.pgcl +25 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/telephone_operator.pgcl +19 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/twocoins.pgcl +17 -0
- prodigy/pgfexamples/inference/loop_free/conditioning/undefined_normalization.pgcl +4 -0
- prodigy/pgfexamples/inference/loop_free/dnd_handicap.pgcl +39 -0
- prodigy/pgfexamples/inference/loop_free/function.pgcl +7 -0
- prodigy/pgfexamples/inference/loop_free/fuzzy_or.pgcl +48 -0
- prodigy/pgfexamples/inference/loop_free/max.pgcl +24 -0
- prodigy/pgfexamples/inference/loop_free/monty_hall.pgcl +69 -0
- prodigy/pgfexamples/inference/loop_free/pi.pgcl +7 -0
- prodigy/pgfexamples/inference/loop_free/piranha.pgcl +22 -0
- prodigy/pgfexamples/inference/loop_free/poor_scale.pgcl +8 -0
- prodigy/pgfexamples/inference/loop_free/prob_collatz.pgcl +12 -0
- prodigy/pgfexamples/inference/loop_free/scalability.pgcl +9 -0
- prodigy/pgfexamples/inference/loopy/conditioning/dep_bern.pgcl +14 -0
- prodigy/pgfexamples/inference/loopy/conditioning/trivial_iid.pgcl +13 -0
- prodigy/pgfexamples/inference/loopy/skip_exceeding_sum.pgcl +13 -0
- prodigy/pgfexamples/inference/loopy/skip_geom_dist.pgcl +10 -0
- prodigy/pgfexamples/inference/loopy/skip_random_walk.pgcl +15 -0
- prodigy/pgfexamples/inference/loopy/uniform_sampling.pgcl +30 -0
- prodigy/pgfexamples/invariant_synthesis/cond_and.pgcl +7 -0
- prodigy/pgfexamples/invariant_synthesis/dueling_cowboys.pgcl +15 -0
- prodigy/pgfexamples/invariant_synthesis/fast_dice_roller.pgcl +35 -0
- prodigy/pgfexamples/invariant_synthesis/faulty_decrement.pgcl +7 -0
- prodigy/pgfexamples/invariant_synthesis/geometric.pgcl +7 -0
- prodigy/pgfexamples/invariant_synthesis/geometric_counter.pgcl +8 -0
- prodigy/pgfexamples/invariant_synthesis/modulo_geometric.pgcl +10 -0
- prodigy/pgfexamples/invariant_synthesis/nontermination.pgcl +8 -0
- prodigy/pgfexamples/invariant_synthesis/random_walk.pgcl +7 -0
- prodigy/pgfexamples/invariant_synthesis/random_walk_counter.pgcl +13 -0
- prodigy/pgfexamples/invariant_synthesis/skip_sequential_loops.pgcl +12 -0
- prodigy/pgfexamples/invariant_synthesis/subdist_enter.pgcl +7 -0
- prodigy/pgfexamples/invariant_synthesis/thirds_geometric.pgcl +22 -0
- prodigy/pgfexamples/lower_bounds/conditioning/README.md +6 -0
- prodigy/pgfexamples/lower_bounds/conditioning/noisy_geometric.pgcl +16 -0
- prodigy/pgfexamples/lower_bounds/conditioning/rw_counter_observe.pgcl +20 -0
- prodigy/pgfexamples/lower_bounds/exact/coupon_collector.pgcl +18 -0
- prodigy/pgfexamples/lower_bounds/exact/dueling_cowboys.pgcl +14 -0
- prodigy/pgfexamples/lower_bounds/exact/escape_counter.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/exact/fast_dice_roller.pgcl +35 -0
- prodigy/pgfexamples/lower_bounds/exact/faulty_decrement.pgcl +6 -0
- prodigy/pgfexamples/lower_bounds/exact/gamblers_ruin_counter.pgcl +11 -0
- prodigy/pgfexamples/lower_bounds/exact/geometric.pgcl +6 -0
- prodigy/pgfexamples/lower_bounds/exact/geometric_counter.pgcl +7 -0
- prodigy/pgfexamples/lower_bounds/exact/hark_counterexample.pgcl +14 -0
- prodigy/pgfexamples/lower_bounds/exact/lossy_retransmission.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/exact/modulo_geometric.pgcl +9 -0
- prodigy/pgfexamples/lower_bounds/exact/nontermination.pgcl +6 -0
- prodigy/pgfexamples/lower_bounds/exact/random_walk.pgcl +6 -0
- prodigy/pgfexamples/lower_bounds/exact/random_walk_counter.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/exact/subdist_enter.pgcl +6 -0
- prodigy/pgfexamples/lower_bounds/exact/thirds_geometric.pgcl +21 -0
- prodigy/pgfexamples/lower_bounds/sandwich/escape_counter.pgcl +13 -0
- prodigy/pgfexamples/lower_bounds/sandwich/escape_noisy.pgcl +13 -0
- prodigy/pgfexamples/lower_bounds/sandwich/escape_walk.pgcl +10 -0
- prodigy/pgfexamples/lower_bounds/sandwich/jump_counter.pgcl +13 -0
- prodigy/pgfexamples/lower_bounds/sandwich/quadrant_drift.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/sandwich/quadrant_timeout.pgcl +14 -0
- prodigy/pgfexamples/lower_bounds/sandwich/rw_counter_1120.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/sandwich/rw_counter_23.pgcl +15 -0
- prodigy/pgfexamples/lower_bounds/sandwich/rw_counter_35.pgcl +12 -0
- prodigy/pgfexamples/lower_bounds/sandwich/symmetric_noisy.pgcl +14 -0
- prodigy/pgfexamples/template_parameter_synthesis/bit_flip_conditioning_generalized_parameter.pgcl +18 -0
- prodigy/pgfexamples/template_parameter_synthesis/bit_flip_conditioning_parameter.pgcl +17 -0
- prodigy/pgfexamples/template_parameter_synthesis/brp_obs_parameter.pgcl +13 -0
- prodigy/pgfexamples/template_parameter_synthesis/dueling_cowboys_parameter.pgcl +13 -0
- prodigy/pgfexamples/template_parameter_synthesis/geometric_observe_parameter.pgcl +12 -0
- prodigy/pgfexamples/template_parameter_synthesis/geometric_parameter.pgcl +9 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/bit_flip_conditioning_generalized_parameter_invariant.pgcl +34 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/bit_flip_conditioning_parameter_invariant.pgcl +36 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/brp_obs_parameter_invariant.pgcl +47 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/dueling_cowboys_parameter_invariant.pgcl +19 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/geometric_observe_parameter_invariant.pgcl +12 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/geometric_parameter_invariant.pgcl +12 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/ky_die_parameter_invariant.pgcl +40 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/n_geometric_parameter_invariant.pgcl +12 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/negative_binomial_parameter_invariant.pgcl +13 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/negative_binomial_reversed_parameter_invariant.pgcl +13 -0
- prodigy/pgfexamples/template_parameter_synthesis/invariants/random_walk_parameter_invariant.pgcl +13 -0
- prodigy/pgfexamples/template_parameter_synthesis/ky_die_parameter.pgcl +35 -0
- prodigy/pgfexamples/template_parameter_synthesis/n_geometric_parameter.pgcl +10 -0
- prodigy/pgfexamples/template_parameter_synthesis/negative_binomial_parameter.pgcl +14 -0
- prodigy/pgfexamples/template_parameter_synthesis/negative_binomial_reversed_parameter.pgcl +14 -0
- prodigy/pgfexamples/template_parameter_synthesis/random_walk_parameter.pgcl +11 -0
- prodigy/pgfexamples/template_parameter_synthesis/telephone_operator_parameter.pgcl +19 -0
- prodigy/util/__init__.py +89 -0
- prodigy/util/color.py +19 -0
- prodigy/util/logger.py +39 -0
- prodigy/util/order.py +30 -0
- prodigy/util/plotter.py +172 -0
- prodigy/web/__init__.py +0 -0
- prodigy/web/app.py +199 -0
- prodigy/web/diagnostics.py +211 -0
- prodigy/web/examples.py +189 -0
- prodigy/web/frontend/README.md +15 -0
- prodigy/web/frontend/build.mjs +35 -0
- prodigy/web/frontend/codemirror-entry.js +64 -0
- prodigy/web/frontend/package-lock.json +657 -0
- prodigy/web/frontend/package.json +21 -0
- prodigy/web/jobs.py +316 -0
- prodigy/web/requests.py +109 -0
- prodigy/web/static/app/api.js +45 -0
- prodigy/web/static/app/app.css +485 -0
- prodigy/web/static/app/components/charts.js +133 -0
- prodigy/web/static/app/components/dom.js +71 -0
- prodigy/web/static/app/components/format.js +106 -0
- prodigy/web/static/app/components/math.js +20 -0
- prodigy/web/static/app/components/result-view.css +181 -0
- prodigy/web/static/app/components/result-view.js +495 -0
- prodigy/web/static/app/document.js +283 -0
- prodigy/web/static/app/editor.js +244 -0
- prodigy/web/static/app/library.js +100 -0
- prodigy/web/static/app/main.js +544 -0
- prodigy/web/static/app/setup.js +283 -0
- prodigy/web/static/app/store.js +20 -0
- prodigy/web/static/vendor/codemirror.js +13 -0
- prodigy/web/static/vendor/katex/LICENSE +21 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- prodigy/web/static/vendor/katex/katex.min.css +1 -0
- prodigy/web/static/vendor/katex/katex.min.js +1 -0
- prodigy/web/templates/app.html +124 -0
- prodigy_ppl-0.1.0.dist-info/METADATA +120 -0
- prodigy_ppl-0.1.0.dist-info/RECORD +301 -0
- prodigy_ppl-0.1.0.dist-info/WHEEL +4 -0
- prodigy_ppl-0.1.0.dist-info/entry_points.txt +2 -0
- prodigy_ppl-0.1.0.dist-info/licenses/LICENSE +201 -0
prodigy/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
================
|
|
3
|
+
Forward Analysis
|
|
4
|
+
================
|
|
5
|
+
|
|
6
|
+
The semantics transformation of the forwards analysis is mainly specified in the instruction handlers. They provide
|
|
7
|
+
the actual semantics on a generic level without knowing any specific encoding of a probability distribution.
|
|
8
|
+
|
|
9
|
+
The Semantics
|
|
10
|
+
#############
|
|
11
|
+
|
|
12
|
+
The semantics of a probabilistic program is defined as a distribution transformer. Each individual program instruction
|
|
13
|
+
is handled via a dedicated instruction handler. These handlers describe on a generic level, how the input distribution
|
|
14
|
+
is changed after executing the program instruction. This is oftentimes not computable, i.e., when the input distribution
|
|
15
|
+
has infinite support, or in case of while-loop instructions.
|
|
16
|
+
|
|
17
|
+
Distribution
|
|
18
|
+
############
|
|
19
|
+
|
|
20
|
+
Distributions are the core objects to transform by a probabilistic program. This class specifies a generic interface
|
|
21
|
+
with desired convenience methods needed, in order to abstract away from an actual distribution encoding.
|
|
22
|
+
In fact, we deliver two types of distribution backends:
|
|
23
|
+
|
|
24
|
+
#. A `sympy` implementation of generating functions
|
|
25
|
+
#. A `GiNaC` C++ implementation shipped as a `pybind11` module.
|
|
26
|
+
|
|
27
|
+
.. automodule:: prodigy.distribution
|
|
28
|
+
|
|
29
|
+
Analysis Configuration
|
|
30
|
+
######################
|
|
31
|
+
|
|
32
|
+
Sometimes, intermediate steps of the computation are desired. This and more configurations can be setup in a
|
|
33
|
+
`ForwardAnalysisConfig` object. Here also different backend Engines can be chosen.
|
|
34
|
+
|
|
35
|
+
.. automodule:: prodigy.analysis.config
|
|
36
|
+
"""
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Loop invariants proposed by a language model and checked by Prodigy.
|
|
3
|
+
|
|
4
|
+
The model only proposes candidates. Every candidate goes through the same exact check as an
|
|
5
|
+
invariant typed in by hand (strategies ``evt_invariant`` and ``evt_subinvariant``), so a wrong
|
|
6
|
+
answer makes an attempt fail but never makes a result unsound. When a candidate is rejected, the
|
|
7
|
+
next request tells the model why (e.g. Φ(I) − I), so it can correct itself.
|
|
8
|
+
|
|
9
|
+
Answers are untrusted input. Before any computer algebra system sees a candidate (SymPy's parser
|
|
10
|
+
evaluates Python code), it must consist of numbers, the program's variables and parameters,
|
|
11
|
+
``+ - * / ^``, parentheses, ``sqrt`` and ``exp``, and parse as a pGCL expression.
|
|
12
|
+
|
|
13
|
+
Configuration, read from the environment of ``prodigy`` (it never reaches the browser):
|
|
14
|
+
|
|
15
|
+
* ``ANTHROPIC_API_KEY``: Claude API, model ``PRODIGY_AI_MODEL`` (default ``claude-sonnet-5-5``);
|
|
16
|
+
``ANTHROPIC_WORKSPACE_ID`` for a key that is not scoped to a workspace.
|
|
17
|
+
* ``OPENAI_API_KEY`` and/or ``PRODIGY_AI_BASE_URL`` with ``PRODIGY_AI_MODEL``: an OpenAI-compatible
|
|
18
|
+
chat-completions API, e.g. ``http://localhost:11434/v1`` for a local model served by Ollama.
|
|
19
|
+
* ``PRODIGY_AI_PROVIDER`` (``anthropic`` or ``openai``) chooses if both are configured;
|
|
20
|
+
``PRODIGY_AI_TIMEOUT``: seconds per request (default 120).
|
|
21
|
+
"""
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import re
|
|
27
|
+
import urllib.error
|
|
28
|
+
import urllib.request
|
|
29
|
+
from dataclasses import dataclass, field
|
|
30
|
+
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple
|
|
31
|
+
|
|
32
|
+
from prodigy.pgcl.parser import parse_expr
|
|
33
|
+
|
|
34
|
+
DEFAULT_ANTHROPIC_MODEL = "claude-sonnet-5-5"
|
|
35
|
+
ANTHROPIC_URL = "https://api.anthropic.com/v1/messages"
|
|
36
|
+
ANTHROPIC_VERSION = "2026-10-21"
|
|
37
|
+
FUNCTIONS = frozenset({"sqrt", "exp"})
|
|
38
|
+
FORMS = ("evt_invariant", "evt_subinvariant")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AIConfigurationError(Exception):
|
|
42
|
+
"""No language model is configured, or its configuration is incomplete."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AIRequestError(Exception):
|
|
46
|
+
"""The request to the language model failed."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CandidateError(ValueError):
|
|
50
|
+
"""An answer contains no admissible candidate."""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class Provider:
|
|
55
|
+
name: str
|
|
56
|
+
model: str
|
|
57
|
+
ask: Callable[[str, str], str]
|
|
58
|
+
"""(system prompt, user prompt) -> text of the answer"""
|
|
59
|
+
diagnostics: Dict[str, Any] = field(default_factory=dict)
|
|
60
|
+
"""Why the last answer ended (``stop_reason``) and the kinds of its content blocks."""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _post(url: str, headers: Dict[str, str], body: Dict[str, Any], timeout: float) -> Dict[str, Any]:
|
|
64
|
+
request = urllib.request.Request(url, data=json.dumps(body).encode("utf-8"), method="POST",
|
|
65
|
+
headers={"content-type": "application/json", **headers})
|
|
66
|
+
try:
|
|
67
|
+
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
68
|
+
return json.loads(response.read().decode("utf-8"))
|
|
69
|
+
except urllib.error.HTTPError as exc:
|
|
70
|
+
detail = exc.read().decode("utf-8", errors="replace")[:500]
|
|
71
|
+
raise AIRequestError(f"{url} answered {exc.code}: {detail}") from exc
|
|
72
|
+
except (urllib.error.URLError, OSError) as exc:
|
|
73
|
+
raise AIRequestError(f"{url} could not be reached: {exc}") from exc
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_provider(env: Optional[Mapping[str, str]] = None) -> Provider:
|
|
77
|
+
"""The configured language model (see the module documentation)."""
|
|
78
|
+
env = os.environ if env is None else env
|
|
79
|
+
choice = env.get("PRODIGY_AI_PROVIDER", "").strip().lower()
|
|
80
|
+
if not choice:
|
|
81
|
+
if env.get("ANTHROPIC_API_KEY"):
|
|
82
|
+
choice = "anthropic"
|
|
83
|
+
elif env.get("OPENAI_API_KEY") or env.get("PRODIGY_AI_BASE_URL"):
|
|
84
|
+
choice = "openai"
|
|
85
|
+
timeout = float(env.get("PRODIGY_AI_TIMEOUT") or 120)
|
|
86
|
+
if choice == "anthropic":
|
|
87
|
+
return _anthropic(env, timeout)
|
|
88
|
+
if choice == "openai":
|
|
89
|
+
return _openai(env, timeout)
|
|
90
|
+
if choice:
|
|
91
|
+
raise AIConfigurationError(f"Unknown PRODIGY_AI_PROVIDER {choice!r} (anthropic or openai).")
|
|
92
|
+
raise AIConfigurationError(
|
|
93
|
+
"No language model is configured. Set ANTHROPIC_API_KEY (Claude API), or OPENAI_API_KEY or "
|
|
94
|
+
"PRODIGY_AI_BASE_URL together with PRODIGY_AI_MODEL (OpenAI-compatible API, e.g. a local model) "
|
|
95
|
+
"in the environment of prodigy, and start it again.")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _anthropic(env: Mapping[str, str], timeout: float) -> Provider:
|
|
99
|
+
key = env.get("ANTHROPIC_API_KEY")
|
|
100
|
+
if not key:
|
|
101
|
+
raise AIConfigurationError("PRODIGY_AI_PROVIDER=anthropic needs ANTHROPIC_API_KEY.")
|
|
102
|
+
model = env.get("PRODIGY_AI_MODEL") or DEFAULT_ANTHROPIC_MODEL
|
|
103
|
+
headers = {"authorization": f"Bearer {key}", "anthropic-version": ANTHROPIC_VERSION}
|
|
104
|
+
if env.get("ANTHROPIC_WORKSPACE_ID"):
|
|
105
|
+
headers["anthropic-workspace-id"] = env["ANTHROPIC_WORKSPACE_ID"]
|
|
106
|
+
|
|
107
|
+
diagnostics: Dict[str, Any] = {}
|
|
108
|
+
|
|
109
|
+
def ask(system: str, user: str) -> str:
|
|
110
|
+
# room for the model's reasoning; only the tokens used are billed
|
|
111
|
+
reply = _post(ANTHROPIC_URL, headers, {"model": model, "max_tokens": 16000, "system": system,
|
|
112
|
+
"messages": [{"role": "user", "content": user}]}, timeout)
|
|
113
|
+
blocks = reply.get("content") or []
|
|
114
|
+
diagnostics.clear()
|
|
115
|
+
diagnostics.update(stop_reason=reply.get("stop_reason"), content=[b.get("type") for b in blocks])
|
|
116
|
+
return "".join(block.get("text", "") for block in blocks if block.get("type") == "text")
|
|
117
|
+
|
|
118
|
+
return Provider("anthropic", model, ask, diagnostics)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _openai(env: Mapping[str, str], timeout: float) -> Provider:
|
|
122
|
+
model = env.get("PRODIGY_AI_MODEL")
|
|
123
|
+
if not model:
|
|
124
|
+
raise AIConfigurationError("An OpenAI-compatible API needs PRODIGY_AI_MODEL (the name of the model).")
|
|
125
|
+
url = (env.get("PRODIGY_AI_BASE_URL") or "https://api.openai.com/v1").rstrip("/") + "/chat/completions"
|
|
126
|
+
headers = {"authorization": f"Bearer {env['OPENAI_API_KEY']}"} if env.get("OPENAI_API_KEY") else {}
|
|
127
|
+
|
|
128
|
+
diagnostics: Dict[str, Any] = {}
|
|
129
|
+
|
|
130
|
+
def ask(system: str, user: str) -> str:
|
|
131
|
+
reply = _post(url, headers, {"model": model, "messages": [{"role": "system", "content": system},
|
|
132
|
+
{"role": "user", "content": user}]}, timeout)
|
|
133
|
+
try:
|
|
134
|
+
choice = reply["choices"][0]
|
|
135
|
+
diagnostics.clear()
|
|
136
|
+
diagnostics.update(stop_reason=choice.get("finish_reason"))
|
|
137
|
+
return choice["message"]["content"] or ""
|
|
138
|
+
except (KeyError, IndexError, TypeError, AttributeError) as exc:
|
|
139
|
+
raise AIRequestError(f"{url}: unexpected answer {str(reply)[:300]}") from exc
|
|
140
|
+
|
|
141
|
+
return Provider("openai", model, ask, diagnostics)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ---------------------------------------------------------------------------------------
|
|
145
|
+
# Prompts and answers
|
|
146
|
+
# ---------------------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
SYSTEM_PROMPT = (
|
|
149
|
+
"You help the verifier Prodigy analyse probabilistic programs with loops. Prodigy represents a "
|
|
150
|
+
"distribution over program states by its probability generating function (PGF) in the program "
|
|
151
|
+
"variables: the coefficient of x^i*c^j is the probability of the state x = i, c = j. You propose a "
|
|
152
|
+
"generating function; Prodigy checks it exactly and rejects it if it is wrong. Your candidate is a "
|
|
153
|
+
"single closed-form expression in the program variables and parameters, using numbers, + - * / ^, "
|
|
154
|
+
"parentheses, sqrt and exp. Keep any reasoning short and end your answer with the line\n"
|
|
155
|
+
"ANSWER: <expression>\n"
|
|
156
|
+
"If you cannot find a candidate, end with the line ANSWER: none")
|
|
157
|
+
|
|
158
|
+
_TASKS = {
|
|
159
|
+
"evt_invariant": (
|
|
160
|
+
"Find the generating function I of the expected visiting times at the head of the loop: the least "
|
|
161
|
+
"solution of I = G + B(<I>_guard), where G is the PGF entering the loop, <F>_guard keeps the terms "
|
|
162
|
+
"of F whose states satisfy the guard, and B is the PGF transformer of one execution of the loop "
|
|
163
|
+
"body. Then <I>_(not guard) is the output distribution of the loop. I must have non-negative "
|
|
164
|
+
"coefficients; Prodigy accepts it as exact only if I(1, ..., 1) is finite."),
|
|
165
|
+
"evt_subinvariant": (
|
|
166
|
+
"Find an occupation sub-invariant nu: a generating function with non-negative coefficients and "
|
|
167
|
+
"finite mass nu(1, ..., 1) such that nu <= G + B(<nu>_guard) coefficient-wise, where G is the PGF "
|
|
168
|
+
"entering the loop, <F>_guard keeps the terms of F whose states satisfy the guard, and B is the "
|
|
169
|
+
"PGF transformer of one execution of the loop body. Then <nu>_(not guard) is a lower bound on the "
|
|
170
|
+
"output distribution. The expected visiting times are the best choice if their mass is finite; "
|
|
171
|
+
"otherwise choose a smaller nu with finite mass."),
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
_EXAMPLE = ("Example: for `nat x; nat c; x := 1; while (x = 1) { {x := 0} [1/2] {c := c + 1} }` the loop "
|
|
175
|
+
"is entered with G = x, and its expected visiting times are (x + 1/2)/(1 - c/2).")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def prompt(form: str, program: str, loop: str, variables: Sequence[str], parameters: Sequence[str],
|
|
179
|
+
entering: str, rejected: Sequence[Tuple[str, str]]) -> str:
|
|
180
|
+
"""The request for a candidate of kind ``form`` (see :data:`FORMS`) for ``loop``."""
|
|
181
|
+
parts = [f"Program:\n{program}", f"The loop:\n{loop}",
|
|
182
|
+
f"Program variables: {', '.join(variables)}",
|
|
183
|
+
f"Parameters (unknown constants): {', '.join(parameters) if parameters else 'none'}",
|
|
184
|
+
f"G, the PGF entering the loop: {entering}", _TASKS[form], _EXAMPLE]
|
|
185
|
+
if rejected:
|
|
186
|
+
parts.append("Prodigy rejected these candidates; do not propose them again:\n"
|
|
187
|
+
+ "\n".join(f"- {candidate}: {reason}" for candidate, reason in rejected))
|
|
188
|
+
return "\n\n".join(parts)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
_FENCE = re.compile(r"```[A-Za-z]*\n?(.*?)```", re.S)
|
|
192
|
+
_ANSWER = re.compile(r"^[\s*_#>]*ANSWER[\s*_]*:[\s*_`$]*(.*?)[\s*_`$]*$", re.I | re.M)
|
|
193
|
+
_PREFIX = re.compile(r"^(?:[A-Za-z_]\w*\s*(?:\([^)]*\))?\s*=\s*)+")
|
|
194
|
+
_ALLOWED = re.compile(r"[0-9A-Za-z_+\-*/^().\s]+")
|
|
195
|
+
_IDENTIFIER = re.compile(r"[A-Za-z_]\w*")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def candidate_from_answer(answer: str, names: Sequence[str]) -> str:
|
|
199
|
+
"""The candidate in a model's answer: its last ``ANSWER:`` line, else the last line of its last
|
|
200
|
+
code block or of the answer.
|
|
201
|
+
|
|
202
|
+
Raises :class:`CandidateError` unless it is an expression over ``names``, numbers,
|
|
203
|
+
``+ - * / ^``, parentheses, ``sqrt`` and ``exp`` that parses as a pGCL expression."""
|
|
204
|
+
marked = _ANSWER.findall(answer)
|
|
205
|
+
if marked:
|
|
206
|
+
line = marked[-1].strip()
|
|
207
|
+
if line.lower().strip(".") in ("none", "unknown", ""):
|
|
208
|
+
raise CandidateError("The model found no candidate (ANSWER: none).")
|
|
209
|
+
else:
|
|
210
|
+
blocks = _FENCE.findall(answer)
|
|
211
|
+
lines = [line.strip() for line in (blocks[-1] if blocks else answer).splitlines() if line.strip()]
|
|
212
|
+
if not lines:
|
|
213
|
+
raise CandidateError("The answer contains no text.")
|
|
214
|
+
line = lines[-1]
|
|
215
|
+
candidate = _PREFIX.sub("", line.strip("`$ ")).rstrip(".;").strip().replace("**", "^")
|
|
216
|
+
if not _ALLOWED.fullmatch(candidate):
|
|
217
|
+
raise CandidateError(f"{candidate!r} contains characters other than numbers, variables, "
|
|
218
|
+
"+ - * / ^ and parentheses.")
|
|
219
|
+
unknown = sorted(set(_IDENTIFIER.findall(candidate)) - set(names) - FUNCTIONS)
|
|
220
|
+
if unknown:
|
|
221
|
+
raise CandidateError(f"{candidate!r} uses {', '.join(unknown)}, which are neither program "
|
|
222
|
+
"variables nor parameters.")
|
|
223
|
+
try:
|
|
224
|
+
parse_expr(candidate)
|
|
225
|
+
except Exception as exc: # pylint: disable=broad-except # lark raises several error types
|
|
226
|
+
raise CandidateError(f"{candidate!r} is not a well-formed expression: {exc}") from exc
|
|
227
|
+
return candidate
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Callable, Optional, Union, Sequence, get_args
|
|
6
|
+
|
|
7
|
+
from prodigy.pgcl import Program, Instr, SkipInstr, AbortInstr, WhileInstr, LoopInstr, QueryInstr, Query, \
|
|
8
|
+
ObserveInstr, ChoiceInstr, AsgnInstr, IfInstr
|
|
9
|
+
|
|
10
|
+
from prodigy.analysis.config import ForwardAnalysisConfig
|
|
11
|
+
from prodigy.analysis.exceptions import ObserveZeroEventError
|
|
12
|
+
from prodigy.analysis.instructionhandler.assignment_handler import AssignmentHandler
|
|
13
|
+
from prodigy.analysis.instructionhandler.ite_handler import ITEHandler
|
|
14
|
+
from prodigy.analysis.instructionhandler.loop_handler import LoopHandler
|
|
15
|
+
from prodigy.analysis.instructionhandler.observe_handler import ObserveHandler
|
|
16
|
+
from prodigy.analysis.instructionhandler.probchoice_handler import PChoiceHandler
|
|
17
|
+
from prodigy.analysis.instructionhandler.program_info import ProgramInfo
|
|
18
|
+
from prodigy.analysis.instructionhandler.query_handler import QueryHandler
|
|
19
|
+
from prodigy.analysis.instructionhandler.queryblock_handler import QueryBlockHandler
|
|
20
|
+
from prodigy.analysis.instructionhandler.while_handler import WhileHandler
|
|
21
|
+
from prodigy.analysis.session import current_session
|
|
22
|
+
from prodigy.distribution import Distribution
|
|
23
|
+
from prodigy.util.color import Style
|
|
24
|
+
from prodigy.util.logger import log_setup
|
|
25
|
+
|
|
26
|
+
logger = log_setup(str(__name__).rsplit(".", maxsplit=1)[-1], logging.DEBUG)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def condition_distribution(
|
|
30
|
+
dist: Distribution, error_prob: Distribution,
|
|
31
|
+
config: ForwardAnalysisConfig) -> tuple[Distribution, Distribution]:
|
|
32
|
+
# lets the session rescale the residual mass of approximated loops
|
|
33
|
+
current_session().on_normalize(error_prob)
|
|
34
|
+
one = config.factory.one(*error_prob.get_variables())
|
|
35
|
+
zero = one * "0"
|
|
36
|
+
if dist == zero and error_prob == one:
|
|
37
|
+
raise ObserveZeroEventError(
|
|
38
|
+
"Undefined semantics: Probability of observing a valid run is 0.")
|
|
39
|
+
result = dist / (one - error_prob)
|
|
40
|
+
return result, error_prob
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def compute_discrete_distribution(
|
|
44
|
+
program: Program | ProgramInfo, dist: Distribution,
|
|
45
|
+
config: ForwardAnalysisConfig,
|
|
46
|
+
step_callback: Optional[Callable[[Instr, Distribution, Distribution], None]] = None
|
|
47
|
+
) -> tuple[Distribution, Distribution]:
|
|
48
|
+
"""
|
|
49
|
+
Computes the output distribution of ``program`` for the input ``dist``.
|
|
50
|
+
|
|
51
|
+
:param step_callback: if given, called with (instruction, distribution, error_prob) after each
|
|
52
|
+
top-level instruction.
|
|
53
|
+
"""
|
|
54
|
+
logger.debug("Compute the distribution for\n%s\nand distribution %s", str(program), dist)
|
|
55
|
+
|
|
56
|
+
# Inject the context from the parsed program into the initial distribution.
|
|
57
|
+
prog_info = program if isinstance(program,
|
|
58
|
+
ProgramInfo) else ProgramInfo(program)
|
|
59
|
+
variables = set(prog_info.variables.keys()).union(dist.get_variables())
|
|
60
|
+
parameters = set(prog_info.parameters.keys()).union(dist.get_parameters())
|
|
61
|
+
initial_dist = dist.set_variables(*variables).set_parameters(*parameters)
|
|
62
|
+
error_prob = config.factory.one(*variables) * 0
|
|
63
|
+
if step_callback is None:
|
|
64
|
+
dist, error_prob = compute_semantics(prog_info.instructions,
|
|
65
|
+
prog_info, initial_dist,
|
|
66
|
+
error_prob, config)
|
|
67
|
+
else:
|
|
68
|
+
dist = initial_dist
|
|
69
|
+
for instr in prog_info.instructions:
|
|
70
|
+
dist, error_prob = compute_semantics([instr], prog_info, dist, error_prob, config)
|
|
71
|
+
step_callback(instr, dist, error_prob)
|
|
72
|
+
if config.normalize:
|
|
73
|
+
dist, error_prob = condition_distribution(dist, error_prob, config)
|
|
74
|
+
return dist, error_prob
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def compute_semantics(
|
|
78
|
+
instruction: Union[Instr, Sequence[Instr]],
|
|
79
|
+
prog_info: ProgramInfo,
|
|
80
|
+
distribution: Distribution,
|
|
81
|
+
error_prob: Distribution,
|
|
82
|
+
config=ForwardAnalysisConfig()
|
|
83
|
+
) -> tuple[Distribution, Distribution]:
|
|
84
|
+
def _show_steps(inp: tuple[Distribution, Distribution],
|
|
85
|
+
instr: Instr) -> tuple[Distribution, Distribution]:
|
|
86
|
+
dist, error_prob = inp
|
|
87
|
+
res, error_prob = compute_semantics(instr, prog_info, dist,
|
|
88
|
+
error_prob, config)
|
|
89
|
+
if isinstance(instr, (WhileInstr, IfInstr, LoopInstr)):
|
|
90
|
+
print("\n")
|
|
91
|
+
output = f"\n{Style.BLUE}Instruction:{Style.RESET} {instr}\t {Style.GREEN}Result:{Style.RESET} {res}"
|
|
92
|
+
print(output, prog_info.so_vars)
|
|
93
|
+
if config.step_wise:
|
|
94
|
+
current_session().provider.next_step()
|
|
95
|
+
return res, error_prob
|
|
96
|
+
|
|
97
|
+
def _dont_show_steps(
|
|
98
|
+
inp: tuple[Distribution, Distribution],
|
|
99
|
+
instr: Instr) -> tuple[Distribution, Distribution]:
|
|
100
|
+
dist, error_prob = inp
|
|
101
|
+
return compute_semantics(instr, prog_info, dist, error_prob,
|
|
102
|
+
config)
|
|
103
|
+
|
|
104
|
+
if not isinstance(instruction, list):
|
|
105
|
+
current_session().on_instruction(instruction)
|
|
106
|
+
|
|
107
|
+
if isinstance(instruction, list):
|
|
108
|
+
func = _show_steps if config.show_intermediate_steps else _dont_show_steps
|
|
109
|
+
return functools.reduce(func, instruction,
|
|
110
|
+
(distribution, error_prob))
|
|
111
|
+
|
|
112
|
+
elif isinstance(instruction, SkipInstr):
|
|
113
|
+
return distribution, error_prob
|
|
114
|
+
|
|
115
|
+
elif isinstance(instruction, AbortInstr):
|
|
116
|
+
return distribution.factory().undefined(
|
|
117
|
+
*distribution.get_variables()), error_prob
|
|
118
|
+
|
|
119
|
+
elif isinstance(instruction, WhileInstr):
|
|
120
|
+
logger.info("\n%s gets handled", instruction)
|
|
121
|
+
return WhileHandler.compute(instruction, prog_info, distribution,
|
|
122
|
+
error_prob, config, compute_semantics)
|
|
123
|
+
|
|
124
|
+
elif isinstance(instruction, IfInstr):
|
|
125
|
+
return ITEHandler.compute(instruction, prog_info, distribution,
|
|
126
|
+
error_prob, config, compute_semantics)
|
|
127
|
+
|
|
128
|
+
elif isinstance(instruction, AsgnInstr):
|
|
129
|
+
return AssignmentHandler.compute(instruction, prog_info,
|
|
130
|
+
distribution, error_prob, config, compute_semantics)
|
|
131
|
+
|
|
132
|
+
elif isinstance(instruction, ChoiceInstr):
|
|
133
|
+
return PChoiceHandler.compute(instruction, prog_info, distribution,
|
|
134
|
+
error_prob, config, compute_semantics)
|
|
135
|
+
|
|
136
|
+
elif isinstance(instruction, ObserveInstr):
|
|
137
|
+
logger.info("%s gets handled", instruction)
|
|
138
|
+
return ObserveHandler.compute(instruction, prog_info, distribution,
|
|
139
|
+
error_prob, config, compute_semantics)
|
|
140
|
+
|
|
141
|
+
elif isinstance(instruction, get_args(Query)):
|
|
142
|
+
logger.info("%s gets handled", instruction)
|
|
143
|
+
if config.normalize:
|
|
144
|
+
distribution, error_prob = condition_distribution(
|
|
145
|
+
distribution, error_prob,
|
|
146
|
+
config) # evaluate queries on conditioned distribution
|
|
147
|
+
error_prob *= "0"
|
|
148
|
+
return QueryHandler.compute(instruction, prog_info, distribution,
|
|
149
|
+
error_prob, config, compute_semantics)
|
|
150
|
+
|
|
151
|
+
elif isinstance(instruction, LoopInstr):
|
|
152
|
+
logger.info("%s gets handled", instruction)
|
|
153
|
+
return LoopHandler.compute(instruction, prog_info, distribution,
|
|
154
|
+
error_prob, config, compute_semantics)
|
|
155
|
+
|
|
156
|
+
elif isinstance(instruction, QueryInstr):
|
|
157
|
+
logger.info("entering query block")
|
|
158
|
+
return QueryBlockHandler.compute(instruction, prog_info,
|
|
159
|
+
distribution, error_prob, config, compute_semantics)
|
|
160
|
+
|
|
161
|
+
raise TypeError("illegal instruction")
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
-----------------------
|
|
3
|
+
Forward Analysis Config
|
|
4
|
+
-----------------------
|
|
5
|
+
"""
|
|
6
|
+
from enum import Enum, auto
|
|
7
|
+
from typing import Type
|
|
8
|
+
|
|
9
|
+
import attr
|
|
10
|
+
|
|
11
|
+
from prodigy.analysis.optimization import Optimizer
|
|
12
|
+
from prodigy.analysis.optimization.gf_optimizer import GFOptimizer
|
|
13
|
+
from prodigy.distribution.generating_function import (GeneratingFunction,
|
|
14
|
+
SympyPGF)
|
|
15
|
+
from prodigy.distribution.symengine_distribution import SymenginePGF
|
|
16
|
+
from .evtinvariants.heuristics.positivity.heuristics_factory import PositivityHeuristics
|
|
17
|
+
from .evtinvariants.heuristics.strategies import SynthesisStrategies
|
|
18
|
+
from .evtinvariants.heuristics.templates.templates_factory import TemplateHeuristics
|
|
19
|
+
from .exceptions import ConfigurationError
|
|
20
|
+
from .solver.solver_type import SolverType
|
|
21
|
+
from ..distribution.distribution import CommonDistributionsFactory
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@attr.s
|
|
25
|
+
class ForwardAnalysisConfig:
|
|
26
|
+
"""Global configurable options for forward analysis."""
|
|
27
|
+
|
|
28
|
+
class Engine(Enum):
|
|
29
|
+
"""
|
|
30
|
+
This enumeration specifies the type of backend used for distribution encodings and mathematical operations.
|
|
31
|
+
"""
|
|
32
|
+
SYMPY = auto()
|
|
33
|
+
GINAC = auto()
|
|
34
|
+
SYMENGINE = auto()
|
|
35
|
+
|
|
36
|
+
show_intermediate_steps: bool = attr.ib(default=False)
|
|
37
|
+
"""Enables the printing of results after each instruction."""
|
|
38
|
+
|
|
39
|
+
step_wise: bool = attr.ib(default=False)
|
|
40
|
+
"""Enables step-by-step computation"""
|
|
41
|
+
|
|
42
|
+
use_simplification: bool = attr.ib(default=False)
|
|
43
|
+
"""Enables simplification heuristics for expressions."""
|
|
44
|
+
|
|
45
|
+
use_latex: bool = attr.ib(default=False)
|
|
46
|
+
"""Toggle to print LaTeX-Code instead of ASCII expressions."""
|
|
47
|
+
|
|
48
|
+
engine: Engine = attr.ib(default=Engine.SYMPY)
|
|
49
|
+
"""Selects the distribution backend."""
|
|
50
|
+
|
|
51
|
+
normalize: bool = attr.ib(default=True)
|
|
52
|
+
"""Switch to compute the normalized distribution"""
|
|
53
|
+
|
|
54
|
+
strategy: SynthesisStrategies = attr.ib(default=SynthesisStrategies.DEFAULT)
|
|
55
|
+
"""The Strategy for dealing with synthesis."""
|
|
56
|
+
|
|
57
|
+
templ_heuristic: TemplateHeuristics = attr.ib(default=TemplateHeuristics.DEFAULT)
|
|
58
|
+
""" The heuristics to use for template genesis"""
|
|
59
|
+
|
|
60
|
+
positivity_heuristic: PositivityHeuristics = attr.ib(default=PositivityHeuristics.DEFAULT)
|
|
61
|
+
""" The heuristics to validate positivity"""
|
|
62
|
+
|
|
63
|
+
solver_type: SolverType = attr.ib(default=SolverType.SYMPY)
|
|
64
|
+
"""The solver to use for equation systems."""
|
|
65
|
+
|
|
66
|
+
show_all_invs: bool = attr.ib(default=False)
|
|
67
|
+
"""Toggels printing of spurious invariants"""
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def optimizer(self) -> Type[Optimizer]:
|
|
71
|
+
if self.engine == ForwardAnalysisConfig.Engine.SYMPY:
|
|
72
|
+
return GFOptimizer
|
|
73
|
+
else:
|
|
74
|
+
raise ConfigurationError(
|
|
75
|
+
"The configured engine does not implement an optimizer.")
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def factory(self) -> Type[CommonDistributionsFactory]:
|
|
79
|
+
if self.engine == self.Engine.SYMPY:
|
|
80
|
+
return SympyPGF
|
|
81
|
+
elif self.engine == self.Engine.GINAC:
|
|
82
|
+
# pygin is optional (prodigy-ppl[ginac]); only the GiNaC engine imports it
|
|
83
|
+
try:
|
|
84
|
+
# pylint: disable=import-outside-toplevel
|
|
85
|
+
from prodigy.distribution.fast_generating_function import ProdigyPGF
|
|
86
|
+
except ImportError as error:
|
|
87
|
+
raise ConfigurationError(
|
|
88
|
+
"The GiNaC engine needs pygin: pip install \"pygin @ "
|
|
89
|
+
"git+https://github.com/LKlinke/prodigy-ginac-bindings.git@dynamic-variables\"") from error
|
|
90
|
+
return ProdigyPGF
|
|
91
|
+
elif self.engine == self.Engine.SYMENGINE:
|
|
92
|
+
return SymenginePGF
|
|
93
|
+
else:
|
|
94
|
+
return CommonDistributionsFactory
|
|
95
|
+
|
|
96
|
+
def __attrs_post_init__(self):
|
|
97
|
+
GeneratingFunction.use_latex_output = self.use_latex
|
|
98
|
+
GeneratingFunction.use_simplification = self.use_simplification
|