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.
Files changed (222) hide show
  1. lp2graph-0.4.0/CHANGELOG.md +380 -0
  2. {lp2graph-0.3.0 → lp2graph-0.4.0}/PKG-INFO +25 -5
  3. {lp2graph-0.3.0 → lp2graph-0.4.0}/README.md +18 -0
  4. lp2graph-0.4.0/docs/DECISIONS.md +23 -0
  5. lp2graph-0.4.0/docs/STACK.md +196 -0
  6. lp2graph-0.4.0/docs/adr/0008-pulp4-forward-compatible-solver.md +98 -0
  7. lp2graph-0.4.0/docs/adr/0009-ingest-untrusted-input-hardening.md +71 -0
  8. lp2graph-0.4.0/docs/adr/0010-codec-roundtrip-fidelity.md +87 -0
  9. lp2graph-0.4.0/docs/adr/0011-canonical-formulation-as-interchange-pivot.md +72 -0
  10. lp2graph-0.4.0/docs/adr/0012-single-format-registry.md +68 -0
  11. lp2graph-0.4.0/docs/adr/0013-ship-and-enforce-the-canonical-schema.md +140 -0
  12. lp2graph-0.4.0/docs/adr/0014-extras-resolve-on-every-supported-python.md +78 -0
  13. lp2graph-0.4.0/docs/adr/0015-text-io-declares-encoding-and-newline.md +72 -0
  14. lp2graph-0.4.0/docs/adr/0016-one-reading-of-aggregation-loops.md +88 -0
  15. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/configurator.html +18 -0
  16. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/explore.html +11 -8
  17. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/demo/index.html +9 -0
  18. lp2graph-0.4.0/docs/interop.md +81 -0
  19. lp2graph-0.4.0/docs/validation.md +113 -0
  20. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/views.md +21 -4
  21. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_8_pesp.json +1 -2
  22. {lp2graph-0.3.0 → lp2graph-0.4.0}/pyproject.toml +33 -5
  23. {lp2graph-0.3.0 → lp2graph-0.4.0}/schema/canonical.schema.json +8 -8
  24. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/__init__.py +4 -1
  25. lp2graph-0.4.0/src/lp2graph/_optional.py +66 -0
  26. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/cli.py +160 -19
  27. lp2graph-0.4.0/src/lp2graph/codec/latex.py +1298 -0
  28. lp2graph-0.4.0/src/lp2graph/core/loader.py +118 -0
  29. lp2graph-0.4.0/src/lp2graph/core/scope.py +150 -0
  30. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/pyomo_stub.py +7 -0
  31. lp2graph-0.4.0/src/lp2graph/formats.py +109 -0
  32. lp2graph-0.4.0/src/lp2graph/interop/__init__.py +79 -0
  33. lp2graph-0.4.0/src/lp2graph/interop/_grounded.py +498 -0
  34. lp2graph-0.4.0/src/lp2graph/interop/_linexpr.py +114 -0
  35. lp2graph-0.4.0/src/lp2graph/interop/ampl.py +237 -0
  36. lp2graph-0.4.0/src/lp2graph/interop/gams.py +281 -0
  37. lp2graph-0.4.0/src/lp2graph/interop/gurobi.py +202 -0
  38. lp2graph-0.4.0/src/lp2graph/interop/jump.py +289 -0
  39. lp2graph-0.4.0/src/lp2graph/interop/lp_format.py +334 -0
  40. lp2graph-0.4.0/src/lp2graph/interop/mps.py +310 -0
  41. lp2graph-0.4.0/src/lp2graph/interop/pulp_io.py +97 -0
  42. lp2graph-0.4.0/src/lp2graph/interop/pyomo_io.py +217 -0
  43. lp2graph-0.4.0/src/lp2graph/mining/cluster/agglomerative.py +151 -0
  44. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/distance.py +39 -3
  45. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/operator.py +2 -1
  46. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/__init__.py +7 -1
  47. lp2graph-0.4.0/src/lp2graph/mining/ingest/code_importers.py +164 -0
  48. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/dispatch.py +42 -32
  49. lp2graph-0.4.0/src/lp2graph/mining/ingest/latex_normalizer.py +1217 -0
  50. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/versions.py +1 -1
  51. lp2graph-0.4.0/src/lp2graph/schema/__init__.py +62 -0
  52. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/solve/__init__.py +35 -3
  53. lp2graph-0.4.0/src/lp2graph/solve/grounder.py +637 -0
  54. lp2graph-0.4.0/src/lp2graph/solve/solvers.py +99 -0
  55. lp2graph-0.4.0/src/lp2graph/validation/__init__.py +52 -0
  56. lp2graph-0.4.0/src/lp2graph/validation/detect.py +328 -0
  57. lp2graph-0.4.0/src/lp2graph/validation/pipeline.py +717 -0
  58. lp2graph-0.4.0/src/lp2graph/validation/report.py +139 -0
  59. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/ground.py +76 -60
  60. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/hybrid.py +24 -4
  61. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/schema.py +48 -2
  62. lp2graph-0.4.0/tests/interop/_models.py +246 -0
  63. lp2graph-0.4.0/tests/interop/conftest.py +15 -0
  64. lp2graph-0.4.0/tests/interop/test_convert_cli.py +76 -0
  65. lp2graph-0.4.0/tests/interop/test_grounded.py +141 -0
  66. lp2graph-0.4.0/tests/interop/test_object_apis.py +282 -0
  67. lp2graph-0.4.0/tests/interop/test_text_formats.py +212 -0
  68. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_cluster.py +89 -0
  69. lp2graph-0.4.0/tests/mining/test_ingest.py +1015 -0
  70. lp2graph-0.4.0/tests/test_aggregation_scope.py +278 -0
  71. lp2graph-0.4.0/tests/test_cli.py +114 -0
  72. lp2graph-0.4.0/tests/test_formats.py +68 -0
  73. lp2graph-0.4.0/tests/test_interchange_contract.py +234 -0
  74. lp2graph-0.4.0/tests/test_optional_deps.py +150 -0
  75. lp2graph-0.4.0/tests/test_solve.py +325 -0
  76. lp2graph-0.4.0/tests/test_text_io_encoding.py +141 -0
  77. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_views.py +18 -0
  78. lp2graph-0.4.0/tests/validation/test_detect.py +99 -0
  79. lp2graph-0.4.0/tests/validation/test_pipeline.py +210 -0
  80. lp2graph-0.3.0/CHANGELOG.md +0 -137
  81. lp2graph-0.3.0/docs/STACK.md +0 -95
  82. lp2graph-0.3.0/src/lp2graph/codec/latex.py +0 -884
  83. lp2graph-0.3.0/src/lp2graph/core/loader.py +0 -63
  84. lp2graph-0.3.0/src/lp2graph/mining/cluster/agglomerative.py +0 -82
  85. lp2graph-0.3.0/src/lp2graph/mining/ingest/code_importers.py +0 -104
  86. lp2graph-0.3.0/src/lp2graph/mining/ingest/latex_normalizer.py +0 -243
  87. lp2graph-0.3.0/src/lp2graph/solve/grounder.py +0 -405
  88. lp2graph-0.3.0/tests/mining/test_ingest.py +0 -230
  89. lp2graph-0.3.0/tests/test_solve.py +0 -68
  90. {lp2graph-0.3.0 → lp2graph-0.4.0}/.gitignore +0 -0
  91. {lp2graph-0.3.0 → lp2graph-0.4.0}/CITATION.cff +0 -0
  92. {lp2graph-0.3.0 → lp2graph-0.4.0}/LICENSE +0 -0
  93. {lp2graph-0.3.0 → lp2graph-0.4.0}/corpus/validation/README.md +0 -0
  94. {lp2graph-0.3.0 → lp2graph-0.4.0}/corpus/validation/codec_pipeline/README.md +0 -0
  95. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/add-a-formulation.md +0 -0
  96. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0001-build-tool.md +0 -0
  97. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0002-internal-graph.md +0 -0
  98. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0003-license.md +0 -0
  99. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0004-schema-versioning.md +0 -0
  100. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0005-viewer-hosting.md +0 -0
  101. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0006-determinism.md +0 -0
  102. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/adr/0007-optional-deps-lazy-import.md +0 -0
  103. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/catalog.md +0 -0
  104. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/data-model.md +0 -0
  105. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/design-context.md +0 -0
  106. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/extraction-report.md +0 -0
  107. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/index.md +0 -0
  108. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/metrics.md +0 -0
  109. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/mining.md +0 -0
  110. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/text-graph-interface.md +0 -0
  111. {lp2graph-0.3.0 → lp2graph-0.4.0}/docs/v1-acceptance.md +0 -0
  112. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/01-load-and-validate.py +0 -0
  113. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/02-derive-three-views.py +0 -0
  114. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/03-compute-metrics.py +0 -0
  115. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/04-export-to-pyg.py +0 -0
  116. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/05-add-a-new-formulation.md +0 -0
  117. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/ModelOrTools.java +0 -0
  118. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/README.md +0 -0
  119. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/build_pdf.py +0 -0
  120. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/data.json +0 -0
  121. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model.lp +0 -0
  122. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model.mps +0 -0
  123. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ampl.mod +0 -0
  124. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_cvxpy.py +0 -0
  125. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_docplex.py +0 -0
  126. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_gams.gms +0 -0
  127. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_glpk.mod +0 -0
  128. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_gurobi.py +0 -0
  129. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_jump.jl +0 -0
  130. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_matlab.m +0 -0
  131. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_minizinc.mzn +0 -0
  132. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ompr.R +0 -0
  133. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_opl.mod +0 -0
  134. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ortools.cpp +0 -0
  135. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_ortools.py +0 -0
  136. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_pulp.py +0 -0
  137. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_pyomo.py +0 -0
  138. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_python_mip.py +0 -0
  139. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/model_zimpl.zpl +0 -0
  140. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/problem.tex +0 -0
  141. {lp2graph-0.3.0 → lp2graph-0.4.0}/examples/railway-bigm-milp/railway_bigm_milp.pdf +0 -0
  142. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/assignment.json +0 -0
  143. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/lp_1_1_fixed_sequence.json +0 -0
  144. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/lp_1_5_soft_regularity.json +0 -0
  145. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_1_big_m.json +0 -0
  146. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/mip_2_4_time_indexed.json +0 -0
  147. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/constraints/pesp_solvable.json +0 -0
  148. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/multi_obj_lateness_energy.json +0 -0
  149. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/objective_abs_deviation.json +0 -0
  150. {lp2graph-0.3.0 → lp2graph-0.4.0}/formulations/objectives/objective_lex_priority.json +0 -0
  151. {lp2graph-0.3.0 → lp2graph-0.4.0}/legacy/README.md +0 -0
  152. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/codec/__init__.py +0 -0
  153. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/codec/normalize.py +0 -0
  154. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/__init__.py +0 -0
  155. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/graph.py +0 -0
  156. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/model.py +0 -0
  157. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/core/validate.py +0 -0
  158. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/__init__.py +0 -0
  159. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/dgl.py +0 -0
  160. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/latex.py +0 -0
  161. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/networkx_adapter.py +0 -0
  162. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/export/pyg.py +0 -0
  163. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/__init__.py +0 -0
  164. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/classification.py +0 -0
  165. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/flags.py +0 -0
  166. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/result.py +0 -0
  167. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/metrics/structural.py +0 -0
  168. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/__init__.py +0 -0
  169. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/__init__.py +0 -0
  170. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/silhouette.py +0 -0
  171. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/stability.py +0 -0
  172. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/cluster/taxonomy.py +0 -0
  173. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/__init__.py +0 -0
  174. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/dedup.py +0 -0
  175. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/manager.py +0 -0
  176. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/manifest.py +0 -0
  177. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/record.py +0 -0
  178. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/corpusmgr/select.py +0 -0
  179. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/__init__.py +0 -0
  180. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/concept.py +0 -0
  181. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/entity.py +0 -0
  182. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/lemmatize.py +0 -0
  183. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/signature.py +0 -0
  184. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/thesaurus.py +0 -0
  185. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/tokenize.py +0 -0
  186. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/homologize/vectorize.py +0 -0
  187. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/pyomo_importer.py +0 -0
  188. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/ingest/result.py +0 -0
  189. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/isomorphism/__init__.py +0 -0
  190. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/isomorphism/report.py +0 -0
  191. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/__init__.py +0 -0
  192. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/classifier.py +0 -0
  193. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/features.py +0 -0
  194. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/guardrails.py +0 -0
  195. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/loop.py +0 -0
  196. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/rules.py +0 -0
  197. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/store.py +0 -0
  198. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/label/vocab.py +0 -0
  199. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/mining/provenance.py +0 -0
  200. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/nl/__init__.py +0 -0
  201. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/nl/describe.py +0 -0
  202. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/__init__.py +0 -0
  203. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/palette.py +0 -0
  204. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/render/svg.py +0 -0
  205. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/solve/instance.py +0 -0
  206. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/transform/__init__.py +0 -0
  207. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/transform/bigm.py +0 -0
  208. {lp2graph-0.3.0 → lp2graph-0.4.0}/src/lp2graph/views/__init__.py +0 -0
  209. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/conftest.py +0 -0
  210. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_corpus_manager.py +0 -0
  211. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_homologize.py +0 -0
  212. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_isomorphism.py +0 -0
  213. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/mining/test_label.py +0 -0
  214. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_bigm.py +0 -0
  215. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_codec.py +0 -0
  216. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_describe.py +0 -0
  217. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_export.py +0 -0
  218. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_metrics.py +0 -0
  219. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_render.py +0 -0
  220. {lp2graph-0.3.0 → lp2graph-0.4.0}/tests/test_schema.py +0 -0
  221. {lp2graph-0.3.0 → lp2graph-0.4.0}/ui/README.md +0 -0
  222. {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.4
1
+ Metadata-Version: 2.5
2
2
  Name: lp2graph
3
- Version: 0.3.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>=2.8; extra == 'all'
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>=2.8; extra == 'solver'
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
+