parse-lp 2.4.3__tar.gz → 2.5.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 (164) hide show
  1. {parse_lp-2.4.3 → parse_lp-2.5.0}/Cargo.lock +1 -1
  2. parse_lp-2.5.0/PKG-INFO +213 -0
  3. parse_lp-2.5.0/README.md +194 -0
  4. parse_lp-2.5.0/parse_lp/__init__.py +39 -0
  5. parse_lp-2.5.0/parse_lp/parse_lp.pyi +179 -0
  6. {parse_lp-2.4.3 → parse_lp-2.5.0}/pyproject.toml +12 -5
  7. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/Cargo.toml +2 -2
  8. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/MANIFEST.in +3 -2
  9. parse_lp-2.5.0/python/README.md +194 -0
  10. parse_lp-2.5.0/python/parse_lp/__init__.py +39 -0
  11. parse_lp-2.5.0/python/parse_lp/parse_lp.pyi +179 -0
  12. parse_lp-2.5.0/python/parse_lp/py.typed +0 -0
  13. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/python/CHANGELOG.md +7 -0
  14. parse_lp-2.5.0/python/src/lib.rs +256 -0
  15. parse_lp-2.5.0/python/tests/__init__.py +0 -0
  16. parse_lp-2.5.0/python/tests/fixtures/minimize.lp +16 -0
  17. parse_lp-2.5.0/python/tests/fixtures/simple.lp +12 -0
  18. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/tests/test_csv_writer.py +0 -5
  19. parse_lp-2.5.0/python/tests/test_lp_parser.py +219 -0
  20. parse_lp-2.5.0/python/uv.lock +253 -0
  21. parse_lp-2.5.0/rust/resources/afiro.lp +36 -0
  22. parse_lp-2.4.3/PKG-INFO +0 -43
  23. parse_lp-2.4.3/README.md +0 -25
  24. parse_lp-2.4.3/parse_lp/__init__.py +0 -3
  25. parse_lp-2.4.3/python/README.md +0 -25
  26. parse_lp-2.4.3/python/parse_lp/__init__.py +0 -3
  27. parse_lp-2.4.3/python/src/lib.rs +0 -51
  28. parse_lp-2.4.3/python/uv.lock +0 -232
  29. {parse_lp-2.4.3 → parse_lp-2.5.0}/Cargo.toml +0 -0
  30. {parse_lp-2.4.3 → parse_lp-2.5.0}/LICENSE-APACHE +0 -0
  31. /parse_lp-2.4.3/python/tests/__init__.py → /parse_lp-2.5.0/parse_lp/py.typed +0 -0
  32. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/.cargo/config.toml +0 -0
  33. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/CHANGELOG.md +0 -0
  34. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/LICENSE-APACHE +0 -0
  35. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/Makefile +0 -0
  36. {parse_lp-2.4.3 → parse_lp-2.5.0}/python/resources/afiro.lp +0 -0
  37. {parse_lp-2.4.3/rust/resources → parse_lp-2.5.0/python/tests/fixtures}/afiro.lp +0 -0
  38. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/CHANGELOG.md +0 -0
  39. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/Cargo.toml +0 -0
  40. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/README.md +0 -0
  41. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/benches/lp_parsing.rs +0 -0
  42. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/benches/parsing_micro.rs +0 -0
  43. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/1obj_1cons_all_variables_with_bounds.lp +0 -0
  44. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/2obj_2cons_all_variable_types.lp +0 -0
  45. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/2obj_2cons_only_binary_vars.lp +0 -0
  46. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/3obj_2cons.lp +0 -0
  47. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/AmericanSteelProblem.lp +0 -0
  48. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/BeerDistributionProblem.lp +0 -0
  49. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/ComputerPlantProblem.lp +0 -0
  50. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/WhiskasModel2.lp +0 -0
  51. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/afiro_ext.lp +0 -0
  52. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/blank_lines.lp +0 -0
  53. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/boeing1.lp +0 -0
  54. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/boeing2.lp +0 -0
  55. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/carbon.png +0 -0
  56. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/complex_names.lp +0 -0
  57. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/corrupt.lp +0 -0
  58. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/cplex.lp +0 -0
  59. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/diet.lp +0 -0
  60. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/empty_bounds.lp +0 -0
  61. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/fit1d.lp +0 -0
  62. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/fit2d.lp +0 -0
  63. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/infile_comments.lp +0 -0
  64. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/infile_comments2.lp +0 -0
  65. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/invalid.lp +0 -0
  66. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/kb2.lp +0 -0
  67. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/limbo.lp +0 -0
  68. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/lol.lp +0 -0
  69. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/milo1.lp +0 -0
  70. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/missing_signs.lp +0 -0
  71. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/model.lp +0 -0
  72. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/model2.lp +0 -0
  73. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/mosek.lp +0 -0
  74. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/mosek_bounds.lp +0 -0
  75. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/no_end_section.lp +0 -0
  76. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/optional_labels.lp +0 -0
  77. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output.lp +0 -0
  78. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output2_1.lp +0 -0
  79. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output2_2.lp +0 -0
  80. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output2_3.lp +0 -0
  81. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output2_4.lp +0 -0
  82. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/output_cplex_2.lp +0 -0
  83. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/pulp.lp +0 -0
  84. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/pulp2.lp +0 -0
  85. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/sc50a.lp +0 -0
  86. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/scientific_notation.lp +0 -0
  87. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/scientific_notation_2.lp +0 -0
  88. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/semi_continuous.lp +0 -0
  89. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/sos.lp +0 -0
  90. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/sudoku.lp +0 -0
  91. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/test.lp +0 -0
  92. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/test2.lp +0 -0
  93. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/test_copy.lp +0 -0
  94. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/resources/wbm.lp +0 -0
  95. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/rust/CHANGELOG.md +0 -0
  96. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/bin/main.rs +0 -0
  97. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/builder.rs +0 -0
  98. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/context.rs +0 -0
  99. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/csv.rs +0 -0
  100. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/error.rs +0 -0
  101. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/lib.rs +0 -0
  102. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/model.rs +0 -0
  103. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parser.rs +0 -0
  104. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/coefficient.rs +0 -0
  105. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/constraint.rs +0 -0
  106. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/mod.rs +0 -0
  107. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/number.rs +0 -0
  108. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/objective.rs +0 -0
  109. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/parser_traits.rs +0 -0
  110. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/problem_name.rs +0 -0
  111. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/sense.rs +0 -0
  112. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/sos_constraint.rs +0 -0
  113. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/parsers/variable.rs +0 -0
  114. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/perf.rs +0 -0
  115. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/problem.rs +0 -0
  116. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/snapshots/lp_parser_rs__problem__test__minified_example.snap +0 -0
  117. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/snapshots/lp_parser_rs__problem__test__small_input.snap +0 -0
  118. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/src/validation.rs +0 -0
  119. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__afiro.snap +0 -0
  120. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__afiro_ext.snap +0 -0
  121. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__american_steel_problem.snap +0 -0
  122. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__beer_distribution_problem.snap +0 -0
  123. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__blank_lines.snap +0 -0
  124. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__boeing1.snap +0 -0
  125. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__boeing2.snap +0 -0
  126. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__complex_names.snap +0 -0
  127. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__computer_plant_problem.snap +0 -0
  128. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__cplex.snap +0 -0
  129. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__diet.snap +0 -0
  130. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__empty_bounds.snap +0 -0
  131. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__fit1d.snap +0 -0
  132. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__infile_comments.snap +0 -0
  133. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__infile_comments2.snap +0 -0
  134. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__kb2.snap +0 -0
  135. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__limbo.snap +0 -0
  136. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__lol.snap +0 -0
  137. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__milo1.snap +0 -0
  138. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__missing_signs.snap +0 -0
  139. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__model.snap +0 -0
  140. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__model2.snap +0 -0
  141. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__mosek.snap +0 -0
  142. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__mosek_bounds.snap +0 -0
  143. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__nom_fit2d.snap +0 -0
  144. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__obj3_2cons.snap +0 -0
  145. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__obj_1cons_all_variables_with_bounds.snap +0 -0
  146. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__obj_2cons_all_variable_types.snap +0 -0
  147. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__obj_2cons_only_binary_vars.snap +0 -0
  148. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__output.snap +0 -0
  149. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__output2_1.snap +0 -0
  150. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__output2_2.snap +0 -0
  151. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__output2_3.snap +0 -0
  152. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__output2_4.snap +0 -0
  153. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__pulp.snap +0 -0
  154. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__pulp2.snap +0 -0
  155. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__sc50a.snap +0 -0
  156. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__scientific_notation.snap +0 -0
  157. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__scientific_notation_2.snap +0 -0
  158. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__sos.snap +0 -0
  159. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__sudoku.snap +0 -0
  160. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__test.snap +0 -0
  161. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__test2.snap +0 -0
  162. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__wbm.snap +0 -0
  163. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/snapshots/test_nom_from_file__whiskas_model_2.snap +0 -0
  164. {parse_lp-2.4.3 → parse_lp-2.5.0}/rust/tests/test_nom_from_file.rs +0 -0
@@ -432,7 +432,7 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
432
432
 
433
433
  [[package]]
434
434
  name = "parse_lp"
435
- version = "2.4.3"
435
+ version = "2.5.0"
436
436
  dependencies = [
437
437
  "lp_parser_rs",
438
438
  "pyo3",
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.4
2
+ Name: parse_lp
3
+ Version: 2.5.0
4
+ Classifier: License :: OSI Approved :: Apache Software License
5
+ Classifier: Programming Language :: Python :: 3.9
6
+ Classifier: Programming Language :: Python :: 3.10
7
+ Classifier: Programming Language :: Python :: 3.11
8
+ Classifier: Programming Language :: Python :: 3.12
9
+ License-File: LICENSE-APACHE
10
+ Summary: A LP file format parser for Python, powered by Rust
11
+ Keywords: linear-programming,optimization,parser,integer-programming,math
12
+ Author: Dan Dixey, Dan Dixey <dan@functorml.co.uk>
13
+ Author-email: Dan Dixey, Dan Dixey <dan@functorml.co.uk>
14
+ License: MIT OR Apache-2.0
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
17
+ Project-URL: Source Code, https://github.com/dandxy89/lp_parser_rs
18
+
19
+ # parse_lp
20
+
21
+ A LP file format parser for Python, powered by Rust.
22
+
23
+ ## Features
24
+
25
+ - **Complete LP Support**: Handles all standard LP file format features
26
+ - **Easy Data Access**: Direct access to problem components (variables, constraints, objectives)
27
+ - **CSV Export**: Export parsed data to CSV files for further analysis
28
+ - **Problem Comparison**: Compare two LP problems to identify differences
29
+ - **Type Safety**: Full type hints for better IDE support and development experience
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install parse_lp
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```python
40
+ from parse_lp import LpParser
41
+
42
+ # Parse an LP file
43
+ parser = LpParser("path/to/problem.lp")
44
+ parser.parse()
45
+
46
+ # Access problem information
47
+ print(f"Problem: {parser.name}")
48
+ print(f"Sense: {parser.sense}")
49
+ print(f"Variables: {parser.variable_count()}")
50
+ print(f"Constraints: {parser.constraint_count()}")
51
+
52
+ # Export to CSV files
53
+ parser.to_csv("output_directory/")
54
+ ```
55
+
56
+ ## Usage Examples
57
+
58
+ ### Basic Parsing and Information
59
+
60
+ ```python
61
+ from parse_lp import LpParser
62
+
63
+ parser = LpParser("optimization_problem.lp")
64
+ parser.parse()
65
+
66
+ # Get problem overview
67
+ print(f"Problem Name: {parser.name}")
68
+ print(f"Optimization Sense: {parser.sense}")
69
+ print(f"Variables: {parser.variable_count()}")
70
+ print(f"Constraints: {parser.constraint_count()}")
71
+ print(f"Objectives: {parser.objective_count()}")
72
+ ```
73
+
74
+ ### Accessing Problem Data
75
+
76
+ ```python
77
+ # Access objectives
78
+ for i, objective in enumerate(parser.objectives):
79
+ print(f"Objective {i+1}: {objective['name']}")
80
+ for coef in objective['coefficients']:
81
+ print(f" {coef['name']}: {coef['value']}")
82
+
83
+ # Access variables
84
+ for var_name, var_info in parser.variables.items():
85
+ print(f"Variable {var_name}:")
86
+ print(f" Type: {var_info['var_type']}")
87
+ if 'bounds' in var_info:
88
+ bounds = var_info['bounds']
89
+ if 'lower' in bounds:
90
+ print(f" Lower bound: {bounds['lower']}")
91
+ if 'upper' in bounds:
92
+ print(f" Upper bound: {bounds['upper']}")
93
+
94
+ # Access constraints
95
+ for constraint in parser.constraints:
96
+ print(f"Constraint {constraint['name']}:")
97
+ print(f" Type: {constraint['type']}")
98
+ if constraint['type'] == 'standard':
99
+ print(f" Sense: {constraint['sense']}")
100
+ print(f" RHS: {constraint['rhs']}")
101
+ print(f" Coefficients: {len(constraint['coefficients'])}")
102
+ ```
103
+
104
+ ### CSV Export
105
+
106
+ ```python
107
+ import os
108
+
109
+ # Create output directory
110
+ os.makedirs("output", exist_ok=True)
111
+
112
+ # Export to CSV files
113
+ parser.to_csv("output/")
114
+
115
+ # Files created:
116
+ # - output/variables.csv
117
+ # - output/constraints.csv
118
+ # - output/objectives.csv
119
+ ```
120
+
121
+ ### Problem Comparison
122
+
123
+ ```python
124
+ # Parse two different versions of a problem
125
+ parser1 = LpParser("problem_v1.lp")
126
+ parser1.parse()
127
+
128
+ parser2 = LpParser("problem_v2.lp")
129
+ parser2.parse()
130
+
131
+ # Compare the problems
132
+ diff = parser1.compare(parser2)
133
+
134
+ print("Comparison Results:")
135
+ print(f"Name changed: {diff['name_changed']}")
136
+ print(f"Sense changed: {diff['sense_changed']}")
137
+ print(f"Variable count difference: {diff['variable_count_diff']}")
138
+ print(f"Added variables: {diff['added_variables']}")
139
+ print(f"Removed variables: {diff['removed_variables']}")
140
+ print(f"Modified variables: {diff['modified_variables']}")
141
+ print(f"Added constraints: {diff['added_constraints']}")
142
+ print(f"Removed constraints: {diff['removed_constraints']}")
143
+ ```
144
+
145
+ ### Objectives Structure
146
+
147
+ ```python
148
+ [
149
+ {
150
+ "name": "objective_name",
151
+ "coefficients": [
152
+ {"name": "variable_name", "value": 1.5},
153
+ {"name": "another_var", "value": -2.0}
154
+ ]
155
+ }
156
+ ]
157
+ ```
158
+
159
+ ### Variables Structure
160
+
161
+ ```python
162
+ {
163
+ "variable_name": {
164
+ "name": "variable_name",
165
+ "var_type": "Continuous", # or "Binary", "Integer", etc.
166
+ "bounds": { # Optional
167
+ "lower": 0.0, # Optional
168
+ "upper": 100.0 # Optional
169
+ }
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Constraints Structure
175
+
176
+ ```python
177
+ [
178
+ {
179
+ "name": "constraint_name",
180
+ "type": "standard", # or "sos"
181
+ "sense": "LessOrEqual", # "Equal", "GreaterOrEqual"
182
+ "rhs": 10.0,
183
+ "coefficients": [
184
+ {"name": "variable_name", "value": 2.0}
185
+ ]
186
+ },
187
+ {
188
+ "name": "sos_constraint",
189
+ "type": "sos",
190
+ "sos_type": "SOS1", # or "SOS2"
191
+ "variables": ["var1", "var2", "var3"]
192
+ }
193
+ ]
194
+ ```
195
+
196
+ ## Supported LP Format Features
197
+
198
+ - Multiple objective functions
199
+ - Standard constraints (≤, =, ≥)
200
+ - Variable bounds
201
+ - Variable types (continuous, binary, integer)
202
+ - SOS (Special Ordered Sets) constraints
203
+ - Problem names and comments
204
+ - Scientific notation in coefficients
205
+
206
+ ## License
207
+
208
+ Licensed under either of Apache License, Version 2.0 or MIT license at your option.
209
+
210
+ ## Contributing
211
+
212
+ Issues and pull requests are welcome at: <https://github.com/dandxy89/lp_parser_rs>
213
+
@@ -0,0 +1,194 @@
1
+ # parse_lp
2
+
3
+ A LP file format parser for Python, powered by Rust.
4
+
5
+ ## Features
6
+
7
+ - **Complete LP Support**: Handles all standard LP file format features
8
+ - **Easy Data Access**: Direct access to problem components (variables, constraints, objectives)
9
+ - **CSV Export**: Export parsed data to CSV files for further analysis
10
+ - **Problem Comparison**: Compare two LP problems to identify differences
11
+ - **Type Safety**: Full type hints for better IDE support and development experience
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pip install parse_lp
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```python
22
+ from parse_lp import LpParser
23
+
24
+ # Parse an LP file
25
+ parser = LpParser("path/to/problem.lp")
26
+ parser.parse()
27
+
28
+ # Access problem information
29
+ print(f"Problem: {parser.name}")
30
+ print(f"Sense: {parser.sense}")
31
+ print(f"Variables: {parser.variable_count()}")
32
+ print(f"Constraints: {parser.constraint_count()}")
33
+
34
+ # Export to CSV files
35
+ parser.to_csv("output_directory/")
36
+ ```
37
+
38
+ ## Usage Examples
39
+
40
+ ### Basic Parsing and Information
41
+
42
+ ```python
43
+ from parse_lp import LpParser
44
+
45
+ parser = LpParser("optimization_problem.lp")
46
+ parser.parse()
47
+
48
+ # Get problem overview
49
+ print(f"Problem Name: {parser.name}")
50
+ print(f"Optimization Sense: {parser.sense}")
51
+ print(f"Variables: {parser.variable_count()}")
52
+ print(f"Constraints: {parser.constraint_count()}")
53
+ print(f"Objectives: {parser.objective_count()}")
54
+ ```
55
+
56
+ ### Accessing Problem Data
57
+
58
+ ```python
59
+ # Access objectives
60
+ for i, objective in enumerate(parser.objectives):
61
+ print(f"Objective {i+1}: {objective['name']}")
62
+ for coef in objective['coefficients']:
63
+ print(f" {coef['name']}: {coef['value']}")
64
+
65
+ # Access variables
66
+ for var_name, var_info in parser.variables.items():
67
+ print(f"Variable {var_name}:")
68
+ print(f" Type: {var_info['var_type']}")
69
+ if 'bounds' in var_info:
70
+ bounds = var_info['bounds']
71
+ if 'lower' in bounds:
72
+ print(f" Lower bound: {bounds['lower']}")
73
+ if 'upper' in bounds:
74
+ print(f" Upper bound: {bounds['upper']}")
75
+
76
+ # Access constraints
77
+ for constraint in parser.constraints:
78
+ print(f"Constraint {constraint['name']}:")
79
+ print(f" Type: {constraint['type']}")
80
+ if constraint['type'] == 'standard':
81
+ print(f" Sense: {constraint['sense']}")
82
+ print(f" RHS: {constraint['rhs']}")
83
+ print(f" Coefficients: {len(constraint['coefficients'])}")
84
+ ```
85
+
86
+ ### CSV Export
87
+
88
+ ```python
89
+ import os
90
+
91
+ # Create output directory
92
+ os.makedirs("output", exist_ok=True)
93
+
94
+ # Export to CSV files
95
+ parser.to_csv("output/")
96
+
97
+ # Files created:
98
+ # - output/variables.csv
99
+ # - output/constraints.csv
100
+ # - output/objectives.csv
101
+ ```
102
+
103
+ ### Problem Comparison
104
+
105
+ ```python
106
+ # Parse two different versions of a problem
107
+ parser1 = LpParser("problem_v1.lp")
108
+ parser1.parse()
109
+
110
+ parser2 = LpParser("problem_v2.lp")
111
+ parser2.parse()
112
+
113
+ # Compare the problems
114
+ diff = parser1.compare(parser2)
115
+
116
+ print("Comparison Results:")
117
+ print(f"Name changed: {diff['name_changed']}")
118
+ print(f"Sense changed: {diff['sense_changed']}")
119
+ print(f"Variable count difference: {diff['variable_count_diff']}")
120
+ print(f"Added variables: {diff['added_variables']}")
121
+ print(f"Removed variables: {diff['removed_variables']}")
122
+ print(f"Modified variables: {diff['modified_variables']}")
123
+ print(f"Added constraints: {diff['added_constraints']}")
124
+ print(f"Removed constraints: {diff['removed_constraints']}")
125
+ ```
126
+
127
+ ### Objectives Structure
128
+
129
+ ```python
130
+ [
131
+ {
132
+ "name": "objective_name",
133
+ "coefficients": [
134
+ {"name": "variable_name", "value": 1.5},
135
+ {"name": "another_var", "value": -2.0}
136
+ ]
137
+ }
138
+ ]
139
+ ```
140
+
141
+ ### Variables Structure
142
+
143
+ ```python
144
+ {
145
+ "variable_name": {
146
+ "name": "variable_name",
147
+ "var_type": "Continuous", # or "Binary", "Integer", etc.
148
+ "bounds": { # Optional
149
+ "lower": 0.0, # Optional
150
+ "upper": 100.0 # Optional
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ ### Constraints Structure
157
+
158
+ ```python
159
+ [
160
+ {
161
+ "name": "constraint_name",
162
+ "type": "standard", # or "sos"
163
+ "sense": "LessOrEqual", # "Equal", "GreaterOrEqual"
164
+ "rhs": 10.0,
165
+ "coefficients": [
166
+ {"name": "variable_name", "value": 2.0}
167
+ ]
168
+ },
169
+ {
170
+ "name": "sos_constraint",
171
+ "type": "sos",
172
+ "sos_type": "SOS1", # or "SOS2"
173
+ "variables": ["var1", "var2", "var3"]
174
+ }
175
+ ]
176
+ ```
177
+
178
+ ## Supported LP Format Features
179
+
180
+ - Multiple objective functions
181
+ - Standard constraints (≤, =, ≥)
182
+ - Variable bounds
183
+ - Variable types (continuous, binary, integer)
184
+ - SOS (Special Ordered Sets) constraints
185
+ - Problem names and comments
186
+ - Scientific notation in coefficients
187
+
188
+ ## License
189
+
190
+ Licensed under either of Apache License, Version 2.0 or MIT license at your option.
191
+
192
+ ## Contributing
193
+
194
+ Issues and pull requests are welcome at: <https://github.com/dandxy89/lp_parser_rs>
@@ -0,0 +1,39 @@
1
+ """parse_lp - A fast LP file format parser for Python, powered by Rust.
2
+
3
+ This package provides a high-performance parser for Linear Programming (LP) files,
4
+ leveraging Rust for speed and Python for ease of use.
5
+
6
+ Key Features:
7
+ - Fast parsing of LP files of various formats
8
+ - Export parsed data to CSV files
9
+ - Access to problem components (variables, constraints, objectives)
10
+ - Compare two LP problems to find differences
11
+ - Type hints for better IDE support
12
+
13
+ Example Usage:
14
+ >>> from parse_lp import LpParser
15
+ >>>
16
+ >>> # Parse an LP file
17
+ >>> parser = LpParser("optimization_problem.lp")
18
+ >>> parser.parse()
19
+ >>>
20
+ >>> # Access problem information
21
+ >>> print(f"Problem name: {parser.name}")
22
+ >>> print(f"Variables: {parser.variable_count()}")
23
+ >>> print(f"Constraints: {parser.constraint_count()}")
24
+ >>>
25
+ >>> # Export to CSV files
26
+ >>> parser.to_csv("output/")
27
+ >>>
28
+ >>> # Compare two problems
29
+ >>> parser2 = LpParser("modified_problem.lp")
30
+ >>> parser2.parse()
31
+ >>> diff = parser.compare(parser2)
32
+ >>> print(f"Added variables: {diff['added_variables']}")
33
+
34
+ """
35
+
36
+ from .parse_lp import LpParser
37
+
38
+ __version__ = "2.4.2"
39
+ __all__ = ["LpParser"]
@@ -0,0 +1,179 @@
1
+ """Type stubs for parse_lp module."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ class LpParser:
6
+ """A fast LP file format parser.
7
+
8
+ This class provides functionality to parse Linear Programming (LP) files
9
+ and access the parsed data or export it to CSV format.
10
+
11
+ Attributes:
12
+ lp_file: Path to the LP file being parsed.
13
+ name: Name of the LP problem (after parsing).
14
+ sense: Optimization sense ('maximize' or 'minimize').
15
+ objectives: List of objective functions with coefficients.
16
+ constraints: List of constraints with their properties.
17
+ variables: Dictionary of variables with their properties.
18
+
19
+ Example:
20
+ >>> from parse_lp import LpParser
21
+ >>> parser = LpParser("path/to/problem.lp")
22
+ >>> parser.parse() # Parse the file
23
+ >>> print(f"Problem: {parser.name}")
24
+ >>> print(f"Variables: {parser.variable_count()}")
25
+ >>> parser.to_csv("output/directory/")
26
+ """
27
+
28
+ def __init__(self, lp_file: str) -> None:
29
+ """Initialize the LP parser with a file path.
30
+
31
+ Args:
32
+ lp_file: Path to the LP file to parse.
33
+
34
+ Raises:
35
+ FileExistsError: If the specified file does not exist.
36
+ """
37
+ ...
38
+
39
+ @property
40
+ def lp_file(self) -> str:
41
+ """Get the path to the LP file.
42
+
43
+ Returns:
44
+ The path to the LP file being parsed.
45
+ """
46
+ ...
47
+
48
+ @property
49
+ def name(self) -> Optional[str]:
50
+ """Get the name of the LP problem.
51
+
52
+ Returns:
53
+ The problem name if available and parsed, None otherwise.
54
+ """
55
+ ...
56
+
57
+ @property
58
+ def sense(self) -> Optional[str]:
59
+ """Get the optimization sense of the problem.
60
+
61
+ Returns:
62
+ 'maximize' or 'minimize' if parsed, None otherwise.
63
+ """
64
+ ...
65
+
66
+ @property
67
+ def objectives(self) -> List[Dict[str, Any]]:
68
+ """Get the list of objective functions.
69
+
70
+ Returns:
71
+ List of dictionaries containing objective information:
72
+ - name: Name of the objective
73
+ - coefficients: List of coefficient dictionaries with 'name' and 'value'
74
+ """
75
+ ...
76
+
77
+ @property
78
+ def constraints(self) -> List[Dict[str, Any]]:
79
+ """Get the list of constraints.
80
+
81
+ Returns:
82
+ List of dictionaries containing constraint information:
83
+ - name: Name of the constraint
84
+ - type: 'standard' or 'sos'
85
+ - For standard constraints: coefficients, sense, rhs
86
+ - For SOS constraints: sos_type, variables
87
+ """
88
+ ...
89
+
90
+ @property
91
+ def variables(self) -> Dict[str, Dict[str, Any]]:
92
+ """Get the dictionary of variables.
93
+
94
+ Returns:
95
+ Dictionary mapping variable names to their properties:
96
+ - name: Variable name
97
+ - var_type: Type of variable (e.g., 'Continuous', 'Binary', 'Integer')
98
+ - bounds: Optional dictionary with 'lower' and/or 'upper' bounds
99
+ """
100
+ ...
101
+
102
+ def parse(self) -> None:
103
+ """Parse the LP file.
104
+
105
+ This method reads and parses the LP file, storing the problem
106
+ data internally for later access.
107
+
108
+ Raises:
109
+ RuntimeError: If the LP file cannot be read or parsed.
110
+ """
111
+ ...
112
+
113
+ def to_csv(self, base_directory: str) -> None:
114
+ """Export the parsed LP problem to CSV files.
115
+
116
+ This method parses the LP file (if not already parsed) and exports
117
+ the problem data (variables, constraints, and objectives) to separate
118
+ CSV files in the specified directory.
119
+
120
+ Args:
121
+ base_directory: Path to the directory where CSV files will be saved.
122
+ The directory must exist.
123
+
124
+ Raises:
125
+ NotADirectoryError: If base_directory is not a valid directory.
126
+ RuntimeError: If the LP file cannot be read or parsed, or if
127
+ CSV files cannot be written.
128
+ """
129
+ ...
130
+
131
+ def variable_count(self) -> int:
132
+ """Get the number of variables in the problem.
133
+
134
+ Returns:
135
+ Number of variables, or 0 if not parsed.
136
+ """
137
+ ...
138
+
139
+ def constraint_count(self) -> int:
140
+ """Get the number of constraints in the problem.
141
+
142
+ Returns:
143
+ Number of constraints, or 0 if not parsed.
144
+ """
145
+ ...
146
+
147
+ def objective_count(self) -> int:
148
+ """Get the number of objective functions in the problem.
149
+
150
+ Returns:
151
+ Number of objectives, or 0 if not parsed.
152
+ """
153
+ ...
154
+
155
+ def compare(self, other: "LpParser") -> Dict[str, Any]:
156
+ """Compare this LP problem with another.
157
+
158
+ Both parsers must be parsed before comparison.
159
+
160
+ Args:
161
+ other: Another LpParser instance to compare with.
162
+
163
+ Returns:
164
+ Dictionary containing comparison results:
165
+ - name_changed: Whether problem names differ
166
+ - sense_changed: Whether optimization senses differ
167
+ - variable_count_diff: Difference in variable counts
168
+ - constraint_count_diff: Difference in constraint counts
169
+ - objective_count_diff: Difference in objective counts
170
+ - added_variables: Variables in 'other' but not in 'self'
171
+ - removed_variables: Variables in 'self' but not in 'other'
172
+ - modified_variables: Variables that exist in both but differ
173
+ - added_constraints: Constraints in 'other' but not in 'self'
174
+ - removed_constraints: Constraints in 'self' but not in 'other'
175
+
176
+ Raises:
177
+ RuntimeError: If either parser hasn't been parsed yet.
178
+ """
179
+ ...
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "parse_lp"
3
- description = "A fast LP file format parser for Python, powered by Rust"
3
+ description = "A LP file format parser for Python, powered by Rust"
4
4
  license = { file = "LICENSE-APACHE" }
5
5
  requires-python = ">=3.9"
6
6
  keywords = [
@@ -29,10 +29,10 @@ manifest-path = "python/Cargo.toml"
29
29
 
30
30
  [dependency-groups]
31
31
  dev = [
32
- "mypy>=1.14.1",
33
- "pip>=25.0",
34
- "pytest>=8.3.4",
35
- "ruff>=0.9.3",
32
+ "mypy>=1.17.0",
33
+ "pip>=25.1.1",
34
+ "pytest>=8.4.1",
35
+ "ruff>=0.12.5",
36
36
  "wheel>=0.45.1",
37
37
  ]
38
38
 
@@ -43,6 +43,13 @@ ignore = ["E501"]
43
43
  [tool.ruff.lint.isort]
44
44
  known-first-party = ["parse_lp"]
45
45
 
46
+ [tool.pytest.ini_options]
47
+ testpaths = ["tests"]
48
+ python_files = "test_*.py"
49
+ python_classes = "Test*"
50
+ python_functions = "test_*"
51
+ addopts = "-v --color=yes"
52
+
46
53
  [tool.coverage.run]
47
54
  branch = true
48
55
  source = ["parse_lp"]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "parse_lp"
3
- version = "2.4.3"
3
+ version = "2.5.0"
4
4
  edition = "2021"
5
5
  authors = ["Dan Dixey", "Dan Dixey <dan@functorml.co.uk>"]
6
6
  rust-version = "1.70.0"
@@ -21,7 +21,7 @@ name = "parse_lp"
21
21
  crate-type = ["cdylib"]
22
22
 
23
23
  [dependencies]
24
- lp_parser_rs = { path = "../rust", features = ["csv"] }
24
+ lp_parser_rs = { path = "../rust", features = ["csv", "diff", "serde"] }
25
25
  pyo3 = { version = "0.25.1", features = [
26
26
  "extension-module",
27
27
  "abi3",
@@ -2,5 +2,6 @@ include LICENSE-APACHE
2
2
  include Cargo.toml
3
3
  include pyproject.toml
4
4
  recursive-include src *
5
- recursive-include parse_lp *.py
6
- exclude ../README.md
5
+ recursive-include parse_lp *.py *.pyi
6
+ include parse_lp/py.typed
7
+ exclude ../README.md