eqc-models 0.11.1__tar.gz → 0.13.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 (190) hide show
  1. eqc_models-0.13.0/.gitignore +6 -0
  2. {eqc_models-0.11.1/eqc_models.egg-info → eqc_models-0.13.0}/PKG-INFO +7 -9
  3. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/base.py +11 -0
  4. eqc_models-0.13.0/eqc_models/base/binaries.py +33 -0
  5. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/constraints.py +39 -0
  6. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/polynomial.py +21 -2
  7. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/quadratic.py +2 -2
  8. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/results.py +81 -1
  9. eqc_models-0.13.0/eqc_models/graph/__init__.py +11 -0
  10. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/graph/base.py +8 -4
  11. eqc_models-0.13.0/eqc_models/graph/rcshortestpath.py +81 -0
  12. eqc_models-0.13.0/eqc_models/graph/shortestpath.py +190 -0
  13. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/classifierbase.py +30 -5
  14. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/classifierqboost.py +14 -1
  15. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/classifierqsvm.py +28 -0
  16. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/clustering.py +5 -5
  17. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/clusteringbase.py +1 -2
  18. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/decomposition.py +0 -1
  19. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/regressorbase.py +0 -1
  20. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/process/base.py +6 -1
  21. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/process/mpc.py +1 -1
  22. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/solvers/__init__.py +10 -6
  23. eqc_models-0.13.0/eqc_models/solvers/eqcdirect.py +76 -0
  24. eqc_models-0.13.0/eqc_models/solvers/mip.py +115 -0
  25. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/solvers/qciclient.py +11 -22
  26. eqc_models-0.13.0/eqc_models/solvers/responselog.py +47 -0
  27. {eqc_models-0.11.1 → eqc_models-0.13.0/eqc_models.egg-info}/PKG-INFO +7 -9
  28. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models.egg-info/SOURCES.txt +61 -2
  29. eqc_models-0.13.0/eqc_models.egg-info/requires.txt +12 -0
  30. {eqc_models-0.11.1 → eqc_models-0.13.0}/pyproject.toml +14 -12
  31. eqc_models-0.13.0/scripts/binary_w_continuous_solver_example.py +46 -0
  32. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/integer_job_example.py +1 -1
  33. eqc_models-0.13.0/scripts/mip_example.py +47 -0
  34. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/port_opt_dirac3.py +0 -1
  35. eqc_models-0.13.0/scripts/qboost_iris_dirac3_direct.py +103 -0
  36. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qplib_benchmark_config.py +1 -1
  37. eqc_models-0.13.0/scripts/test_shortestpath.py +65 -0
  38. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testpolynomialmodel.py +6 -4
  39. eqc_models-0.13.0/test/testshortestpath.py +55 -0
  40. eqc_models-0.13.0/test_suite/README.txt +22 -0
  41. eqc_models-0.13.0/test_suite/run_tests.py +293 -0
  42. eqc_models-0.13.0/test_suite/test_cases/c6h6_graph_clustering/c6h6_graph_clustering.py +60 -0
  43. eqc_models-0.13.0/test_suite/test_cases/clustering/clustering.py +49 -0
  44. eqc_models-0.13.0/test_suite/test_cases/clustering/data/X.npy +0 -0
  45. eqc_models-0.13.0/test_suite/test_cases/cvqboost_iris/cvqboost_iris.py +63 -0
  46. eqc_models-0.13.0/test_suite/test_cases/cvqboost_iris/data/X_test.npy +0 -0
  47. eqc_models-0.13.0/test_suite/test_cases/cvqboost_iris/data/X_train.npy +0 -0
  48. eqc_models-0.13.0/test_suite/test_cases/cvqboost_iris/data/y_test.npy +0 -0
  49. eqc_models-0.13.0/test_suite/test_cases/cvqboost_iris/data/y_train.npy +0 -0
  50. eqc_models-0.13.0/test_suite/test_cases/karate_graph_clustering/karate_graph_clustering.py +44 -0
  51. eqc_models-0.13.0/test_suite/test_cases/pca_iris/pca_iris.py +59 -0
  52. eqc_models-0.13.0/test_suite/test_cases/protein_design_1MJC/data/C_1MJC.npy +0 -0
  53. eqc_models-0.13.0/test_suite/test_cases/protein_design_1MJC/data/J_1MJC.npy +0 -0
  54. eqc_models-0.13.0/test_suite/test_cases/protein_design_1MJC/protein_design_1MJC.py +52 -0
  55. eqc_models-0.13.0/test_suite/test_cases/protein_design_1NXB/data/C_1NXB.npy +0 -0
  56. eqc_models-0.13.0/test_suite/test_cases/protein_design_1NXB/data/J_1NXB.npy +0 -0
  57. eqc_models-0.13.0/test_suite/test_cases/protein_design_1NXB/protein_design_1NXB.py +52 -0
  58. eqc_models-0.13.0/test_suite/test_cases/protein_design_1POH/data/C_1POH.npy +0 -0
  59. eqc_models-0.13.0/test_suite/test_cases/protein_design_1POH/data/J_1POH.npy +0 -0
  60. eqc_models-0.13.0/test_suite/test_cases/protein_design_1POH/protein_design_1POH.py +52 -0
  61. eqc_models-0.13.0/test_suite/test_cases/qsvm_dual_iris/data/X_test.npy +0 -0
  62. eqc_models-0.13.0/test_suite/test_cases/qsvm_dual_iris/data/X_train.npy +0 -0
  63. eqc_models-0.13.0/test_suite/test_cases/qsvm_dual_iris/data/y_test.npy +0 -0
  64. eqc_models-0.13.0/test_suite/test_cases/qsvm_dual_iris/data/y_train.npy +0 -0
  65. eqc_models-0.13.0/test_suite/test_cases/qsvm_dual_iris/qsvm_dual_iris.py +64 -0
  66. eqc_models-0.13.0/test_suite/test_cases/qsvm_primal_iris/data/X_test.npy +0 -0
  67. eqc_models-0.13.0/test_suite/test_cases/qsvm_primal_iris/data/X_train.npy +0 -0
  68. eqc_models-0.13.0/test_suite/test_cases/qsvm_primal_iris/data/y_test.npy +0 -0
  69. eqc_models-0.13.0/test_suite/test_cases/qsvm_primal_iris/data/y_train.npy +0 -0
  70. eqc_models-0.13.0/test_suite/test_cases/qsvm_primal_iris/qsvm_primal_iris.py +57 -0
  71. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_100/data/C_8000000_100.npy +0 -0
  72. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_100/data/J_8000000_100.npy +0 -0
  73. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_100/synthetic_cls_100.py +52 -0
  74. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_300/data/C_8000000_300.npy +0 -0
  75. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_300/data/J_8000000_300.npy +0 -0
  76. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_300/synthetic_cls_300.py +52 -0
  77. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_500/data/C_8000000_500.npy +0 -0
  78. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_500/data/J_8000000_500.npy +0 -0
  79. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_500/synthetic_cls_500.py +52 -0
  80. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_700/data/C_8000000_700.npy +0 -0
  81. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_700/data/J_8000000_700.npy +0 -0
  82. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_700/synthetic_cls_700.py +52 -0
  83. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_900/data/C_8000000_900.npy +0 -0
  84. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_900/data/J_8000000_900.npy +0 -0
  85. eqc_models-0.13.0/test_suite/test_cases/synthetic_cls_900/synthetic_cls_900.py +52 -0
  86. eqc_models-0.13.0/test_suite/test_suite_config.json +212 -0
  87. eqc_models-0.13.0/test_suite/test_utils.py +34 -0
  88. eqc_models-0.11.1/eqc_models/base/polyeval.c +0 -11363
  89. eqc_models-0.11.1/eqc_models/graph/__init__.py +0 -6
  90. eqc_models-0.11.1/eqc_models.egg-info/requires.txt +0 -14
  91. {eqc_models-0.11.1 → eqc_models-0.13.0}/.gitlab-ci.yml +0 -0
  92. {eqc_models-0.11.1 → eqc_models-0.13.0}/LICENSE.txt +0 -0
  93. {eqc_models-0.11.1 → eqc_models-0.13.0}/MANIFEST.in +0 -0
  94. {eqc_models-0.11.1 → eqc_models-0.13.0}/README.md +0 -0
  95. {eqc_models-0.11.1 → eqc_models-0.13.0}/compile_extensions.py +0 -0
  96. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/Makefile +0 -0
  97. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/basic.css +0 -0
  98. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/css/badge_only.css +0 -0
  99. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/css/theme.css +0 -0
  100. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/custom.css +0 -0
  101. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/file.png +0 -0
  102. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/minus.png +0 -0
  103. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/plus.png +0 -0
  104. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/pygments.css +0 -0
  105. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/build/html/_static/white_logo.png +0 -0
  106. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/make.bat +0 -0
  107. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/_static/custom.css +0 -0
  108. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/_static/white_logo.png +0 -0
  109. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/conf.py +0 -0
  110. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/dependencies.rst +0 -0
  111. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/eqc_models.rst +0 -0
  112. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/index.rst +0 -0
  113. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/modules.rst +0 -0
  114. {eqc_models-0.11.1 → eqc_models-0.13.0}/docs/source/usage.rst +0 -0
  115. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/__init__.py +0 -0
  116. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/algorithms/__init__.py +0 -0
  117. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/algorithms/base.py +0 -0
  118. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/algorithms/penaltymultiplier.py +0 -0
  119. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/allocation/__init__.py +0 -0
  120. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/allocation/allocation.py +0 -0
  121. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/allocation/portbase.py +0 -0
  122. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/allocation/portmomentum.py +0 -0
  123. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/assignment/__init__.py +0 -0
  124. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/assignment/qap.py +0 -0
  125. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/assignment/resource.py +0 -0
  126. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/assignment/setpartition.py +0 -0
  127. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/__init__.py +0 -0
  128. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/operators.py +0 -0
  129. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/base/polyeval.pyx +0 -0
  130. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/combinatorics/__init__.py +0 -0
  131. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/combinatorics/setcover.py +0 -0
  132. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/combinatorics/setpartition.py +0 -0
  133. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/decoding.py +0 -0
  134. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/graph/hypergraph.py +0 -0
  135. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/graph/maxcut.py +0 -0
  136. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/graph/maxkcut.py +0 -0
  137. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/graph/partition.py +0 -0
  138. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/__init__.py +0 -0
  139. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/cvqboost_hamiltonian.pyx +0 -0
  140. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/cvqboost_hamiltonian_c_func.c +0 -0
  141. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/cvqboost_hamiltonian_c_func.h +0 -0
  142. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/forecast.py +0 -0
  143. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/forecastbase.py +0 -0
  144. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/regressor.py +0 -0
  145. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/ml/reservoir.py +0 -0
  146. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/sequence/__init__.py +0 -0
  147. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/sequence/tsp.py +0 -0
  148. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/utilities/__init__.py +0 -0
  149. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/utilities/fileio.py +0 -0
  150. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/utilities/polynomial.py +0 -0
  151. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models/utilities/qplib.py +0 -0
  152. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models.egg-info/dependency_links.txt +0 -0
  153. {eqc_models-0.11.1 → eqc_models-0.13.0}/eqc_models.egg-info/top_level.txt +0 -0
  154. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/binary_job_example.py +0 -0
  155. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/c6h6_graph_clustering.py +0 -0
  156. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/clustering.py +0 -0
  157. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/continuous_job_example.py +0 -0
  158. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/crew_assignment_example.py +0 -0
  159. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/graph_clustering.py +0 -0
  160. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/graph_partitioning.py +0 -0
  161. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/hamiltonian_to_polynomial.py +0 -0
  162. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/hypergraph.py +0 -0
  163. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/karate_graph_clustering.py +0 -0
  164. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/lin_reg_dirac3.py +0 -0
  165. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/mackey_glass_cell_production_series.csv +0 -0
  166. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/pca_iris_dirac3.py +0 -0
  167. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qboost_iris_dirac3.py +0 -0
  168. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qplib_reader.py +0 -0
  169. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qplib_runner.py +0 -0
  170. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qsvm_dual_iris_dirac3.py +0 -0
  171. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/qsvm_iris_dirac3.py +0 -0
  172. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/reservoir_forecast.py +0 -0
  173. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/results_example.py +0 -0
  174. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/rundoctests.py +0 -0
  175. {eqc_models-0.11.1 → eqc_models-0.13.0}/scripts/utils.py +0 -0
  176. {eqc_models-0.11.1 → eqc_models-0.13.0}/setup.cfg +0 -0
  177. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/doctest_base.py +0 -0
  178. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testallocationmodel.py +0 -0
  179. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testconstraint.py +0 -0
  180. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testcvqboost.py +0 -0
  181. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testeqcdirectsolver.py +0 -0
  182. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testgraphpartitionmodel.py +0 -0
  183. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testhypergraphmodel.py +0 -0
  184. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testmaxcutmodel.py +0 -0
  185. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testqapmodel.py +0 -0
  186. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testqciclientsolver.py +0 -0
  187. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testquadraticmodel.py +0 -0
  188. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testsetcovermodel.py +0 -0
  189. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testsetpartitionmodel.py +0 -0
  190. {eqc_models-0.11.1 → eqc_models-0.13.0}/test/testtsp.py +0 -0
@@ -0,0 +1,6 @@
1
+ __pycache__
2
+ build
3
+ eqc_models.egg-info
4
+
5
+ *.c
6
+ *.so
@@ -1,9 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eqc-models
3
- Version: 0.11.1
3
+ Version: 0.13.0
4
4
  Summary: Optimization and ML modeling package targeting EQC devices
5
- Author: Quantum Computing Inc.
6
- Author-email: support@quantumcomputinginc.com
5
+ Author-email: "Quantum Computing Inc." <support@quantumcomputinginc.com>
7
6
  Project-URL: Homepage, https://quantumcomputinginc.com
8
7
  Project-URL: Documentation, https://quantumcomputinginc.com/learn/support/software-packages/
9
8
  Project-URL: Issues, https://support.quantumcomputinginc.com/
@@ -12,12 +11,11 @@ Description-Content-Type: text/markdown
12
11
  License-File: LICENSE.txt
13
12
  Requires-Dist: numpy<2,>=1.22.1
14
13
  Requires-Dist: networkx<3,>=2.6.3
15
- Requires-Dist: pandas>=2.1.0
16
- Requires-Dist: scikit-learn>=1.2.1
17
- Requires-Dist: qci-client<5,>=4.3.0
18
- Requires-Dist: emucore-direct==1.0.6
19
- Provides-Extra: direct
20
- Requires-Dist: eqc-direct<2,>=1.3.0; extra == "direct"
14
+ Requires-Dist: pandas<3,>=2.1.0
15
+ Requires-Dist: scikit-learn<2,>=1.2.1
16
+ Requires-Dist: qci-client<6,>=5
17
+ Requires-Dist: emucore-direct==1.0.7
18
+ Requires-Dist: eqc-direct==2.0.2
21
19
  Provides-Extra: dev
22
20
  Requires-Dist: pytest<8,>=7.1.0; extra == "dev"
23
21
  Requires-Dist: pytest-cov; extra == "dev"
@@ -41,6 +41,7 @@ class EqcModel:
41
41
  _upper_bound = None
42
42
  _H = None
43
43
  _machine_slacks = 0
44
+ _is_discrete = None
44
45
 
45
46
  def decode(self, solution : np.ndarray, from_encoding : str=None) -> np.ndarray:
46
47
  """
@@ -117,6 +118,16 @@ class EqcModel:
117
118
  warn("The domains property is deprecated in favor of naming it upper_bound", DeprecationWarning)
118
119
  self._upper_bound = value
119
120
 
121
+ @property
122
+ def is_discrete(self) -> List:
123
+ """ An array of boolean values indicating if variable should be restricted to a discrete domain """
124
+
125
+ return self._is_discrete
126
+
127
+ @is_discrete.setter
128
+ def is_discrete(self, value : List):
129
+ self._is_discrete = value
130
+
120
131
  @property
121
132
  def n(self) -> int:
122
133
  """ Return the number of variables """
@@ -0,0 +1,33 @@
1
+
2
+ def make_binary_penalty(var_idx, slack_idx, max_degree=2, penalty_multiplier=None):
3
+ """
4
+ Make a penalty expression enforcing binary values
5
+
6
+ The expression is formed from the conditions
7
+
8
+ $$
9
+ x + w = 1
10
+ $$
11
+
12
+ $$
13
+ xw = 0
14
+ $$
15
+
16
+ $$
17
+ w^2 + 2xw + x^2 - 2w - 2x + 1 + xw = 0
18
+ $$
19
+
20
+ $$
21
+ 3xw + w^2 + x^2 - 2w - 2x + 1 = 0
22
+ $$
23
+
24
+ """
25
+
26
+ indices = [(0, var_idx), (0, slack_idx), (var_idx, slack_idx), (var_idx, var_idx), (slack_idx, slack_idx)]
27
+ coefficients = [-2, -2, 3, 1, 1]
28
+ offset = 1
29
+ if penalty_multiplier is not None:
30
+ coefficients = [penalty_multiplier * c for c in coefficients]
31
+ offset *- penalty_multiplier
32
+ return coefficients, indices, offset
33
+
@@ -82,6 +82,45 @@ class ConstraintsMixIn:
82
82
  def constraints(self, value: Tuple[np.ndarray, np.ndarray]):
83
83
  self.lhs, self.rhs = value
84
84
 
85
+ @staticmethod
86
+ def _stackLHS(*args):
87
+ """
88
+
89
+ >>> m1 = np.array([1, 1, 1])
90
+ >>> m2 = np.array([[2.0, 2.0]])
91
+ >>> m3 = np.array([[0, 3, 3, 1], [1, 0, 1, 0]])
92
+ >>> ConstraintsMixIn._stackLHS(m1, m2, m3)
93
+ array([[1., 1., 1., 0.],
94
+ [2., 2., 0., 0.],
95
+ [0., 3., 3., 1.],
96
+ [1., 0., 1., 0.]])
97
+
98
+ """
99
+ n = 0
100
+ m = 0
101
+ dtype = np.int32
102
+ def update_dtype(matrix):
103
+ type_rank = ["int8", "int16", "int32", "int64", "float8", "float16", "float32", "float64"]
104
+ try:
105
+ idx = type_rank.index(str(matrix.dtype))
106
+ except ValueError:
107
+ raise TypeError(f"matrix type {matrix.dtype} not supported")
108
+ new_args = []
109
+ for mat in args:
110
+ if len(mat.shape) == 1:
111
+ mat = mat.reshape((1, mat.size))
112
+ if mat.shape[1] > n:
113
+ n = mat.shape[1]
114
+ m += mat.shape[0]
115
+ dtype = update_dtype(mat)
116
+ new_args.append(mat)
117
+ new_lhs = np.zeros((m, n), dtype=dtype)
118
+ idx = 0
119
+ for mat in new_args:
120
+ new_lhs[idx:idx+mat.shape[0],:mat.shape[1]] = mat
121
+ idx += mat.shape[0]
122
+ return new_lhs
123
+
85
124
  @property
86
125
  def offset(self) -> float:
87
126
  """ Calculate the offset due to the conversion of constraints to penalties """
@@ -1,10 +1,13 @@
1
1
  # (C) Quantum Computing Inc., 2024.
2
2
  from typing import Tuple, Union, List
3
+ import logging
3
4
  import numpy as np
4
5
  from eqc_models.base.base import EqcModel
5
6
  from eqc_models.base.operators import Polynomial, QUBO, OperatorNotAvailableError
6
7
  from eqc_models.base.constraints import ConstraintsMixIn
7
8
 
9
+ log = logging.getLogger(name=__name__)
10
+
8
11
  class PolynomialMixin:
9
12
  """This class provides an instance method and property that
10
13
  manage polynomial models.
@@ -46,7 +49,12 @@ class PolynomialMixin:
46
49
 
47
50
  """
48
51
 
49
- value = self.polynomial.evaluate(np.array(solution))
52
+ coefficients, indices = self.H
53
+ log.debug("Coefficients (%d): %s", len(coefficients), coefficients)
54
+ log.debug("Indices (%d): %s", len(indices), indices)
55
+ polynomial = self.polynomial
56
+ log.debug("Polynomial: %s", polynomial)
57
+ value = polynomial.evaluate(np.array(solution))
50
58
 
51
59
  return value
52
60
 
@@ -107,8 +115,19 @@ class PolynomialModel(PolynomialMixin, EqcModel):
107
115
  """
108
116
 
109
117
  def __init__(self, coefficients : Union[List, np.ndarray], indices : Union[List, np.ndarray]) -> None:
118
+ # ensure that the coefficients are ordered and not duplicated
119
+ new_coefficients = {}
120
+ for idx, v in zip(indices, coefficients):
121
+ idx = tuple(idx)
122
+ if idx in new_coefficients:
123
+ new_coefficients[idx] += v
124
+ else:
125
+ new_coefficients[idx] = v
126
+ new_indices = [idx for idx in new_coefficients]
127
+ new_indices.sort()
128
+ coefficients = [new_coefficients[idx] for idx in new_indices]
110
129
  self.coefficients = coefficients
111
- self.indices = indices
130
+ self.indices = new_indices
112
131
 
113
132
  @property
114
133
  def polynomial(self) -> Polynomial:
@@ -245,6 +245,6 @@ class ConstrainedQuadraticModel(ConstraintsMixIn, QuadraticModel):
245
245
  return self.lhs, self.rhs
246
246
 
247
247
  def evaluateObjective(self, solution: np.ndarray) -> float:
248
- J = self.quad_objective
249
- C = self.linear_objective
248
+ J = np.array(self.quad_objective)
249
+ C = np.array(self.linear_objective)
250
250
  return np.squeeze(C.T @ solution + solution.T@J@solution)
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ from typing import Dict
2
3
  import warnings
3
4
  import numpy as np
4
5
 
@@ -41,6 +42,14 @@ class SolutionResults:
41
42
  device : str
42
43
  String that represents the device used to solve the model.
43
44
 
45
+ raw_solutions : np.ndarray
46
+ Numpy array of the solutions as returned by the solver device.
47
+
48
+ calibration_time : float
49
+ Total time spend during job exectution where the device performed calibration.
50
+ The calibration is not directly affected by the job submission and the time
51
+ is not included in run_time.
52
+
44
53
  time_units : str
45
54
  String indicator of the unit of time reported in the metrics. Only
46
55
  ns is supported at this time.
@@ -56,6 +65,7 @@ class SolutionResults:
56
65
  postprocessing_time : np.ndarray
57
66
  penalties : np.ndarray = None
58
67
  device : str = None
68
+ raw_solutions : np.ndarray = None
59
69
  time_units : str = "ns"
60
70
 
61
71
  @property
@@ -95,7 +105,22 @@ class SolutionResults:
95
105
 
96
106
  @classmethod
97
107
  def from_cloud_response(cls, model, response, solver):
98
- """ Fill in the details from the cloud """
108
+ """
109
+ Fill in the details from the cloud
110
+
111
+ Parameters
112
+ ------------
113
+
114
+ model : eqc_models.base.EqcModel
115
+ EqcModel object describing the problem solved in response
116
+
117
+ response : Dict
118
+ Dictionary of the repsonse from the solver device.
119
+
120
+ solver : eqc_models.base.ModelSolver
121
+ ModelSolver object which is used to obtain job metrics.
122
+
123
+ """
99
124
 
100
125
  solutions = np.array(response["results"]["solutions"])
101
126
  if model.machine_slacks > 0:
@@ -164,3 +189,58 @@ class SolutionResults:
164
189
  device=device_type, time_units="ns")
165
190
 
166
191
  return results
192
+
193
+ @classmethod
194
+ def from_eqcdirect_response(cls, model, response, solver):
195
+ """
196
+ Fill in details from the response dictionary and possibly the solver device.
197
+
198
+ Parameters
199
+ ------------
200
+
201
+ model : eqc_models.base.EqcModel
202
+ EqcModel object describing the problem solved in response
203
+
204
+ response : Dict
205
+ Dictionary of the repsonse from the solver device.
206
+
207
+ solver : eqc_models.base.ModelSolver
208
+ ModelSolver object which is used to obtain device information.
209
+
210
+ """
211
+ solutions = np.array(response["solution"])
212
+ if model.machine_slacks > 0:
213
+ solutions = solutions[:,:-model.machine_slacks]
214
+ energies = np.array(response["energy"])
215
+ # interrogate to determine the device type
216
+ info_dict = solver.client.system_info()
217
+ device_type = info_dict["device_type"]
218
+ if hasattr(model, "evaluateObjective"):
219
+ objectives = np.zeros((solutions.shape[0],), dtype=np.float32)
220
+ for i in range(solutions.shape[0]):
221
+ try:
222
+ objective = model.evaluateObjective(solutions[i])
223
+ except NotImplementedError:
224
+ warnings.warn(f"Cannot set objective value in results for {model.__class__}")
225
+ objectives = None
226
+ break
227
+ objectives[i] = objective
228
+ else:
229
+ objectives = None
230
+ if hasattr(model, "evaluatePenalties"):
231
+ penalties = np.zeros((solutions.shape[0],), dtype=np.float32)
232
+ for i in range(solutions.shape[0]):
233
+ penalties[i] = model.evaluatePenalties(solutions[i]) + model.offset
234
+ else:
235
+ penalties = None
236
+ counts = np.ones(solutions.shape[0])
237
+ runtime = response["runtime"]
238
+ post = response["postprocessing_time"]
239
+ pre = response["preprocessing_time"]
240
+ results = SolutionResults(solutions, energies, counts, objectives,
241
+ runtime, pre, post, penalties=penalties,
242
+ device=device_type, time_units="s")
243
+
244
+ return results
245
+
246
+
@@ -0,0 +1,11 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+
3
+ from .base import EdgeMixin, EdgeModel, GraphModel, NodeModel
4
+ from .maxcut import MaxCutModel
5
+ from .partition import GraphPartitionModel
6
+ from .shortestpath import ShortestPathModel
7
+ from .rcshortestpath import RCShortestPathModel
8
+
9
+ __all__ = ["MaxCutModel", "GraphPartitionModel",
10
+ "EdgeMixin", "EdgeModel", "GraphModel",
11
+ "NodeModel"]
@@ -7,7 +7,7 @@ class GraphModel(QuadraticModel):
7
7
  """ """
8
8
  def __init__(self, G : nx.Graph):
9
9
  self.G = G
10
- super().__init__(*self.costFunction())
10
+ super(GraphModel, self).__init__(*self.costFunction())
11
11
 
12
12
  @property
13
13
  def linear_objective(self):
@@ -63,12 +63,16 @@ class TwoPartitionModel(NodeModel):
63
63
 
64
64
  """
65
65
 
66
- class EdgeModel(GraphModel):
67
- """ Create a model where the variables are edge-based """
66
+ class EdgeMixin:
68
67
 
69
68
  @property
70
69
  def variables(self) -> List[str]:
71
70
  """ Provide a variable name to index lookup; order enforced by sorting the list before returning """
72
- names = [f"({u},{v})" for u, v in self.G.edges]
71
+ names = [(u, v) for u, v in self.G.edges]
73
72
  names.sort()
74
73
  return names
74
+
75
+ class EdgeModel(EdgeMixin, GraphModel):
76
+ """ Create a model where the variables are edge-based """
77
+
78
+
@@ -0,0 +1,81 @@
1
+ import logging
2
+ from typing import (Any, Dict, Tuple, List)
3
+ import networkx as nx
4
+ import numpy as np
5
+ from eqc_models.graph.shortestpath import ShortestPathModel
6
+
7
+ log = logging.getLogger(name=__name__)
8
+
9
+ class RCShortestPathModel(ShortestPathModel):
10
+ """
11
+ Model for resource constrained shortest path problems.
12
+ Use a shortest path base model to implement the routing
13
+ constraints and objective function. Add resource constraints.
14
+
15
+ """
16
+
17
+ def __init__(self, G : nx.DiGraph, s : Any, t : Any, T : int, resource_key : str="resource"):
18
+ if T <= 0:
19
+ raise ValueError("T must be positive")
20
+ elif round(T, 0) != T:
21
+ raise ValueError("T must be integer-valued")
22
+ self.T = T
23
+ self.resource_key = resource_key
24
+ # determine shortest path by weight
25
+ nx_path = nx.shortest_path(G, s, t)
26
+ path_length = len(nx_path)
27
+ self.resource_mult = resource_mult = 2 * path_length / self.T
28
+ super(RCShortestPathModel, self).__init__(G, s, t)
29
+ upper_bound = np.ones(len(self.variables))
30
+ upper_bound[-1] = np.ceil(resource_mult * T)
31
+ self.upper_bound = upper_bound
32
+ is_discrete = [True for i in range(upper_bound.shape[0])]
33
+ is_discrete[-1] = False
34
+ self.is_discrete = is_discrete
35
+
36
+ @property
37
+ def variables(self):
38
+ variables = super(RCShortestPathModel, self).variables
39
+ return variables + ["resource_slack"]
40
+
41
+ def buildConstraints(self) -> Tuple[np.ndarray,np.ndarray]:
42
+ lhs, rhs = super(RCShortestPathModel, self).buildConstraints()
43
+ # add a single constraint
44
+ G = self.G
45
+ n = len(self.variables)
46
+ resource_lhs = np.zeros((1, n), dtype=np.float32)
47
+ resource_mult = self.resource_mult
48
+ log.debug("Resource multiplier %f", resource_mult)
49
+ for i in range(len(G.edges)):
50
+ (u, v) = self.variables[i]
51
+ # find the time to traverse the arc
52
+ resource_cost = G.edges[(u, v)][self.resource_key]
53
+ resource_cost *= resource_mult
54
+ log.debug(f"Adding resource %s for edge %s", resource_cost, (u, v))
55
+ resource_lhs[0, i] = resource_cost
56
+ resource_lhs[0, -1] = 1
57
+ lhs = self._stackLHS(lhs, resource_lhs)
58
+ rhs = np.hstack([rhs, [self.T*resource_mult]])
59
+ log.debug("LHS shape %s RHS shape %s", lhs.shape, rhs.shape)
60
+ return lhs, rhs
61
+
62
+ def pathCost(self, path):
63
+ """ sum the cost of all legs in the path """
64
+ assert self.s in path
65
+ G = self.G
66
+ node = self.s
67
+ max_len = len(self.G.nodes) - 1
68
+ path_len = 0
69
+ path_cost = 0
70
+ path_resources = 0
71
+ while node != t:
72
+ edge = (node, path[node])
73
+ if edge not in G.edges:
74
+ raise ValueError(f"Edge {edge} not found")
75
+ path_len += 1
76
+ path_cost += G.edges[edge]["weight"]
77
+ path_resources += G.edges[edge][self.resource_key]
78
+ if path_len > max_len:
79
+ raise ValueError("Invalid path. Describes a cycle.")
80
+ return path_cost, path_resources
81
+
@@ -0,0 +1,190 @@
1
+ r"""
2
+ MIP Shortest Path implementation
3
+
4
+ Given a graph $G$ and nodes $s$ and $t$, find the shortest path
5
+ by edge weight between $s$ and $t$.
6
+
7
+ $$
8
+ \min sum_ij w_ij x_ij
9
+ $$
10
+ subject to
11
+ $$
12
+ \sum_{(u,v)\in E} x_{u,v} - \sum{(v,u)\in E} x_{v,u} = 0 \forall u\in N\\{s,t}
13
+ $$
14
+ and
15
+ $$
16
+ \sum_{(s,v)\in E} x_{s,v} = 1
17
+ $$
18
+ and
19
+ $$
20
+ \sum_{(u,t)\in E} x_{u,t} = 1
21
+ $$
22
+
23
+ """
24
+
25
+ from typing import Any, Dict, Tuple
26
+ import logging
27
+ import numpy as np
28
+ import networkx as nx
29
+ from eqc_models.graph import EdgeMixin
30
+ from eqc_models.base.quadratic import ConstrainedQuadraticModel
31
+
32
+ log = logging.getLogger(name=__name__)
33
+
34
+ class ShortestPathModel(EdgeMixin, ConstrainedQuadraticModel):
35
+ """
36
+ ShortestPathModel describes the MIP formulation for the
37
+ shortest path problem.
38
+
39
+ Parameters
40
+ -------------
41
+
42
+ G : nx.DiGraph
43
+ A directed graph which is assumed to be connected. A graph
44
+ with disconnected subgraphs may reveal a solution if $s$ and $t$
45
+ are in the same subgraph, but testing for the existence of a path
46
+ between s and t using this model is not recommended. This is
47
+ due to the difficulty posed by selecting a penalty multiplier
48
+ large enough to enforce the panalties, which DNE in the infeasible
49
+ case.
50
+ s : Any
51
+ This is the label for the start node.
52
+ t : Any
53
+ This is the label for the end node.
54
+
55
+
56
+ """
57
+
58
+ def __init__(self, G: nx.DiGraph, s : Any, t : Any):
59
+ self.G = G
60
+ self.s = s
61
+ self.t = t
62
+ self.lhs, self.rhs = self.buildConstraints()
63
+ C, J = self.buildObjective()
64
+ super(ShortestPathModel, self).__init__(C, J, self.lhs, self.rhs)
65
+ self.upper_bound = np.ones(self.lhs.shape[1])
66
+ self.is_discrete = [True for i in range(self.lhs.shape[1])]
67
+ self.machine_slacks = 0
68
+
69
+ def buildConstraints(self) -> Tuple[np.ndarray,np.ndarray]:
70
+ """
71
+ Constraints:
72
+ $$
73
+ sum_j x[i,l] - sum_j x[j,l] = c for all l
74
+ $$
75
+ $c$ is -1, 1 or 0 for $i=t$, $s$ or all others
76
+
77
+ """
78
+ log.debug("Building constraints to find path from %s to %s", self.s, self.t)
79
+ variables = self.variables
80
+ nodes = [n for n in self.G.nodes]
81
+ m = len(nodes)
82
+ n = len(variables)
83
+ _cons = np.zeros((m, n), dtype=np.int8)
84
+ _rhs = np.zeros((m, 1), dtype=np.int8)
85
+ for node_index, k in enumerate(nodes):
86
+ if k == self.s:
87
+ _rhs[node_index, 0] = 1
88
+ elif k == self.t:
89
+ _rhs[node_index, 0] = -1
90
+ for l in range(len(self.G.edges)): # don't enumerate the edges because the order could change
91
+ (i, j) = self.variables[l]
92
+ if i == j:
93
+ # self loops are not allowed
94
+ raise ValueError("Self loops are not allowed in ShortestPathModel")
95
+ # # ignore these edges because we can't go back to s or leave t
96
+ elif j == self.s:
97
+ continue
98
+ elif i == self.t:
99
+ continue
100
+ i_index = nodes.index(i)
101
+ j_index = nodes.index(j)
102
+ _cons[i_index, l] = 1
103
+ _cons[j_index, l] = -1
104
+ log.debug("LHS shape %s RHS shape %s", _cons.shape, _rhs.shape)
105
+ log.debug("checksum %f min %f", np.sum(_cons), np.min(_cons))
106
+ assert np.sum(_rhs) == 0
107
+ return _cons, np.squeeze(_rhs)
108
+
109
+ def buildObjective(self) -> Tuple[np.ndarray, np.ndarray]:
110
+ r"""
111
+ Objective:
112
+ $\min sum_ij w_ij x_ij$
113
+
114
+ """
115
+ variables = self.variables
116
+ G = self.G
117
+ nodes = G.nodes
118
+ m, n = len(nodes), len(variables)
119
+ _obj = [0 for i in range(n)]
120
+ for index, name in enumerate(variables):
121
+ if type(name) == type((1,2)):
122
+ i, j = name
123
+ _obj[index] = v = G.get_edge_data(i, j)["weight"]
124
+ assert not np.isnan(v), f"Got a NaN at {i, j}"
125
+ J = np.zeros((n, n))
126
+ return np.array(_obj), J
127
+
128
+ def decode(self, solution : np.ndarray) -> Dict:
129
+ """
130
+ Convert a solution to this model into a path, which is
131
+ a dictionary with each edge described by key, value pairs.
132
+
133
+ """
134
+ variables = self.variables[:len(self.G.edges)]
135
+ # log.debug("Using variables %s", variables)
136
+
137
+ lhs, rhs = self.constraints
138
+ log.debug("LHS shape %s RHS shape %s", lhs.shape, rhs.shape)
139
+ upper_thresh = max(solution[:len(variables)])
140
+ lower_thresh = 0
141
+ got_path = None
142
+ while upper_thresh - lower_thresh > 1e-6:
143
+ log.debug("Lower Value: %f Upper Value %f", lower_thresh, upper_thresh)
144
+ thresh = (lower_thresh + upper_thresh) / 2
145
+ nx_path = None
146
+ G = nx.DiGraph()
147
+ for (i, j), value in zip(variables, solution):
148
+ if value > thresh:
149
+ G.add_edge(i, j)
150
+ edges = [e for e in G.edges]
151
+ log.debug("Resulting edge count %s", len(edges))
152
+ self.alt_g = G
153
+ path = {}
154
+ try:
155
+ nx_path = nx.shortest_path(G, self.s, self.t)
156
+ lower_thresh = thresh
157
+ got_path = nx_path
158
+ log.debug("Got path size %d", len(got_path))
159
+ except (nx.exception.NodeNotFound, nx.NetworkXAlgorithmError) as err:
160
+ upper_thresh = thresh
161
+ if got_path is None:
162
+ raise RuntimeError(f"Solution does not describe path from {self.s} to {self.t}")
163
+ # path = {}
164
+ # log.debug("Translating path to dictionary")
165
+ # for i, v in enumerate(got_path):
166
+ # path[got_path[i-1]] = v
167
+ # log.debug("Updated path %s", path)
168
+ # if self.t in path:
169
+ # log.debug("Removing %s from path keys.", self.t)
170
+ # del path[self.t]
171
+ return got_path
172
+
173
+ def pathCost(self, path):
174
+ """ sum the cost of all legs in the path """
175
+ assert self.s in path
176
+ G = self.G
177
+ node = self.s
178
+ max_len = len(self.G.nodes) - 1
179
+ path_len = 0
180
+ path_cost = 0
181
+ while node != t:
182
+ edge = (node, path[node])
183
+ if edge not in G.edges:
184
+ raise ValueError(f"Edge {edge} not found")
185
+ path_len += 1
186
+ path_cost += G.edges[edge]["weight"]
187
+ if path_len > max_len:
188
+ raise ValueError("Invalid path. Describes a cycle.")
189
+ return path_cost
190
+