lp2graph 0.3.0__tar.gz → 0.4.0__tar.gz
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.
- lp2graph-0.4.0/CHANGELOG.md +380 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/PKG-INFO +25 -5
- {lp2graph-0.3.0 → lp2graph-0.4.0}/README.md +18 -0
- lp2graph-0.4.0/docs/DECISIONS.md +23 -0
- lp2graph-0.4.0/docs/STACK.md +196 -0
- lp2graph-0.4.0/docs/adr/0008-pulp4-forward-compatible-solver.md +98 -0
- lp2graph-0.4.0/docs/adr/0009-ingest-untrusted-input-hardening.md +71 -0
- lp2graph-0.4.0/docs/adr/0010-codec-roundtrip-fidelity.md +87 -0
- lp2graph-0.4.0/docs/adr/0011-canonical-formulation-as-interchange-pivot.md +72 -0
- lp2graph-0.4.0/docs/adr/0012-single-format-registry.md +68 -0
- lp2graph-0.4.0/docs/adr/0013-ship-and-enforce-the-canonical-schema.md +140 -0
- lp2graph-0.4.0/docs/adr/0014-extras-resolve-on-every-supported-python.md +78 -0
- lp2graph-0.4.0/docs/adr/0015-text-io-declares-encoding-and-newline.md +72 -0
- lp2graph-0.4.0/docs/adr/0016-one-reading-of-aggregation-loops.md +88 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/configurator.html +18 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/explore.html +11 -8
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/index.html +9 -0
- lp2graph-0.4.0/docs/interop.md +81 -0
- lp2graph-0.4.0/docs/validation.md +113 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/views.md +21 -4
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_8_pesp.json +1 -2
- {lp2graph-0.3.0 → lp2graph-0.4.0}/pyproject.toml +33 -5
- {lp2graph-0.3.0 → lp2graph-0.4.0}/schema/canonical.schema.json +8 -8
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/__init__.py +4 -1
- lp2graph-0.4.0/src/lp2graph/_optional.py +66 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/cli.py +160 -19
- lp2graph-0.4.0/src/lp2graph/codec/latex.py +1298 -0
- lp2graph-0.4.0/src/lp2graph/core/loader.py +118 -0
- lp2graph-0.4.0/src/lp2graph/core/scope.py +150 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/pyomo_stub.py +7 -0
- lp2graph-0.4.0/src/lp2graph/formats.py +109 -0
- lp2graph-0.4.0/src/lp2graph/interop/__init__.py +79 -0
- lp2graph-0.4.0/src/lp2graph/interop/_grounded.py +498 -0
- lp2graph-0.4.0/src/lp2graph/interop/_linexpr.py +114 -0
- lp2graph-0.4.0/src/lp2graph/interop/ampl.py +237 -0
- lp2graph-0.4.0/src/lp2graph/interop/gams.py +281 -0
- lp2graph-0.4.0/src/lp2graph/interop/gurobi.py +202 -0
- lp2graph-0.4.0/src/lp2graph/interop/jump.py +289 -0
- lp2graph-0.4.0/src/lp2graph/interop/lp_format.py +334 -0
- lp2graph-0.4.0/src/lp2graph/interop/mps.py +310 -0
- lp2graph-0.4.0/src/lp2graph/interop/pulp_io.py +97 -0
- lp2graph-0.4.0/src/lp2graph/interop/pyomo_io.py +217 -0
- lp2graph-0.4.0/src/lp2graph/mining/cluster/agglomerative.py +151 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/distance.py +39 -3
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/operator.py +2 -1
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/__init__.py +7 -1
- lp2graph-0.4.0/src/lp2graph/mining/ingest/code_importers.py +164 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/dispatch.py +42 -32
- lp2graph-0.4.0/src/lp2graph/mining/ingest/latex_normalizer.py +1217 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/versions.py +1 -1
- lp2graph-0.4.0/src/lp2graph/schema/__init__.py +62 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/solve/__init__.py +35 -3
- lp2graph-0.4.0/src/lp2graph/solve/grounder.py +637 -0
- lp2graph-0.4.0/src/lp2graph/solve/solvers.py +99 -0
- lp2graph-0.4.0/src/lp2graph/validation/__init__.py +52 -0
- lp2graph-0.4.0/src/lp2graph/validation/detect.py +328 -0
- lp2graph-0.4.0/src/lp2graph/validation/pipeline.py +717 -0
- lp2graph-0.4.0/src/lp2graph/validation/report.py +139 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/ground.py +76 -60
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/hybrid.py +24 -4
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/schema.py +48 -2
- lp2graph-0.4.0/tests/interop/_models.py +246 -0
- lp2graph-0.4.0/tests/interop/conftest.py +15 -0
- lp2graph-0.4.0/tests/interop/test_convert_cli.py +76 -0
- lp2graph-0.4.0/tests/interop/test_grounded.py +141 -0
- lp2graph-0.4.0/tests/interop/test_object_apis.py +282 -0
- lp2graph-0.4.0/tests/interop/test_text_formats.py +212 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_cluster.py +89 -0
- lp2graph-0.4.0/tests/mining/test_ingest.py +1015 -0
- lp2graph-0.4.0/tests/test_aggregation_scope.py +278 -0
- lp2graph-0.4.0/tests/test_cli.py +114 -0
- lp2graph-0.4.0/tests/test_formats.py +68 -0
- lp2graph-0.4.0/tests/test_interchange_contract.py +234 -0
- lp2graph-0.4.0/tests/test_optional_deps.py +150 -0
- lp2graph-0.4.0/tests/test_solve.py +325 -0
- lp2graph-0.4.0/tests/test_text_io_encoding.py +141 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_views.py +18 -0
- lp2graph-0.4.0/tests/validation/test_detect.py +99 -0
- lp2graph-0.4.0/tests/validation/test_pipeline.py +210 -0
- lp2graph-0.3.0/CHANGELOG.md +0 -137
- lp2graph-0.3.0/docs/STACK.md +0 -95
- lp2graph-0.3.0/src/lp2graph/codec/latex.py +0 -884
- lp2graph-0.3.0/src/lp2graph/core/loader.py +0 -63
- lp2graph-0.3.0/src/lp2graph/mining/cluster/agglomerative.py +0 -82
- lp2graph-0.3.0/src/lp2graph/mining/ingest/code_importers.py +0 -104
- lp2graph-0.3.0/src/lp2graph/mining/ingest/latex_normalizer.py +0 -243
- lp2graph-0.3.0/src/lp2graph/solve/grounder.py +0 -405
- lp2graph-0.3.0/tests/mining/test_ingest.py +0 -230
- lp2graph-0.3.0/tests/test_solve.py +0 -68
- {lp2graph-0.3.0 → lp2graph-0.4.0}/.gitignore +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/CITATION.cff +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/LICENSE +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/corpus/validation/README.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/corpus/validation/codec_pipeline/README.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/add-a-formulation.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0001-build-tool.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0002-internal-graph.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0003-license.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0004-schema-versioning.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0005-viewer-hosting.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0006-determinism.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0007-optional-deps-lazy-import.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/catalog.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/data-model.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/design-context.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/extraction-report.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/index.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/metrics.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/mining.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/text-graph-interface.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/v1-acceptance.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/01-load-and-validate.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/02-derive-three-views.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/03-compute-metrics.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/04-export-to-pyg.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/05-add-a-new-formulation.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/ModelOrTools.java +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/README.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/build_pdf.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/data.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model.lp +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model.mps +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ampl.mod +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_cvxpy.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_docplex.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_gams.gms +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_glpk.mod +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_gurobi.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_jump.jl +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_matlab.m +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_minizinc.mzn +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ompr.R +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_opl.mod +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ortools.cpp +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ortools.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_pulp.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_pyomo.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_python_mip.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_zimpl.zpl +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/problem.tex +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/railway_bigm_milp.pdf +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/assignment.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/lp_1_1_fixed_sequence.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/lp_1_5_soft_regularity.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_1_big_m.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_4_time_indexed.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/pesp_solvable.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/multi_obj_lateness_energy.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/objective_abs_deviation.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/objective_lex_priority.json +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/legacy/README.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/codec/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/codec/normalize.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/graph.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/model.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/validate.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/dgl.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/latex.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/networkx_adapter.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/pyg.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/classification.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/flags.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/result.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/structural.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/silhouette.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/stability.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/taxonomy.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/dedup.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/manager.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/manifest.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/record.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/select.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/concept.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/entity.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/lemmatize.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/signature.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/thesaurus.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/tokenize.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/vectorize.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/pyomo_importer.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/result.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/isomorphism/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/isomorphism/report.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/classifier.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/features.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/guardrails.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/loop.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/rules.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/store.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/vocab.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/provenance.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/nl/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/nl/describe.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/palette.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/svg.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/solve/instance.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/transform/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/transform/bigm.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/__init__.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/conftest.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_corpus_manager.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_homologize.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_isomorphism.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_label.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_bigm.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_codec.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_describe.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_export.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_metrics.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_render.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_schema.py +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/ui/README.md +0 -0
- {lp2graph-0.3.0 → lp2graph-0.4.0}/viewer/index.html +0 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.4.0] - 2026-10-04
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`pulp<4` until the back-end is ported** (issue #65). PuLP 4.0.0 rebuilt its
|
|
14
|
+
model classes on a Rust core (no Python-constructible `LpAffineExpression` /
|
|
15
|
+
`LpVariable`, no `PULP_CBC_CMD`, renamed constraints), so a fresh install of
|
|
16
|
+
the `solver` or `all` extra resolved a PuLP the back-end cannot drive. Both
|
|
17
|
+
extras now cap it at `<4` (ADR-0008 addendum).
|
|
18
|
+
- **Summed families the referent does not vary with are summed** (issue #60).
|
|
19
|
+
The grounder looped only over the dummies a term's bindings use and never
|
|
20
|
+
read `Term.operator_over`, so `\\sum_{i \\in I, r \\in R} dur_r` grounded
|
|
21
|
+
to `\\sum_r dur_r` and a summed constant `\\sum_{i \\in I} 5` to `5`: the
|
|
22
|
+
solved value was wrong while text and schema graph were right. Emitter,
|
|
23
|
+
ground view and grounder now share one reading of an aggregation's loops,
|
|
24
|
+
`lp2graph.core.scope` (ADR-0016); a binder whose family disagrees with the
|
|
25
|
+
referent's declared shape (a sum over a subset family, `\\sum_{r \\in
|
|
26
|
+
\\mathcal{R_k}} x_r` with `x` over `R`) is refused (`UnsupportedModel`)
|
|
27
|
+
instead of being summed over the whole slot family.
|
|
28
|
+
- **Ground view: summed terms have edges, pair variables are not the
|
|
29
|
+
diagonal.** The ground view keyed its sum scope by family but resolved
|
|
30
|
+
bindings by dummy, so every aggregated term contributed no edge (the
|
|
31
|
+
`assignment` and `mip_2_4_time_indexed` fixtures grounded to graphs with no
|
|
32
|
+
edges at all), and it resolved bindings into a dict keyed by family, so
|
|
33
|
+
`y_{i,j}` over `I x I` was wired to `y[j,j]`. Bindings now resolve by slot
|
|
34
|
+
position, `abs` aggregates over its free indices as in the grounder, and a
|
|
35
|
+
recurring summand is one edge with a `multiplicity`.
|
|
36
|
+
- **Emitter: binders print what the model says.** `\\sum_{t \\in T}
|
|
37
|
+
x_{t-1}` was emitted as the unparseable `\\sum_{t-1 \\in T} x_{t-1}`; a
|
|
38
|
+
summed constant lost its binder (the round trip changed the model);
|
|
39
|
+
`\\sum_{j \\in I} y_{i,j}` under `\\forall i \\in I` was printed as
|
|
40
|
+
`\\sum_{i \\in I} y_{i,j}`; a sum over a subset family printed a dummy
|
|
41
|
+
the summand does not use (`\\sum_{r_k \\in \\mathcal{R_k}} x_r`); and a
|
|
42
|
+
counting loop re-bound the row's own dummy. The model round trip was intact
|
|
43
|
+
for the last three, the text was not.
|
|
44
|
+
- **Hybrid view keeps both bindings of a same-family pair.** `offsets` was
|
|
45
|
+
keyed by family, so `y_{i,j}` kept only `j`; repeated families are now
|
|
46
|
+
numbered (`I.1`, `I.2`).
|
|
47
|
+
- **A dumped model loads again.** Since the loader began enforcing the JSON
|
|
48
|
+
Schema (ADR-0013), `loads(f.model_dump_json())` failed for every model with
|
|
49
|
+
an untagged domain facet, no provenance or no objective: the model dumps
|
|
50
|
+
those as `null`, which the schema did not admit for `domain_class`,
|
|
51
|
+
`domain_role`, `provenance` and `objective` (it already did for `lower`,
|
|
52
|
+
`upper`, `indicator` and `where`). The schema now admits `null` for them
|
|
53
|
+
(absent or `null` = unset); every previously valid document stays valid.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- **A `where`-predicate is a graph edge** (issue #61). The schema and hybrid
|
|
58
|
+
views add `uses_parameter` (role `where`) from a constraint to the
|
|
59
|
+
parameter its quantifier predicate reads; the hybrid edge carries the
|
|
60
|
+
compared value. Formulations that select rows by attribute get different
|
|
61
|
+
schema graphs, M6 isomorphism results and WL hashes than before (edges are
|
|
62
|
+
matched on `type|role`); formulations without predicates are unchanged.
|
|
63
|
+
|
|
64
|
+
- **M3 clustering 50x faster, bit-identical results.** `distance_matrix`
|
|
65
|
+
reduces every pair to the non-zero coordinates of the row vector (a zero
|
|
66
|
+
product adds exactly 0.0, so the dense sum is reproduced coordinate for
|
|
67
|
+
coordinate), and `agglomerative` caches every group pair's average linkage
|
|
68
|
+
by group id, recomputes only the merged group's distances with the exact
|
|
69
|
+
member order of the from-scratch loop, and keeps each row's first-minimum
|
|
70
|
+
column, so a merge costs O(g) instead of a full O(n^2 x members)
|
|
71
|
+
rescan. Ties still resolve to the first pair in (row, column) order; a
|
|
72
|
+
property test with forced ties locks the equivalence to the naive
|
|
73
|
+
definition. One taxonomy induction over the lab corpus went from 84 s to
|
|
74
|
+
1.5 s.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **Binder letters, big-M products, set algebra in binders**
|
|
79
|
+
(`rewrite-2026.09.4`): `bigop_lone_binder` gives `\\sum_{n}` the family the
|
|
80
|
+
declared shapes assign to `n` in the row; `distribute_param` distributes a
|
|
81
|
+
declared parameter over a parenthesised sum exactly (`M \\left(1 -
|
|
82
|
+
x\\right)` -> `M - M \\cdot x`, numeric pieces become `2 \\cdot M`);
|
|
83
|
+
`restricted_set_widen` handles set differences/intersections inside binder
|
|
84
|
+
groups (brace-aware); `declared_product` and `distribute_param` never fire
|
|
85
|
+
inside a quantifier tail.
|
|
86
|
+
- **Implicit structure the corpus leaves unsaid** (`rewrite-2026.09.3`,
|
|
87
|
+
measured against the lab corpus after `rewrite-2026.09.2`): a row without
|
|
88
|
+
any quantifier gets `\\forall` over the families the declared shapes give
|
|
89
|
+
its free index letters (`implicit_quantifier`, recorded; a letter that would
|
|
90
|
+
get two families leaves the row untouched); `\\forall f` / `\\forall i, j
|
|
91
|
+
\\in J` resolve lone letters the same way (`forall_lone_letters`);
|
|
92
|
+
memberships listed after the algebra without `\\forall` are the
|
|
93
|
+
quantifier tail (codec); an equality chain `a = b = c` and a same-direction
|
|
94
|
+
chain with three or more comparators split into consecutive rows, and an
|
|
95
|
+
interval membership `t \\in [a, b]` into its two bounds (codec, exact);
|
|
96
|
+
strict `<`/`>` in the algebra relax to `\\le`/`\\ge` as a recorded
|
|
97
|
+
approximation (restrictions in a tail keep `<`); a parenthesised single
|
|
98
|
+
symbol `(\\bar{a})_{ik}` is that symbol (`paren_symbol_unwrap`);
|
|
99
|
+
`\\begin{matrix}`-style wrappers inside a row are dropped
|
|
100
|
+
(`env_wrapper_strip`); `\\mathbf{\\mathcal{E}}` and `\\in \\mathbf{I}`
|
|
101
|
+
are the set (`mathbf_set_unwrap`); `\\text{u}` naming a declared or bound
|
|
102
|
+
symbol is the identifier (`text_ident_symbol`); `restricted_set_widen` also
|
|
103
|
+
widens function-style sets `H(e)` and set differences/intersections
|
|
104
|
+
(`S \\setminus T`, `S \\cap T`) to the base family, recorded; unions are
|
|
105
|
+
refused.
|
|
106
|
+
- **Row conventions the corpus writes** (`rewrite-2026.09.2`, issue #64;
|
|
107
|
+
lab measurement: 55 of 6,207 accepted rows parsed before this). Codec: the
|
|
108
|
+
quantifier tail starts at `\qquad` OR the first top-level `\forall`
|
|
109
|
+
(a dangling comma before it is dropped); trailing `,` `.` `;` `:` on a
|
|
110
|
+
row, a term or a tail are typography; binder and quantifier clauses accept
|
|
111
|
+
`i \in \mathcal{I}`, `i \in \mathit{I}`, `i \in I` and `i, j \in I`,
|
|
112
|
+
and REFUSE by name what used to bind silently or vanish (a subscripted set,
|
|
113
|
+
a tuple binder, a range, any clause that is neither a set membership, a
|
|
114
|
+
where-clause nor a restriction). M1b: `text_ident_script` unwraps
|
|
115
|
+
`\text{l}`/`\mathrm{l}` inside a script; `declared_product` inserts the
|
|
116
|
+
`\cdot` between a declared parameter and a declared symbol written side by
|
|
117
|
+
side (coefficient first, `x_{i} c_{i}` -> `c_{i} \cdot x_{i}`), leaving
|
|
118
|
+
undeclared names for the parser to refuse. `restricted_set_widen` widens a
|
|
119
|
+
binder or quantifier over a subscripted/superscripted set (`\\mathcal{S}_{i}`)
|
|
120
|
+
to its base family and records the widening as a rewrite (the same recorded
|
|
121
|
+
approximation the repository converter makes); Greek index letters
|
|
122
|
+
(`\\mathit{delta} \\in T`) bind, and a `:` in a quantifier tail separates a
|
|
123
|
+
restriction clause.
|
|
124
|
+
- **M1b declaration-driven script resolution** (`rewrite-2026.09.1`,
|
|
125
|
+
issue #63; corpus evidence: 49 + 21 of the 220 papers still failing the
|
|
126
|
+
Paper-1 promotion after the #52–#57 batch stall on superscripts and
|
|
127
|
+
label subscripts). The canonical grammar has no superscripts and every
|
|
128
|
+
subscript position is an index, so the normalizer now decides what a
|
|
129
|
+
script is from the document itself — the `%@` header (declared names,
|
|
130
|
+
shapes) and the body's binders/quantifiers — and rewrites bijectively:
|
|
131
|
+
`bare_sub_brace`/`bare_sup_brace` brace unbraced scripts against the
|
|
132
|
+
declarations (`B_u \cdot w_u` -> `B_{u} \cdot w_{u}`, a declared plain
|
|
133
|
+
name such as `Z_1` stays); `superscript_index` moves bound-letter
|
|
134
|
+
superscripts into the subscript (`x_{i}^{k}` -> `x_{i, k}`,
|
|
135
|
+
`p_{n}^{t + 1}` -> `p_{n, t + 1}`, MathML-spaced `^{i j}` -> two
|
|
136
|
+
indices); `superscript_label` folds label superscripts into plain names
|
|
137
|
+
(`t_{i}^{arr}` -> `t_arr_{i}`, `v_{i}^{c}` -> `v_c_{i}`,
|
|
138
|
+
`\mathit{tau}_{k}^{de}` -> `tau_de_{k}`, `Y_{i,s}^{1}` -> `Y_1_{i,s}`,
|
|
139
|
+
`q^{*}` -> `q_star`, `d^{+}`/`d^{-}` -> `d_plus`/`d_minus`); `label_subscript` folds label subscripts
|
|
140
|
+
(`h_{min}` -> `h_min`, `Z_{1}` -> `Z_1` when `Z` has no declared shape;
|
|
141
|
+
a numeric subscript on a shaped symbol stays a fixed-element reference).
|
|
142
|
+
Nested or delimited scripts are left untouched for the parser to refuse
|
|
143
|
+
by name, glued bound letters split into indices (`x_{ij}` -> `x_{i, j}`),
|
|
144
|
+
and a document without any `%@` declaration is left untouched by all
|
|
145
|
+
five rules (nothing to resolve against). `RewriteRule` gained `ctx_replacement` (rules that read a
|
|
146
|
+
`DocContext`), and a rule that declines records no rewrite. The
|
|
147
|
+
declaration sidecar must declare the folded spellings.
|
|
148
|
+
|
|
149
|
+
### Fixed
|
|
150
|
+
|
|
151
|
+
- **Codec accepted undeclared identifiers silently** (issue #62): an
|
|
152
|
+
undeclared referent fell through `_SymTab.kind()` as a `literal` term
|
|
153
|
+
and a bare coefficient name was returned without checking it is a
|
|
154
|
+
declared parameter, so unbraced subscripts (`B_u \cdot w_u`) ingested
|
|
155
|
+
`ok=True` with `ref="w_u"`, `coefficient="B_u"` and did not round-trip
|
|
156
|
+
(surfaced by the lab's re-promoted `trc.2014.06.003`). Both paths now
|
|
157
|
+
refuse by name, pointing at the declaration and the braced form.
|
|
158
|
+
|
|
159
|
+
- **Grammar holes #52–#57 closed deterministically** (issues filed from the
|
|
160
|
+
Paper-1 corpus promotion sprint; every fix is exact-or-refused-by-name,
|
|
161
|
+
nothing is dropped silently):
|
|
162
|
+
- *Codec parser* (`codec.latex`): `\leq`/`\geq`/`\leqslant`/`\geqslant`
|
|
163
|
+
accepted as row comparators with token guards (previously `\leq` was read
|
|
164
|
+
as `\le` plus a stray `q`, and `\left|` on a constraint LHS was read as
|
|
165
|
+
`\le` — both silently corrupted the relation split); chained
|
|
166
|
+
same-direction inequality rows `l \le e \le u` split into two constraints
|
|
167
|
+
`<name>_lo`/`<name>_up` inheriting the row's quantifiers (#53;
|
|
168
|
+
mixed-direction, equality, and 3+-comparator chains refused by name);
|
|
169
|
+
subscripted coefficients `w_{e} \cdot x_{e}` resolve to the bare
|
|
170
|
+
parameter exactly when the written indices match what grounding uses —
|
|
171
|
+
the referent's bindings, or the unique in-scope binder/quantifier index
|
|
172
|
+
per declared shape slot (#52); numeric `\frac{a}{b}` with a terminating
|
|
173
|
+
decimal folds into the numeric coefficient, every other `\frac` is
|
|
174
|
+
refused by name (#57); constant subscripts (`t_{0}`) get a named
|
|
175
|
+
diagnostic pointing at the missing shape declaration — with the family
|
|
176
|
+
declared they were already legal element references (#54); trailing
|
|
177
|
+
`^{...}` or juxtaposed factors after a referent's subscript are refused
|
|
178
|
+
by name instead of being **silently dropped** (previously
|
|
179
|
+
`p_{e} x_{e}` parsed with `x` discarded and `x_{e}^{k}` lost its
|
|
180
|
+
superscript index).
|
|
181
|
+
- *M1b normalizer* (`mining.ingest.latex_normalizer`): `sum_merge`
|
|
182
|
+
(consecutive `\sum` operators merge into one multi-binder `\sum`,
|
|
183
|
+
order-preserving, #56); `overset_accent`/`underset_accent` (accent-shaped
|
|
184
|
+
`\overset`/`\underset` pairs collapse to the standard accent command
|
|
185
|
+
before `overset_base` can drop the accent); `accent_ident` and
|
|
186
|
+
`prime_ident` (decorated identifiers rename bijectively to plain `\w+`
|
|
187
|
+
names — `\hat{tc}` → `tc_hat`, `t'`/`k^{'}` → `tp`/`kp` — valid in every
|
|
188
|
+
grammar position, #55). Rule-table version bumped to `rewrite-2026.08.1`.
|
|
189
|
+
|
|
190
|
+
- **`lp2graph.validation`** — end-to-end validation of (LLM-)generated
|
|
191
|
+
LP/MILP artifacts. `validate_text` / `validate_path` / `validate_formulation`
|
|
192
|
+
accept raw text, bytes, files, or parsed models in any supported format
|
|
193
|
+
(canonical JSON, LaTeX, LP, MPS, GAMS, AMPL, JuMP) and return a structured
|
|
194
|
+
`ValidationReport` instead of raising: faulty-input detectors and repairs
|
|
195
|
+
(markdown fences, unicode look-alikes, truncation, NUL/encoding damage),
|
|
196
|
+
format sniffing with parse fallbacks, the semantic invariants, structural
|
|
197
|
+
detectors (completeness, coherence, duplicate/unused symbols, constant
|
|
198
|
+
constraints, bound conflicts), and an optional grounding smoke check on a
|
|
199
|
+
synthesized all-ones instance (CBC/HiGHS/Gurobi; skipped gracefully without
|
|
200
|
+
pulp). Reports are deterministic and stamped with a `pipeline_version`.
|
|
201
|
+
- **`lp2graph validate` CLI** upgraded to run that pipeline on any supported
|
|
202
|
+
model file (previously canonical JSON only): `--fmt`, `--json`, `--no-solve`,
|
|
203
|
+
`--instance`, `--solver`, `--time-limit`; exit code 0 unless the verdict is
|
|
204
|
+
`invalid`. New docs page `docs/validation.md`.
|
|
205
|
+
|
|
206
|
+
- **M1b big-operator wrapper rules** (`mining.ingest.latex_normalizer`) — four
|
|
207
|
+
new rewrite rules driven by Paper-1 corpus evidence (3,668 of 8,957 Tier-2
|
|
208
|
+
MathML-derived formulas): `underset_bigop` (`\underset{X}{\sum}` →
|
|
209
|
+
`\sum_{X}`, likewise `\prod`/`\min`/`\max`/`\int`/`\bigcup`/`\bigcap`),
|
|
210
|
+
`mathop_unwrap`, `underbrace_unwrap`, and `overset_base`. Rule-table version
|
|
211
|
+
bumped to `rewrite-2026.07.0`.
|
|
212
|
+
- **`lp2graph.interop`** — functional code ⇄ graph interfaces for the common
|
|
213
|
+
modeling languages, replacing the historic stubs. Importers build
|
|
214
|
+
coefficient-faithful flat `Formulation`s from **gurobipy** models
|
|
215
|
+
(`from_gurobipy`), **PuLP** problems (`from_pulp`), **Pyomo** concrete
|
|
216
|
+
models (`from_pyomo`, full linear term recovery via `standard_repn`;
|
|
217
|
+
ranged constraints split), and **LP / MPS / GAMS / AMPL / JuMP** text
|
|
218
|
+
(`from_lp_string` / `from_mps_string` / `from_gams` / `from_ampl` /
|
|
219
|
+
`from_jump`). Exporters emit every one of those targets from any
|
|
220
|
+
formulation (`to_gurobipy[_code]`, `to_pulp[_code]`, `to_pyomo[_code]`,
|
|
221
|
+
`to_lp_string`, `to_mps_string`, `to_gams`, `to_ampl`, `to_jump`) — flat
|
|
222
|
+
models directly, template-level models through the PuLP grounder with an
|
|
223
|
+
`Instance`. All emitters are deterministic fixpoints; unsupported
|
|
224
|
+
constructs raise `InteropError` instead of being dropped. Verified by a
|
|
225
|
+
`code → graph → code` round-trip matrix (`tests/interop/`, 100 tests)
|
|
226
|
+
that solves every path against hand-verified optima with CBC, HiGHS, and
|
|
227
|
+
Gurobi, including cross-reads of Gurobi-written `.lp`/`.mps` files.
|
|
228
|
+
- **`lp2graph convert IN OUT`** — CLI conversion between modeling languages
|
|
229
|
+
through the canonical graph, routed by file extension
|
|
230
|
+
(`.json/.tex/.lp/.mps/.gms/.mod/.jl`, plus `.py` solver scripts via
|
|
231
|
+
`--python-api {gurobipy,pulp,pyomo}`).
|
|
232
|
+
- **`mining.ingest`** now routes `.gms/.mod/.jl` to the real interop parsers
|
|
233
|
+
(previously honest stubs) and gained `.lp`/`.mps` support (`import_lp`,
|
|
234
|
+
`import_mps`); parse problems surface as structured `stage="parse"`
|
|
235
|
+
failures.
|
|
236
|
+
|
|
237
|
+
- **`metrics.model_completeness`** — the second model-level well-formedness
|
|
238
|
+
indicator described in *LP Mining with LP2Graph* (objective declared together
|
|
239
|
+
with ≥1 variable and ≥1 constraint), companion to `model_coherence`. Now
|
|
240
|
+
computed and wired into the Level-M structural feature document
|
|
241
|
+
(`mining.cluster.taxonomy.model_feature_document`) as a `complete:{0,1}`
|
|
242
|
+
feature, closing a paper↔code gap where the paper named both indicators but
|
|
243
|
+
only coherence was implemented.
|
|
244
|
+
- **LP mining extensions** (`lp2graph.mining`, issues #38–#43) — six
|
|
245
|
+
deterministic modules implementing the *LP Mining with LP2Graph* method on
|
|
246
|
+
top of the core library. Every frozen resource is versioned in
|
|
247
|
+
`lp2graph.mining.versions` and stamped into emitted records.
|
|
248
|
+
- **M1 `mining.ingest`** — heterogeneous ingestion front-end: a Pyomo
|
|
249
|
+
importer (`from_pyomo`), a versioned non-canonical LaTeX normalizer with
|
|
250
|
+
source-span provenance (`normalize_latex` / `ingest_latex`), and an
|
|
251
|
+
extension dispatcher (`ingest`) that reports failures as structured
|
|
252
|
+
`IngestionResult`s rather than dropping them.
|
|
253
|
+
- **M2 `mining.homologize`** — lexical homologizer (tokenize / lemmatize /
|
|
254
|
+
versioned stop-list / frozen domain thesaurus + optional WordNet) and a
|
|
255
|
+
TF-IDF `ConceptVectorizer` over a sorted, diffable `Vocabulary`, plus the
|
|
256
|
+
type signature `τ(s)` and the `Entity` model for levels V/C/M.
|
|
257
|
+
- **M3 `mining.cluster`** — the cluster-and-name operator `CN` (deterministic
|
|
258
|
+
average-linkage default, `fixed_k`+silhouette, optional HDBSCAN), the
|
|
259
|
+
bottom-up Level V→C→M taxonomy `induce`, and `stability_report`
|
|
260
|
+
(silhouette + bootstrap ARI + sensitivity).
|
|
261
|
+
- **M4 `mining.label`** — two-stage labeling service: rule layer + calibrated
|
|
262
|
+
one-vs-rest `LinearSVM`, a confidence-gated closed loop with a versioned,
|
|
263
|
+
replayable `LabelStore`, rule promotion, retraining, and gold-set
|
|
264
|
+
guardrails (drift, per-class P/R, Cohen's κ, rollback flag).
|
|
265
|
+
- **M5 `mining.corpusmgr`** — provenance records, a regeneration `CorpusManifest`
|
|
266
|
+
(frozen search date + queries), deterministic dedup (schema-graph hash or
|
|
267
|
+
bibliographic key), and reproducible representative selection.
|
|
268
|
+
- **M6 `mining.isomorphism`** — per-cluster schema-graph isomorphism rate via
|
|
269
|
+
the NetworkX export (`isomorphism_report`).
|
|
270
|
+
|
|
271
|
+
### Fixed
|
|
272
|
+
|
|
273
|
+
- **`interop.from_pulp` crashed on unnamed constraints** (`prob += expr <= rhs`
|
|
274
|
+
without a name label): the constraint's ``name`` is ``None`` and the
|
|
275
|
+
name-sanitizer raised ``TypeError``. Anonymous constraints now take the
|
|
276
|
+
deterministic ``c1``/``c2``/... fallback names.
|
|
277
|
+
|
|
278
|
+
### Changed
|
|
279
|
+
|
|
280
|
+
- **PuLP 4.0 forward-compatibility** (`lp2graph.solve` + `lp2graph.interop`):
|
|
281
|
+
migrated off the APIs PuLP 4.0 removes — variables are built with
|
|
282
|
+
`prob.add_variable(...)`, constraints counted with `prob.numConstraints()`,
|
|
283
|
+
and the default CBC solver is created by the new `solve.default_solver()`
|
|
284
|
+
(`COIN_CMD` with a bundled-CBC fallback) instead of the deprecated
|
|
285
|
+
`PULP_CBC_CMD`. `interop.grounded_from_pulp` lists constraints
|
|
286
|
+
version-agnostically, `to_pulp_code` emits scripts using the sanctioned
|
|
287
|
+
APIs, and the M1 ingest reader reports non-UTF-8 source files as
|
|
288
|
+
structured read-stage failures (ADR-0009) instead of raising.
|
|
289
|
+
|
|
290
|
+
- **CI now gates formatting:** added a `ruff format --check src tests` step to
|
|
291
|
+
`ci.yml` (previously only `ruff check` ran, so format drift could land
|
|
292
|
+
undetected). Bumped the `ruff-pre-commit` pin v0.4.10 → v0.15.12 and the
|
|
293
|
+
`dev` extra to `ruff>=0.15.12,<0.16` so local, pre-commit, and CI agree;
|
|
294
|
+
reformatted 21 pre-existing files to the current ruff style.
|
|
295
|
+
|
|
296
|
+
### Docs
|
|
297
|
+
|
|
298
|
+
- Added `docs/STACK.md` (software-stack reference) and ADR-0006 (determinism as
|
|
299
|
+
a hard requirement) and ADR-0007 (optional dependencies are lazily imported),
|
|
300
|
+
wired into the MkDocs nav.
|
|
301
|
+
|
|
302
|
+
## [0.3.0] - 2026-06-01
|
|
303
|
+
|
|
304
|
+
### Added
|
|
305
|
+
|
|
306
|
+
- **Deterministic LaTeX ⇄ graph codec** (`lp2graph.codec`): paper-style
|
|
307
|
+
LaTeX (`\mathcal` index sets, `\sum`, `\forall`, big-M) round-trips with
|
|
308
|
+
the canonical model with no LLM in the loop.
|
|
309
|
+
`to_canonical_latex` / `from_canonical_latex`, plus
|
|
310
|
+
`canonical_normal_form` for round-trip comparison. The LaTeX
|
|
311
|
+
serialization is a tested fixed point (`to(from(to(f))) == to(f)`); the
|
|
312
|
+
solvable content round-trips exactly.
|
|
313
|
+
- **Real grounding solver back-end** (`lp2graph.solve`): replaces the v0.1
|
|
314
|
+
Pyomo stub. Grounds a formulation with an `Instance` (cardinalities +
|
|
315
|
+
parameter values) into a concrete `pulp.LpProblem` and solves it
|
|
316
|
+
(CBC / HiGHS / Gurobi). Covers the linear core including big-M and PESP
|
|
317
|
+
modulo; boundary-degenerate constraint instances are correctly omitted.
|
|
318
|
+
- **Deterministic graph → natural-language describer** (`lp2graph.nl`):
|
|
319
|
+
generates a Markdown problem description (sets, data, decisions,
|
|
320
|
+
per-constraint sentences, objective) with parameter **data tables** when
|
|
321
|
+
an instance is supplied.
|
|
322
|
+
- **End-to-end validation suite** (`corpus/validation/codec_pipeline/`):
|
|
323
|
+
runs the full JSON→LaTeX→parse→ground→solve loop and checks codec
|
|
324
|
+
round-trip, pipeline-vs-direct equality, cross-solver agreement, and
|
|
325
|
+
match to independently established optima (assignment 13, big-M ordering
|
|
326
|
+
30, fixed-sequence 18, PESP 1, time-indexed 4). Records paper anchors
|
|
327
|
+
(timtab1 = 764772, marcotallone = 3913.47).
|
|
328
|
+
- New formulations: `assignment.json`, `pesp_solvable.json`.
|
|
329
|
+
- CLI: `lp2graph latex | parse | describe | solve`.
|
|
330
|
+
- New tests: `test_codec.py`, `test_solve.py`, `test_describe.py`.
|
|
331
|
+
- Optional `solver` extra (`pulp`, `highspy`).
|
|
332
|
+
|
|
333
|
+
## [0.2.0] - 2026-05-20
|
|
334
|
+
|
|
335
|
+
### Changed
|
|
336
|
+
|
|
337
|
+
- **BREAKING:** the import package is renamed `optgraph` → `lp2graph`, so it
|
|
338
|
+
now matches the distribution name. Update imports
|
|
339
|
+
(`from optgraph import …` → `from lp2graph import …`) and the console
|
|
340
|
+
script (`optgraph …` → `lp2graph …`). No behaviour changed; this is a
|
|
341
|
+
pure rename. The canonical `schema_version` is unaffected (still `0.1.0`).
|
|
342
|
+
|
|
343
|
+
## [0.1.0] - 2026-05-03
|
|
344
|
+
|
|
345
|
+
### Added
|
|
346
|
+
|
|
347
|
+
- Canonical JSON schema (`schema/canonical.schema.json`) with index
|
|
348
|
+
families, parameters, variable templates, constraint templates,
|
|
349
|
+
first-class objectives, and term-level refs/bindings/role/sign
|
|
350
|
+
semantics.
|
|
351
|
+
- Pydantic v2 model (`lp2graph.core.model`) mirroring the schema.
|
|
352
|
+
- Two-phase validator (JSON Schema + semantic invariants).
|
|
353
|
+
- Three view derivations: `lp2graph.views.schema`, `.hybrid`, `.ground`.
|
|
354
|
+
- Internal typed graph (`lp2graph.core.graph`) — library-agnostic.
|
|
355
|
+
- Structural metrics: `node_counts_by_class`, `edge_density`,
|
|
356
|
+
`constraint_variable_ratio`, `minimal_size`, `model_coherence`,
|
|
357
|
+
`graph_diameter`.
|
|
358
|
+
- Presence flags: `has_big_m`, `has_integer_vars`,
|
|
359
|
+
`has_modulo_offset`, `has_soft_slack`,
|
|
360
|
+
`has_aggregation_operator`.
|
|
361
|
+
- Constraint classification (heuristic, regex-based; ported from the
|
|
362
|
+
source repo with attribution).
|
|
363
|
+
- SVG renderer with the design-context palette and typography.
|
|
364
|
+
- Static interactive viewer (`viewer/index.html`).
|
|
365
|
+
- Export adapters: NetworkX (full), PyG (HeteroData), DGL, LaTeX,
|
|
366
|
+
Pyomo (skeleton; bodies are stubs in v0.1).
|
|
367
|
+
- CLI: `lp2graph validate | view | render | metrics | export`.
|
|
368
|
+
- Initial catalog: 5 constraint-focused and 3 objective-focused
|
|
369
|
+
formulations across LP and MILP families.
|
|
370
|
+
- 36 tests covering schema validation, view derivations, metrics,
|
|
371
|
+
rendering, and exports.
|
|
372
|
+
- CI for Linux + macOS, Python 3.11/3.12/3.13.
|
|
373
|
+
- Docs: data-model, views, metrics, add-a-formulation, design context,
|
|
374
|
+
extraction report; ADRs 0001-0005.
|
|
375
|
+
|
|
376
|
+
### Origin
|
|
377
|
+
|
|
378
|
+
Bootstrapped from
|
|
379
|
+
[`joernmht/raiLPminerExperimentation`](https://github.com/joernmht/raiLPminerExperimentation)
|
|
380
|
+
(MIT). See `docs/extraction-report.md` for the manifest.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: lp2graph
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Typed-graph representation of LP, MIP, and MILP formulations.
|
|
5
5
|
Project-URL: Homepage, https://github.com/joernmht/lp2graph
|
|
6
6
|
Project-URL: Documentation, https://github.com/joernmht/lp2graph#readme
|
|
@@ -27,7 +27,7 @@ Requires-Dist: hdbscan>=0.8; extra == 'all'
|
|
|
27
27
|
Requires-Dist: highspy>=1.7; extra == 'all'
|
|
28
28
|
Requires-Dist: networkx>=3.0; extra == 'all'
|
|
29
29
|
Requires-Dist: nltk>=3.8; extra == 'all'
|
|
30
|
-
Requires-Dist: pulp
|
|
30
|
+
Requires-Dist: pulp<4,>=2.8; extra == 'all'
|
|
31
31
|
Requires-Dist: pyomo>=6.7; extra == 'all'
|
|
32
32
|
Requires-Dist: torch-geometric>=2.4; extra == 'all'
|
|
33
33
|
Requires-Dist: torch>=2.0; extra == 'all'
|
|
@@ -40,11 +40,13 @@ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
|
40
40
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
41
41
|
Requires-Dist: ruff<0.16,>=0.15.12; extra == 'dev'
|
|
42
42
|
Provides-Extra: dgl
|
|
43
|
-
Requires-Dist: dgl>=2.0; extra == 'dgl'
|
|
43
|
+
Requires-Dist: dgl>=2.0; (python_version < '3.13') and extra == 'dgl'
|
|
44
44
|
Requires-Dist: torch>=2.0; extra == 'dgl'
|
|
45
45
|
Provides-Extra: docs
|
|
46
46
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
47
47
|
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
|
|
48
|
+
Provides-Extra: gurobi
|
|
49
|
+
Requires-Dist: gurobipy>=11.0; extra == 'gurobi'
|
|
48
50
|
Provides-Extra: mining
|
|
49
51
|
Requires-Dist: hdbscan>=0.8; extra == 'mining'
|
|
50
52
|
Requires-Dist: networkx>=3.0; extra == 'mining'
|
|
@@ -59,7 +61,7 @@ Provides-Extra: pyomo
|
|
|
59
61
|
Requires-Dist: pyomo>=6.7; extra == 'pyomo'
|
|
60
62
|
Provides-Extra: solver
|
|
61
63
|
Requires-Dist: highspy>=1.7; extra == 'solver'
|
|
62
|
-
Requires-Dist: pulp
|
|
64
|
+
Requires-Dist: pulp<4,>=2.8; extra == 'solver'
|
|
63
65
|
Description-Content-Type: text/markdown
|
|
64
66
|
|
|
65
67
|
# lp2graph
|
|
@@ -169,6 +171,18 @@ A **formulation** is a JSON document validated against
|
|
|
169
171
|
*constraint templates* with quantifiers and bindings, and an optional
|
|
170
172
|
*objective* with first-class terms.
|
|
171
173
|
|
|
174
|
+
`load()`/`loads()` enforce that schema on every document before the typed
|
|
175
|
+
model sees it, so a file that violates the published contract is refused with
|
|
176
|
+
spec-grounded messages naming the source. The schema ships inside the package;
|
|
177
|
+
read it with the accessor rather than by path, since its location differs
|
|
178
|
+
between a wheel and a source checkout:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
import lp2graph
|
|
182
|
+
schema = lp2graph.canonical_schema() # parsed dict, cached
|
|
183
|
+
path = lp2graph.canonical_schema_path() # on-disk location
|
|
184
|
+
```
|
|
185
|
+
|
|
172
186
|
The **schema view** exposes templates and indices — the topology of the
|
|
173
187
|
problem. The **hybrid view** adds per-term offset, sign, and modulo
|
|
174
188
|
labels. The **ground view** materializes every instance at given index
|
|
@@ -199,6 +213,12 @@ are visible.
|
|
|
199
213
|
See [`CONTRIBUTING.md`](CONTRIBUTING.md). Bugs, new formulations, design
|
|
200
214
|
decisions, and good-first-issues all have dedicated templates.
|
|
201
215
|
|
|
216
|
+
## AI disclaimer
|
|
217
|
+
|
|
218
|
+
This repository was developed with substantial assistance from AI coding tools
|
|
219
|
+
(primarily Anthropic's Claude). Code, documentation and results have been
|
|
220
|
+
reviewed by the author, who takes full responsibility for the content.
|
|
221
|
+
|
|
202
222
|
## License
|
|
203
223
|
|
|
204
224
|
Apache 2.0. Portions adapted from
|
|
@@ -105,6 +105,18 @@ A **formulation** is a JSON document validated against
|
|
|
105
105
|
*constraint templates* with quantifiers and bindings, and an optional
|
|
106
106
|
*objective* with first-class terms.
|
|
107
107
|
|
|
108
|
+
`load()`/`loads()` enforce that schema on every document before the typed
|
|
109
|
+
model sees it, so a file that violates the published contract is refused with
|
|
110
|
+
spec-grounded messages naming the source. The schema ships inside the package;
|
|
111
|
+
read it with the accessor rather than by path, since its location differs
|
|
112
|
+
between a wheel and a source checkout:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
import lp2graph
|
|
116
|
+
schema = lp2graph.canonical_schema() # parsed dict, cached
|
|
117
|
+
path = lp2graph.canonical_schema_path() # on-disk location
|
|
118
|
+
```
|
|
119
|
+
|
|
108
120
|
The **schema view** exposes templates and indices — the topology of the
|
|
109
121
|
problem. The **hybrid view** adds per-term offset, sign, and modulo
|
|
110
122
|
labels. The **ground view** materializes every instance at given index
|
|
@@ -135,6 +147,12 @@ are visible.
|
|
|
135
147
|
See [`CONTRIBUTING.md`](CONTRIBUTING.md). Bugs, new formulations, design
|
|
136
148
|
decisions, and good-first-issues all have dedicated templates.
|
|
137
149
|
|
|
150
|
+
## AI disclaimer
|
|
151
|
+
|
|
152
|
+
This repository was developed with substantial assistance from AI coding tools
|
|
153
|
+
(primarily Anthropic's Claude). Code, documentation and results have been
|
|
154
|
+
reviewed by the author, who takes full responsibility for the content.
|
|
155
|
+
|
|
138
156
|
## License
|
|
139
157
|
|
|
140
158
|
Apache 2.0. Portions adapted from
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Decision & Open-Points Register — lp2graph (library)
|
|
2
|
+
|
|
3
|
+
Running log of **decisions**, **open questions** and **notes** for this project.
|
|
4
|
+
Append-only; newest entries at the bottom. Managed by the `transcript` skill via
|
|
5
|
+
`register.py`. The central aggregate of all open points across projects lives at
|
|
6
|
+
[`~/DECISIONS.md`](/home/joern/DECISIONS.md).
|
|
7
|
+
|
|
8
|
+
**Lifecycle:** `open` → `decided` → (if architectural) graduate into a numbered
|
|
9
|
+
**ADR** in `docs/adr/`. ADRs stay immutable; this register is where things churn.
|
|
10
|
+
|
|
11
|
+
Entry fields: *Type* (decision/open-question/note), *Status*, *Date*, *Source*
|
|
12
|
+
(meeting/transcript), *Owner*, *Summary*, optional *Rationale* and *Next*.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
### D-2026-0619-01 · Periodic model fails to solve despite coherence handling
|
|
17
|
+
- **Type:** open-question
|
|
18
|
+
- **Status:** open
|
|
19
|
+
- **Date:** 2026-06-19
|
|
20
|
+
- **Source:** meeting 2026-06-18 (~/meetings/2026-06-18-paper1-status-review.md)
|
|
21
|
+
- **Owner:** JM
|
|
22
|
+
- **Summary:** A periodic formulation does not solve although Paper-1's coherence handling should cover it ('I don't know why this isn't right; look into this'). Investigate in the lp2graph grounding/solver path.
|
|
23
|
+
|