eqc-models 0.11.0__tar.gz → 0.12.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 (186) hide show
  1. {eqc_models-0.11.0/eqc_models.egg-info → eqc_models-0.12.0}/PKG-INFO +2 -3
  2. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/conf.py +1 -1
  3. eqc_models-0.12.0/eqc_models/assignment/__init__.py +6 -0
  4. eqc_models-0.12.0/eqc_models/assignment/resource.py +165 -0
  5. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/polyeval.c +4219 -3442
  6. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/quadratic.py +2 -2
  7. eqc_models-0.12.0/eqc_models/base/results.py +166 -0
  8. eqc_models-0.12.0/eqc_models/graph/__init__.py +9 -0
  9. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/graph/base.py +8 -4
  10. eqc_models-0.12.0/eqc_models/graph/shortestpath.py +157 -0
  11. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/classifierbase.py +31 -5
  12. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/classifierqboost.py +14 -1
  13. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/classifierqsvm.py +223 -19
  14. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/clustering.py +5 -5
  15. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/clusteringbase.py +1 -1
  16. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/decomposition.py +39 -16
  17. eqc_models-0.12.0/eqc_models/process/base.py +18 -0
  18. eqc_models-0.12.0/eqc_models/process/mpc.py +17 -0
  19. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/solvers/__init__.py +1 -5
  20. eqc_models-0.12.0/eqc_models/solvers/eqcdirect.py +71 -0
  21. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/solvers/qciclient.py +6 -3
  22. {eqc_models-0.11.0 → eqc_models-0.12.0/eqc_models.egg-info}/PKG-INFO +2 -3
  23. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models.egg-info/SOURCES.txt +59 -3
  24. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models.egg-info/requires.txt +1 -3
  25. {eqc_models-0.11.0 → eqc_models-0.12.0}/pyproject.toml +5 -2
  26. eqc_models-0.12.0/scripts/crew_assignment_example.py +51 -0
  27. eqc_models-0.12.0/scripts/qboost_iris_dirac3_direct.py +103 -0
  28. eqc_models-0.12.0/scripts/qsvm_dual_iris_dirac3.py +101 -0
  29. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/qsvm_iris_dirac3.py +2 -5
  30. eqc_models-0.12.0/scripts/test_shortestpath.py +38 -0
  31. eqc_models-0.12.0/test/testshortestpath.py +55 -0
  32. eqc_models-0.12.0/test_suite/README.txt +22 -0
  33. eqc_models-0.12.0/test_suite/run_tests.py +293 -0
  34. eqc_models-0.12.0/test_suite/test_cases/c6h6_graph_clustering/c6h6_graph_clustering.py +60 -0
  35. eqc_models-0.12.0/test_suite/test_cases/clustering/clustering.py +49 -0
  36. eqc_models-0.12.0/test_suite/test_cases/clustering/data/X.npy +0 -0
  37. eqc_models-0.12.0/test_suite/test_cases/cvqboost_iris/cvqboost_iris.py +63 -0
  38. eqc_models-0.12.0/test_suite/test_cases/cvqboost_iris/data/X_test.npy +0 -0
  39. eqc_models-0.12.0/test_suite/test_cases/cvqboost_iris/data/X_train.npy +0 -0
  40. eqc_models-0.12.0/test_suite/test_cases/cvqboost_iris/data/y_test.npy +0 -0
  41. eqc_models-0.12.0/test_suite/test_cases/cvqboost_iris/data/y_train.npy +0 -0
  42. eqc_models-0.12.0/test_suite/test_cases/karate_graph_clustering/karate_graph_clustering.py +44 -0
  43. eqc_models-0.12.0/test_suite/test_cases/pca_iris/pca_iris.py +59 -0
  44. eqc_models-0.12.0/test_suite/test_cases/protein_design_1MJC/data/C_1MJC.npy +0 -0
  45. eqc_models-0.12.0/test_suite/test_cases/protein_design_1MJC/data/J_1MJC.npy +0 -0
  46. eqc_models-0.12.0/test_suite/test_cases/protein_design_1MJC/protein_design_1MJC.py +52 -0
  47. eqc_models-0.12.0/test_suite/test_cases/protein_design_1NXB/data/C_1NXB.npy +0 -0
  48. eqc_models-0.12.0/test_suite/test_cases/protein_design_1NXB/data/J_1NXB.npy +0 -0
  49. eqc_models-0.12.0/test_suite/test_cases/protein_design_1NXB/protein_design_1NXB.py +52 -0
  50. eqc_models-0.12.0/test_suite/test_cases/protein_design_1POH/data/C_1POH.npy +0 -0
  51. eqc_models-0.12.0/test_suite/test_cases/protein_design_1POH/data/J_1POH.npy +0 -0
  52. eqc_models-0.12.0/test_suite/test_cases/protein_design_1POH/protein_design_1POH.py +52 -0
  53. eqc_models-0.12.0/test_suite/test_cases/qsvm_dual_iris/data/X_test.npy +0 -0
  54. eqc_models-0.12.0/test_suite/test_cases/qsvm_dual_iris/data/X_train.npy +0 -0
  55. eqc_models-0.12.0/test_suite/test_cases/qsvm_dual_iris/data/y_test.npy +0 -0
  56. eqc_models-0.12.0/test_suite/test_cases/qsvm_dual_iris/data/y_train.npy +0 -0
  57. eqc_models-0.12.0/test_suite/test_cases/qsvm_dual_iris/qsvm_dual_iris.py +64 -0
  58. eqc_models-0.12.0/test_suite/test_cases/qsvm_primal_iris/data/X_test.npy +0 -0
  59. eqc_models-0.12.0/test_suite/test_cases/qsvm_primal_iris/data/X_train.npy +0 -0
  60. eqc_models-0.12.0/test_suite/test_cases/qsvm_primal_iris/data/y_test.npy +0 -0
  61. eqc_models-0.12.0/test_suite/test_cases/qsvm_primal_iris/data/y_train.npy +0 -0
  62. eqc_models-0.12.0/test_suite/test_cases/qsvm_primal_iris/qsvm_primal_iris.py +57 -0
  63. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_100/data/C_8000000_100.npy +0 -0
  64. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_100/data/J_8000000_100.npy +0 -0
  65. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_100/synthetic_cls_100.py +52 -0
  66. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_300/data/C_8000000_300.npy +0 -0
  67. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_300/data/J_8000000_300.npy +0 -0
  68. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_300/synthetic_cls_300.py +52 -0
  69. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_500/data/C_8000000_500.npy +0 -0
  70. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_500/data/J_8000000_500.npy +0 -0
  71. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_500/synthetic_cls_500.py +52 -0
  72. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_700/data/C_8000000_700.npy +0 -0
  73. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_700/data/J_8000000_700.npy +0 -0
  74. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_700/synthetic_cls_700.py +52 -0
  75. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_900/data/C_8000000_900.npy +0 -0
  76. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_900/data/J_8000000_900.npy +0 -0
  77. eqc_models-0.12.0/test_suite/test_cases/synthetic_cls_900/synthetic_cls_900.py +52 -0
  78. eqc_models-0.12.0/test_suite/test_suite_config.json +212 -0
  79. eqc_models-0.12.0/test_suite/test_utils.py +34 -0
  80. eqc_models-0.11.0/eqc_models/assignment/__init__.py +0 -5
  81. eqc_models-0.11.0/eqc_models/base/results.py +0 -94
  82. eqc_models-0.11.0/eqc_models/graph/__init__.py +0 -6
  83. eqc_models-0.11.0/eqc_models/sequence/scheduling.py +0 -29
  84. eqc_models-0.11.0/scripts/duality_example.py +0 -73
  85. {eqc_models-0.11.0 → eqc_models-0.12.0}/.gitlab-ci.yml +0 -0
  86. {eqc_models-0.11.0 → eqc_models-0.12.0}/LICENSE.txt +0 -0
  87. {eqc_models-0.11.0 → eqc_models-0.12.0}/MANIFEST.in +0 -0
  88. {eqc_models-0.11.0 → eqc_models-0.12.0}/README.md +0 -0
  89. {eqc_models-0.11.0 → eqc_models-0.12.0}/compile_extensions.py +0 -0
  90. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/Makefile +0 -0
  91. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/basic.css +0 -0
  92. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/css/badge_only.css +0 -0
  93. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/css/theme.css +0 -0
  94. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/custom.css +0 -0
  95. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/file.png +0 -0
  96. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/minus.png +0 -0
  97. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/plus.png +0 -0
  98. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/pygments.css +0 -0
  99. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/build/html/_static/white_logo.png +0 -0
  100. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/make.bat +0 -0
  101. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/_static/custom.css +0 -0
  102. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/_static/white_logo.png +0 -0
  103. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/dependencies.rst +0 -0
  104. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/eqc_models.rst +0 -0
  105. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/index.rst +0 -0
  106. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/modules.rst +0 -0
  107. {eqc_models-0.11.0 → eqc_models-0.12.0}/docs/source/usage.rst +0 -0
  108. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/__init__.py +0 -0
  109. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/algorithms/__init__.py +0 -0
  110. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/algorithms/base.py +0 -0
  111. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/algorithms/penaltymultiplier.py +0 -0
  112. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/allocation/__init__.py +0 -0
  113. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/allocation/allocation.py +0 -0
  114. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/allocation/portbase.py +0 -0
  115. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/allocation/portmomentum.py +0 -0
  116. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/assignment/qap.py +0 -0
  117. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/assignment/setpartition.py +0 -0
  118. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/__init__.py +0 -0
  119. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/base.py +0 -0
  120. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/constraints.py +0 -0
  121. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/operators.py +0 -0
  122. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/polyeval.pyx +0 -0
  123. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/base/polynomial.py +0 -0
  124. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/combinatorics/__init__.py +0 -0
  125. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/combinatorics/setcover.py +0 -0
  126. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/combinatorics/setpartition.py +0 -0
  127. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/decoding.py +0 -0
  128. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/graph/hypergraph.py +0 -0
  129. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/graph/maxcut.py +0 -0
  130. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/graph/maxkcut.py +0 -0
  131. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/graph/partition.py +0 -0
  132. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/__init__.py +0 -0
  133. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/cvqboost_hamiltonian.pyx +0 -0
  134. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/cvqboost_hamiltonian_c_func.c +0 -0
  135. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/cvqboost_hamiltonian_c_func.h +0 -0
  136. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/forecast.py +0 -0
  137. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/forecastbase.py +0 -0
  138. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/regressor.py +0 -0
  139. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/regressorbase.py +0 -0
  140. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/ml/reservoir.py +0 -0
  141. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/sequence/__init__.py +0 -0
  142. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/sequence/tsp.py +0 -0
  143. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/utilities/__init__.py +0 -0
  144. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/utilities/fileio.py +0 -0
  145. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/utilities/polynomial.py +0 -0
  146. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models/utilities/qplib.py +0 -0
  147. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models.egg-info/dependency_links.txt +0 -0
  148. {eqc_models-0.11.0 → eqc_models-0.12.0}/eqc_models.egg-info/top_level.txt +0 -0
  149. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/binary_job_example.py +0 -0
  150. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/c6h6_graph_clustering.py +0 -0
  151. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/clustering.py +0 -0
  152. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/continuous_job_example.py +0 -0
  153. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/graph_clustering.py +0 -0
  154. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/graph_partitioning.py +0 -0
  155. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/hamiltonian_to_polynomial.py +0 -0
  156. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/hypergraph.py +0 -0
  157. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/integer_job_example.py +0 -0
  158. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/karate_graph_clustering.py +0 -0
  159. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/lin_reg_dirac3.py +0 -0
  160. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/mackey_glass_cell_production_series.csv +0 -0
  161. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/pca_iris_dirac3.py +0 -0
  162. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/port_opt_dirac3.py +0 -0
  163. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/qboost_iris_dirac3.py +0 -0
  164. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/qplib_benchmark_config.py +0 -0
  165. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/qplib_reader.py +0 -0
  166. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/qplib_runner.py +0 -0
  167. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/reservoir_forecast.py +0 -0
  168. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/results_example.py +0 -0
  169. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/rundoctests.py +0 -0
  170. {eqc_models-0.11.0 → eqc_models-0.12.0}/scripts/utils.py +0 -0
  171. {eqc_models-0.11.0 → eqc_models-0.12.0}/setup.cfg +0 -0
  172. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/doctest_base.py +0 -0
  173. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testallocationmodel.py +0 -0
  174. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testconstraint.py +0 -0
  175. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testcvqboost.py +0 -0
  176. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testeqcdirectsolver.py +0 -0
  177. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testgraphpartitionmodel.py +0 -0
  178. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testhypergraphmodel.py +0 -0
  179. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testmaxcutmodel.py +0 -0
  180. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testpolynomialmodel.py +0 -0
  181. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testqapmodel.py +0 -0
  182. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testqciclientsolver.py +0 -0
  183. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testquadraticmodel.py +0 -0
  184. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testsetcovermodel.py +0 -0
  185. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testsetpartitionmodel.py +0 -0
  186. {eqc_models-0.11.0 → eqc_models-0.12.0}/test/testtsp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eqc-models
3
- Version: 0.11.0
3
+ Version: 0.12.0
4
4
  Summary: Optimization and ML modeling package targeting EQC devices
5
5
  Author: Quantum Computing Inc.
6
6
  Author-email: support@quantumcomputinginc.com
@@ -16,8 +16,7 @@ Requires-Dist: pandas>=2.1.0
16
16
  Requires-Dist: scikit-learn>=1.2.1
17
17
  Requires-Dist: qci-client<5,>=4.3.0
18
18
  Requires-Dist: emucore-direct==1.0.6
19
- Provides-Extra: direct
20
- Requires-Dist: eqc-direct<2,>=1.3.0; extra == "direct"
19
+ Requires-Dist: eqc-direct<3,>=2.0.1
21
20
  Provides-Extra: dev
22
21
  Requires-Dist: pytest<8,>=7.1.0; extra == "dev"
23
22
  Requires-Dist: pytest-cov; extra == "dev"
@@ -9,7 +9,7 @@
9
9
 
10
10
  project = 'eqc-models'
11
11
  # sphinx adds a period after copyright
12
- copyright = '2024 Quantum Computing Inc. All rights reserved'
12
+ copyright = '2025 Quantum Computing Inc. All rights reserved'
13
13
  author = 'Quantum Computing Inc.'
14
14
 
15
15
  import importlib.metadata as metadata
@@ -0,0 +1,6 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+
3
+ from .qap import QAPModel
4
+ from .resource import ResourceAssignmentModel
5
+
6
+ __all__ = ["QAPModel", "ResourceAssignmentModel"]
@@ -0,0 +1,165 @@
1
+ from typing import (Dict, List)
2
+ import numpy as np
3
+ from eqc_models.base.quadratic import ConstrainedQuadraticModel
4
+ from eqc_models.base.constraints import InequalitiesMixin
5
+
6
+ class ResourceAssignmentModel(InequalitiesMixin, ConstrainedQuadraticModel):
7
+ """
8
+ Resource assignment model
9
+
10
+ Parameters
11
+ ------------
12
+
13
+ resources : List
14
+ tasks : List
15
+
16
+ >>> # name is not a required attribute of the resources or tasks
17
+ >>> crews = [{"name": "Maintenance Crew 1", "skills": ["A", "F"], "capacity": 5, "cost": 4},
18
+ ... {"name": "Baggage Crew 1", "skills": ["B"], "capacity": 4, "cost": 1},
19
+ ... {"name": "Maintenance Crew 2", "skills": ["A", "F"], "capacity": 5, "cost": 2}]
20
+ >>> tasks = [{"name": "Refuel", "skill_need": "F", "load": 3},
21
+ ... {"name": "Baggage", "skill_need": "B", "load": 1}]
22
+ >>> model = ResourceAssignmentModel(crews, tasks)
23
+ >>> assignments = model.createAssignmentVars()
24
+ >>> assignments
25
+ [{'resource': 0, 'task': 0}, {'resource': 1, 'task': 1}, {'resource': 2, 'task': 0}]
26
+ >>> A, b, senses = model.constrainAssignments(assignments)
27
+ >>> A
28
+ array([[3., 0., 0.],
29
+ [0., 1., 0.],
30
+ [0., 0., 3.],
31
+ [3., 0., 3.],
32
+ [0., 3., 0.]], dtype=float32)
33
+ >>> b
34
+ array([5., 4., 5., 3., 3.], dtype=float32)
35
+ >>> senses
36
+ ['LE', 'LE', 'LE', 'EQ', 'EQ']
37
+ >>> A, b = model.constraints
38
+ >>> A
39
+ array([[3., 0., 0., 1., 0., 0.],
40
+ [0., 1., 0., 0., 1., 0.],
41
+ [0., 0., 3., 0., 0., 1.],
42
+ [3., 0., 3., 0., 0., 0.],
43
+ [0., 3., 0., 0., 0., 0.]])
44
+
45
+ """
46
+
47
+ def __init__(self, resources, tasks):
48
+ self.resources = resources
49
+ self.checkTasks(tasks)
50
+ self.tasks = tasks
51
+ self.assignments = assignments = self.createAssignmentVars()
52
+ n = len(assignments) + len(resources)
53
+ self.variables = [f"a{i}" for i in range(len(assignments))]
54
+ self.upper_bound = np.ones((n,))
55
+ self.upper_bound[-len(resources):] = [resource["capacity"] for resource in resources]
56
+ A, b, senses = self.constrainAssignments(assignments)
57
+ J = np.zeros((n, n))
58
+ C = np.zeros((n,), dtype=np.float32)
59
+ # objective is to minimize cost of assignments
60
+ for j, assignment in enumerate(assignments):
61
+ C[j] = resources[assignment["resource"]]["cost"] * tasks[assignment["task"]]["load"]
62
+ super(ResourceAssignmentModel, self).__init__(C, J, A, b)
63
+ self.senses = senses
64
+ # always use a machine slack
65
+ self.machine_slacks = 1
66
+
67
+ @classmethod
68
+ def checkTasks(cls, tasks):
69
+ for task in tasks:
70
+ if "skill_need" not in task:
71
+ raise ValueError("All tasks must have the skill_need attribute")
72
+ if "load" not in task:
73
+ raise ValueError("All tasks must have the load attribute")
74
+
75
+ def createAssignmentVars(self):
76
+ """ Examine all combinatins of possible crew-task assignments """
77
+
78
+ assign_vars = []
79
+ resources = self.resources
80
+ tasks = self.tasks
81
+ for i, resource in enumerate(resources):
82
+ skills = resource["skills"]
83
+ for j, task in enumerate(tasks):
84
+ if task["skill_need"] in skills:
85
+ assign_vars.append({"resource": i, "task": j})
86
+ return assign_vars
87
+
88
+ def constrainAssignments(self, assignments : List) -> List:
89
+ """
90
+ Examine the assignments to determine the necessary constraints to
91
+ ensure feasibility of solution.
92
+
93
+ """
94
+ # A is sized using the number of crews and the number of assignment variables plus slacks
95
+ m1 = len(self.resources)
96
+ m2 = len(self.tasks)
97
+ n1 = len(assignments)
98
+ m = m1 + m2
99
+ n = n1
100
+ A = np.zeros((m, n), dtype=np.float32)
101
+ b = np.zeros((m,), dtype=np.float32)
102
+ for i, resource in enumerate(self.resources):
103
+ b[i] = resource["capacity"]
104
+ for k, assignment in enumerate(assignments):
105
+ if assignment["resource"] == i:
106
+ A[i, k] = self.tasks[assignment["task"]]["load"]
107
+ assignment_coeff = np.max(A)
108
+ for i, task in enumerate(self.tasks):
109
+ b[m1+i] = assignment_coeff
110
+ for k, assignment in enumerate(assignments):
111
+ if assignment["task"] == i:
112
+ A[m1+i, k] = assignment_coeff
113
+ senses = ["LE" for resource in self.resources] + ["EQ" for task in self.tasks]
114
+ return A, b, senses
115
+
116
+ @property
117
+ def sum_constraint(self) -> int:
118
+ """ This value is a suggestion which should be used with a machine slack """
119
+
120
+ sc = 0
121
+ sc += sum([resource["capacity"] for resource in self.resources])
122
+ sc += len(self.tasks)
123
+ return sc
124
+
125
+ def decode(self, solution : np.array) -> List[Dict]:
126
+ """
127
+ Convert the binary solution into a list of tasks
128
+
129
+ """
130
+
131
+ # ensure solution is array
132
+ solution = np.array(solution)
133
+ resource_assignments = [[] for resource in self.resources]
134
+ vals = [val for val in set(solution) if val <= 1.0]
135
+ # check if there are fractional values less than 1
136
+ if solution[~np.logical_or(solution==0, solution>=1)].size>0:
137
+ # iterate over the values and assign tasks by largest value for tasks
138
+ # not assigned already
139
+ remaining_tasks = list(range(len(self.tasks)))
140
+ fltr = self.upper_bound==1
141
+ while len(remaining_tasks) > 0 and solution[fltr].shape[0]>0:
142
+ largest = np.max(solution[fltr])
143
+ indices, = np.where(np.logical_and(fltr, solution == largest))
144
+ for idx in indices:
145
+ assignment = self.assignments[idx]
146
+ if assignment["task"] in remaining_tasks:
147
+ task = self.tasks[assignment["task"]]
148
+ resource_assignments[assignment["resource"]].append(task)
149
+ del remaining_tasks[remaining_tasks.index(assignment["task"])]
150
+ break
151
+ fltr = np.logical_and(fltr, solution < largest)
152
+ else:
153
+ # Use the restriction that a task cannot be assigned more than once
154
+ for j, task in enumerate(self.tasks):
155
+ highest = 0
156
+ best_resource = None
157
+ for a, assignment in zip(solution, self.assignments):
158
+ if assignment["task"] == j:
159
+ if a > highest:
160
+ highest = a
161
+ best_resource = assignment["resource"]
162
+ assert best_resource is not None, f"solution had no positive assignment values for {task}"
163
+ resource_assignments[best_resource].append(task)
164
+
165
+ return resource_assignments