eqc-models 0.10.0__tar.gz → 0.10.2__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 (123) hide show
  1. {eqc_models-0.10.0/eqc_models.egg-info → eqc_models-0.10.2}/PKG-INFO +1 -1
  2. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/dependencies.rst +1 -0
  3. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/eqc_models.rst +8 -0
  4. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/__init__.py +2 -1
  5. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/algorithms/penaltymultiplier.py +20 -7
  6. eqc_models-0.10.2/eqc_models/assignment/setpartition.py +4 -0
  7. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/base.py +44 -4
  8. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/polynomial.py +7 -3
  9. eqc_models-0.10.2/eqc_models/combinatorics/__init__.py +6 -0
  10. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/combinatorics/setcover.py +1 -0
  11. {eqc_models-0.10.0/eqc_models/assignment → eqc_models-0.10.2/eqc_models/combinatorics}/setpartition.py +2 -0
  12. eqc_models-0.10.2/eqc_models/graph/__init__.py +6 -0
  13. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/graph/maxkcut.py +26 -68
  14. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/solvers/qciclient.py +11 -2
  15. {eqc_models-0.10.0 → eqc_models-0.10.2/eqc_models.egg-info}/PKG-INFO +1 -1
  16. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models.egg-info/SOURCES.txt +2 -0
  17. eqc_models-0.10.0/eqc_models/graph/__init__.py +0 -5
  18. {eqc_models-0.10.0 → eqc_models-0.10.2}/.gitlab-ci.yml +0 -0
  19. {eqc_models-0.10.0 → eqc_models-0.10.2}/LICENSE.txt +0 -0
  20. {eqc_models-0.10.0 → eqc_models-0.10.2}/MANIFEST.in +0 -0
  21. {eqc_models-0.10.0 → eqc_models-0.10.2}/README.md +0 -0
  22. {eqc_models-0.10.0 → eqc_models-0.10.2}/compile_extensions.py +0 -0
  23. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/Makefile +0 -0
  24. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/basic.css +0 -0
  25. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/css/badge_only.css +0 -0
  26. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/css/theme.css +0 -0
  27. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/custom.css +0 -0
  28. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/file.png +0 -0
  29. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/minus.png +0 -0
  30. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/plus.png +0 -0
  31. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/pygments.css +0 -0
  32. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/build/html/_static/white_logo.png +0 -0
  33. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/make.bat +0 -0
  34. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/_static/custom.css +0 -0
  35. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/_static/white_logo.png +0 -0
  36. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/conf.py +0 -0
  37. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/index.rst +0 -0
  38. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/modules.rst +0 -0
  39. {eqc_models-0.10.0 → eqc_models-0.10.2}/docs/source/usage.rst +0 -0
  40. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/algorithms/__init__.py +0 -0
  41. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/algorithms/base.py +0 -0
  42. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/allocation/__init__.py +0 -0
  43. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/allocation/allocation.py +0 -0
  44. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/allocation/portbase.py +0 -0
  45. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/allocation/portmomentum.py +0 -0
  46. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/assignment/__init__.py +0 -0
  47. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/assignment/qap.py +0 -0
  48. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/__init__.py +0 -0
  49. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/constraints.py +0 -0
  50. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/operators.py +0 -0
  51. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/polyeval.c +0 -0
  52. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/polyeval.pyx +0 -0
  53. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/base/quadratic.py +0 -0
  54. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/decoding.py +0 -0
  55. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/graph/base.py +0 -0
  56. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/graph/hypergraph.py +0 -0
  57. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/graph/maxcut.py +0 -0
  58. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/graph/partition.py +0 -0
  59. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/__init__.py +0 -0
  60. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/classifierbase.py +0 -0
  61. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/classifierqboost.py +0 -0
  62. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/classifierqsvm.py +0 -0
  63. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/clustering.py +0 -0
  64. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/clusteringbase.py +0 -0
  65. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/cvqboost_hamiltonian.pyx +0 -0
  66. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/cvqboost_hamiltonian_c_func.c +0 -0
  67. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/cvqboost_hamiltonian_c_func.h +0 -0
  68. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/decomposition.py +0 -0
  69. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/forecast.py +0 -0
  70. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/forecastbase.py +0 -0
  71. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/regressor.py +0 -0
  72. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/regressorbase.py +0 -0
  73. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/ml/reservoir.py +0 -0
  74. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/sequence/__init__.py +0 -0
  75. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/sequence/tsp.py +0 -0
  76. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/solvers/__init__.py +0 -0
  77. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/utilities/__init__.py +0 -0
  78. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/utilities/fileio.py +0 -0
  79. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/utilities/polynomial.py +0 -0
  80. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models/utilities/qplib.py +0 -0
  81. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models.egg-info/dependency_links.txt +0 -0
  82. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models.egg-info/requires.txt +0 -0
  83. {eqc_models-0.10.0 → eqc_models-0.10.2}/eqc_models.egg-info/top_level.txt +0 -0
  84. {eqc_models-0.10.0 → eqc_models-0.10.2}/pyproject.toml +0 -0
  85. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/binary_job_example.py +0 -0
  86. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/c6h6_graph_clustering.py +0 -0
  87. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/clustering.py +0 -0
  88. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/continuous_job_example.py +0 -0
  89. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/duality_example.py +0 -0
  90. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/graph_clustering.py +0 -0
  91. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/graph_partitioning.py +0 -0
  92. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/hamiltonian_to_polynomial.py +0 -0
  93. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/hypergraph.py +0 -0
  94. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/integer_job_example.py +0 -0
  95. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/karate_graph_clustering.py +0 -0
  96. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/lin_reg_dirac3.py +0 -0
  97. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/mackey_glass_cell_production_series.csv +0 -0
  98. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/pca_iris_dirac3.py +0 -0
  99. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/port_opt_dirac3.py +0 -0
  100. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/qboost_iris_dirac3.py +0 -0
  101. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/qplib_benchmark_config.py +0 -0
  102. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/qplib_reader.py +0 -0
  103. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/qplib_runner.py +0 -0
  104. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/qsvm_iris_dirac3.py +0 -0
  105. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/reservoir_forecast.py +0 -0
  106. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/rundoctests.py +0 -0
  107. {eqc_models-0.10.0 → eqc_models-0.10.2}/scripts/utils.py +0 -0
  108. {eqc_models-0.10.0 → eqc_models-0.10.2}/setup.cfg +0 -0
  109. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/doctest_base.py +0 -0
  110. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testallocationmodel.py +0 -0
  111. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testconstraint.py +0 -0
  112. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testcvqboost.py +0 -0
  113. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testeqcdirectsolver.py +0 -0
  114. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testgraphpartitionmodel.py +0 -0
  115. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testhypergraphmodel.py +0 -0
  116. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testmaxcutmodel.py +0 -0
  117. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testpolynomialmodel.py +0 -0
  118. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testqapmodel.py +0 -0
  119. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testqciclientsolver.py +0 -0
  120. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testquadraticmodel.py +0 -0
  121. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testsetcovermodel.py +0 -0
  122. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testsetpartitionmodel.py +0 -0
  123. {eqc_models-0.10.0 → eqc_models-0.10.2}/test/testtsp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: eqc-models
3
- Version: 0.10.0
3
+ Version: 0.10.2
4
4
  Summary: Optimization and ML modeling package targeting EQC devices
5
5
  Author: Quantum Computing Inc.
6
6
  Author-email: support@quantumcomputinginc.com
@@ -9,5 +9,6 @@ Packages
9
9
  - ``numpy>=1.22.1, <2``
10
10
  - ``networkx>=2.6.3, <3``
11
11
  - ``pandas>=2.1.0``
12
+ - ``scikit-learn>=1.2.1``
12
13
  - ``qci-client>=4.3.0, <5``
13
14
  - ``emucore-direct==1.0.6``
@@ -38,6 +38,14 @@ eqc\_models.assignment
38
38
  :undoc-members:
39
39
  :show-inheritance:
40
40
 
41
+ eqc\_models.combinatorics
42
+ --------------------------
43
+
44
+ .. automodule:: eqc_models.combinatorics
45
+ :members:
46
+ :undoc-members:
47
+ :show-inheritance:
48
+
41
49
  eqc\_models.graph
42
50
  --------------------
43
51
 
@@ -8,8 +8,9 @@ from .base import QuadraticModel, PolynomialModel
8
8
  from .solvers import (Dirac1CloudSolver, Dirac3CloudSolver, Dirac3DirectSolver)
9
9
  from .allocation import AllocationModel, AllocationModelX, ResourceRuleEnum
10
10
  from .assignment import QAPModel
11
+ from .combinatorics import SetCoverModel, SetPartitionModel
11
12
 
12
13
  __all__ = ["QuadraticModel", "PolynomialModel", "Dirac1CloudSolver",
13
14
  "Dirac3CloudSolver", "AllocationModel", "AllocationModelX",
14
15
  "Dirac3DirectSolver", "ResourceRuleEnum",
15
- "QAPModel"]
16
+ "QAPModel", "SetPartitionModel", "SetCoverModel"]
@@ -33,6 +33,14 @@ class PenaltyMultiplierAlgorithm(Algorithm):
33
33
  penalties : List
34
34
  The values for penalties found at each algorithm iteration. A penalty of 0
35
35
  indicates algorithm termination.
36
+ penalty_threshold : float
37
+ This value is the cutoff for penalty values that are threated as 0. Default
38
+ is 1e-6.
39
+ progress_threshold : float
40
+ This value is the cutoff for checking for progress on reducing the penalty
41
+ value. The current penalty is compared to the average of the previous two
42
+ and if the absolute difference is less than this value, then the algorithm
43
+ stops, reporting progress has not been made. The default value is 1e-4.
36
44
  dynamic_range : List
37
45
  The values for the dynamic range of the unconstrained problem formulation,
38
46
  which is useful for identifying difficulty in representation of the problem
@@ -50,7 +58,7 @@ class PenaltyMultiplierAlgorithm(Algorithm):
50
58
  This example uses the quadratic assignment problem and the known multiplier to test
51
59
  the implementation of the algorithm.
52
60
 
53
- >>> from eqc_models.solvers.qciclient import Dirac3CloudSolver
61
+ >>> from eqc_models.solvers.qciclient import Dirac3IntegerCloudSolver
54
62
  >>> from eqc_models.assignment.qap import QAPModel
55
63
  >>> A = np.array([[0, 5, 8, 0, 1],
56
64
  ... [0, 0, 0, 10, 15],
@@ -68,10 +76,10 @@ class PenaltyMultiplierAlgorithm(Algorithm):
68
76
  ... [7, 5, 8, 5, 7],
69
77
  ... [1, 9, 2, 9, 2.]])
70
78
  >>> model = QAPModel(A, B, C)
71
- >>> solver = Dirac3CloudSolver() # must be configured with environment variables
79
+ >>> solver = Dirac3IntegerCloudSolver() # must be configured with environment variables
72
80
  >>> algo = PenaltyMultiplierAlgorithm(model, solver)
73
81
  >>> algo.upper_bound = 330.64
74
- >>> algo.run(relaxation_schedule=2, mean_photon_number=0.15, normalized_loss_rate=4, num_samples=5) # doctest: +ELLIPSIS
82
+ >>> algo.run(relaxation_schedule=2, num_samples=5) # doctest: +ELLIPSIS
75
83
  2... RUNNING... COMPLETED...
76
84
  >>> algo.alphas[-1] # doctest: +SKIP
77
85
  106.25
@@ -80,7 +88,8 @@ class PenaltyMultiplierAlgorithm(Algorithm):
80
88
 
81
89
  """
82
90
 
83
- def __init__(self, model : ConstraintModel, solver : ModelSolver):
91
+ def __init__(self, model : ConstraintModel, solver : ModelSolver, penalty_threshold:float=1e-6,
92
+ progress_threshold : float = 1e-4):
84
93
  self.model = model
85
94
  self.solver = solver
86
95
  # ub = np.max(model.quad_objective)
@@ -95,6 +104,8 @@ class PenaltyMultiplierAlgorithm(Algorithm):
95
104
  self.alphas = None
96
105
  self.dynamic_range = None
97
106
  self.responses = None
107
+ self.penalty_threshold = penalty_threshold
108
+ self.progress_threshold = progress_threshold
98
109
 
99
110
  @property
100
111
  def upper_bound(self) -> float:
@@ -141,7 +152,7 @@ class PenaltyMultiplierAlgorithm(Algorithm):
141
152
  else:
142
153
  penalty = None
143
154
 
144
- while penalty is None or penalty > 1e-6:
155
+ while penalty is None or penalty > self.penalty_threshold:
145
156
  log.info("NEW RUN")
146
157
  log.info("SETTING MULTIPLIER %f", alpha)
147
158
  model.penalty_multiplier = float(alpha)
@@ -162,8 +173,10 @@ class PenaltyMultiplierAlgorithm(Algorithm):
162
173
  energies.append(results["energies"][0])
163
174
  log.info("NEW SOLUTION OBJECTIVE %f LESS OFFSET %f ENERGY %f PENALTY %f",
164
175
  obj_val, less_offset, energies[-1], penalty)
165
- if obj_val < ub:
176
+ if penalty < self.penalty_threshold:
177
+ pass
178
+ elif obj_val < ub:
166
179
  alpha += (ub - obj_val) / penalty
167
- if abs(sum(penalties[-2:])/2-penalty) < 1e-4:
180
+ if penalty > self.penalty_threshold and abs(sum(penalties[-2:])/2-penalty) < self.progress_threshold:
168
181
  log.warn("SUFFICIENT PROGRESS NOT MADE FOR THREE ITERATIONS, QUITTING")
169
182
  break
@@ -0,0 +1,4 @@
1
+ from ..combinatorics import SetPartitionModel
2
+ import warnings
3
+
4
+ warnings.warn("import of SetPartitionModel through assignment is deprecated", DeprecationWarning)
@@ -42,12 +42,50 @@ class EqcModel:
42
42
  _H = None
43
43
  _machine_slacks = 0
44
44
 
45
- def decode(self, solution : np.ndarray) -> np.ndarray:
46
- """ Manipulate the solution to match the variable count """
45
+ def decode(self, solution : np.ndarray, from_encoding : str=None) -> np.ndarray:
46
+ """
47
+ Manipulate the solution to match the variable count. Optionally,
48
+ a log-encoded solution vector can be translated into a vector
49
+ with integral values.
50
+
51
+ Parameters
52
+ -----------
53
+
54
+ solution : np.ndarray
55
+ 1d solution vector
56
+
57
+ from_encoding : str
58
+ string indicating if the vector is an encoding of particular type. The
59
+ text 'qubo' is the only option that does anything for now.
60
+
61
+ >>> model = EqcModel()
62
+ >>> ub = np.array([1, 2, 5])
63
+ >>> model.upper_bound = ub
64
+ >>> model.machine_slacks = 1
65
+ >>> solution = np.array([1, 1, 1, 1, 0, 1, 0]) # this solution has a 3, which violates the upper bound, but decode doesn't care
66
+ >>> model.decode(solution, "qubo")
67
+ array([1, 3, 5])
68
+
69
+ """
47
70
 
71
+ solution = np.array(solution)
48
72
  # ignore any slacks that may have been added during encoding
49
- solution = solution[:-self.machine_slacks]
50
-
73
+ if self.machine_slacks > 0:
74
+ solution = solution[:-self.machine_slacks]
75
+ if from_encoding == "qubo":
76
+ ub = self.upper_bound
77
+ en = solution.shape[0]
78
+ assert en > 0
79
+ n = ub.shape[0]
80
+ linear_operator = np.zeros((n,en))
81
+ j = 0
82
+ for i in range(self.n):
83
+ m = int(np.floor(np.log2(ub[i]))+1)
84
+ bits = 2**np.arange(m)
85
+ assert j+m <= linear_operator.shape[1]+2, f"Invalid slice for i={i} {j}:{j+m}"
86
+ linear_operator[i,j:j+m] = bits
87
+ j += m
88
+ solution = (linear_operator@solution).astype(np.int64)
51
89
  return solution
52
90
 
53
91
  @property
@@ -64,6 +102,8 @@ class EqcModel:
64
102
  value = np.array(value)
65
103
  if (value != value.astype(np.int64)).any():
66
104
  raise ValueError("Upper bound values must be integer")
105
+ if (value==0).any():
106
+ raise ValueError("Zero values are not allowed as an upper_bound.")
67
107
  self._upper_bound = value.astype(np.int64)
68
108
 
69
109
  @property
@@ -117,12 +117,15 @@ class PolynomialModel(PolynomialMixin, EqcModel):
117
117
 
118
118
  @property
119
119
  def qubo(self) -> QUBO:
120
+ polynomial = self.polynomial
121
+ coefficients = polynomial.coefficients
122
+ indices = polynomial.indices
120
123
  try:
121
- if np.all([len(self.polynomial.indices[i]) == 2 for i in range(len(self.polynomial.indices))]):
124
+ if np.all([len(indices[i]) == 2 for i in range(len(indices))]):
122
125
  bin_n = 0
123
126
  bits = []
124
- C, J = self._quadratic_polynomial_to_qubo_coefficients(self.polynomial.coefficients,
125
- self.polynomial.indices, self.n)
127
+ C, J = self._quadratic_polynomial_to_qubo_coefficients(coefficients,
128
+ indices, self.n)
126
129
  # upper_bound is an array of the maximum values each variable can take
127
130
  upper_bound = self.upper_bound
128
131
  if np.sum(upper_bound) != upper_bound.shape[0]:
@@ -158,6 +161,7 @@ class PolynomialModel(PolynomialMixin, EqcModel):
158
161
  raise OperatorNotAvailableError("QUBO operator not available")
159
162
  except OperatorNotAvailableError as e:
160
163
  print(e)
164
+ raise
161
165
 
162
166
  def _quadratic_polynomial_to_qubo_coefficients(self, coefficients, indices, num_variables):
163
167
  """
@@ -0,0 +1,6 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+
3
+ from .setcover import SetCoverModel
4
+ from .setpartition import SetPartitionModel
5
+
6
+ __all__ = ["SetCoverModel", "SetPartitionModel"]
@@ -37,6 +37,7 @@ class SetCoverModel(ConstrainedQuadraticModel):
37
37
  List of weights where each weight is the cost of choosing the subset
38
38
  corresponding to the index of the weight.
39
39
 
40
+
40
41
  >>> X = [set(['A', 'B']), set(['B', 'C']), set(['C'])]
41
42
  >>> weights = [2, 2, 1]
42
43
  >>> model = SetCoverModel(X, weights)
@@ -86,6 +86,8 @@ class SetPartitionModel(ConstrainedPolynomialModel):
86
86
  b = np.ones((A.shape[0],))
87
87
  n = A.shape[1]
88
88
 
89
+ self.upper_bound = np.ones((n,), np.int32)
90
+
89
91
  # Define the linear objective function based on subset weights
90
92
  self.linear_objective = np.array(weights).reshape((n, 1))
91
93
  self.quad_objective = np.zeros((n, n)) #np.zeros_like(J)
@@ -0,0 +1,6 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+
3
+ from .maxcut import MaxCutModel
4
+ from .partition import GraphPartitionModel
5
+
6
+ __all__ = ["MaxCutModel", "GraphPartitionModel"]
@@ -1,20 +1,24 @@
1
1
  # (C) Quantum Computing Inc., 2024.
2
+ from typing import Tuple
2
3
  import numpy as np
3
4
  import networkx as nx
4
5
  from .base import NodeModel
6
+ from eqc_models.base.quadratic import ConstrainedQuadraticModel
5
7
 
6
-
7
- class MaxKCutModel(NodeModel):
8
+ class MaxKCutModel(ConstrainedQuadraticModel):
9
+ _objective = None
8
10
 
9
11
  def __init__(self, G : nx.Graph, k : int):
10
- super(MaxKCutModel, self).__init__(G)
12
+ self.G = G
11
13
  self.k = k
12
- self.lhs = None
13
- self.rhs = None
14
- self._objective = None
15
- self._J = None
16
- self._C = None
17
-
14
+ A, b = self._build_constraints()
15
+ c, J = self.costFunction()
16
+ ConstrainedQuadraticModel.__init__(self, c, J, A, b)
17
+ if k < 3:
18
+ raise ValueError("k must be greater than 2")
19
+ n = len(G.nodes) * k
20
+ self.upper_bound = np.ones((n,))
21
+
18
22
  def decode(self, solution: np.ndarray) -> np.ndarray:
19
23
  """ Override the default decoding to use a the max cut metric to determine a solution """
20
24
 
@@ -22,7 +26,8 @@ class MaxKCutModel(NodeModel):
22
26
  # rather than the same cutoff per node, use the max value per partition
23
27
  decoded_solution = np.zeros_like(solution, dtype=np.int32)
24
28
  k = self.k
25
- for i, u in enumerate(self.variables):
29
+ G = self.G
30
+ for i, u in enumerate(G.nodes):
26
31
  idx = slice(k*i, k*(i+1))
27
32
  spins = solution[idx]
28
33
  mx = np.max(spins)
@@ -35,9 +40,9 @@ class MaxKCutModel(NodeModel):
35
40
  def partition(self, solution):
36
41
  """ Return a dictionary with the partition number of each node """
37
42
  k = self.k
38
- n = len(self.variables)
43
+ G = self.G
39
44
  partition_num = {}
40
- for i, u in enumerate(self.variables):
45
+ for i, u in enumerate(G.nodes):
41
46
  for j in range(k):
42
47
  if solution[i*k+j] == 1:
43
48
  partition_num[u] = j+1
@@ -50,10 +55,10 @@ class MaxKCutModel(NodeModel):
50
55
  cut_size += 1
51
56
  return cut_size
52
57
 
53
- def _build_objective(self):
58
+ def costFunction(self) -> Tuple:
54
59
 
55
- node_map = self.variables
56
60
  G = self.G
61
+ node_map = list(G.nodes)
57
62
  m = len(G.nodes)
58
63
  n = self.k * m
59
64
  # construct the quadratic portion of the objective
@@ -70,12 +75,12 @@ class MaxKCutModel(NodeModel):
70
75
  idx1 = ibase + incr1
71
76
  idx2 = jbase + incr2
72
77
  objective[idx1, idx2] += -1
73
- self._objective = (np.zeros((n, 1)), objective)
78
+ return (np.zeros((n, 1)), objective)
74
79
 
75
80
  def _build_constraints(self):
76
81
 
77
- node_map = self.variables
78
82
  G = self.G
83
+ node_map = list(G.nodes)
79
84
  m = len(G.nodes)
80
85
  n = self.k * m
81
86
 
@@ -86,78 +91,31 @@ class MaxKCutModel(NodeModel):
86
91
  i = node_map.index(u)
87
92
  ibase = i * self.k
88
93
  A[i, ibase:ibase+self.k] = 1
89
- self.lhs = A
90
- self.rhs = b
91
-
92
- def build(self, multiplier=None):
93
- """ Create the constraints and objective and Hamiltonian """
94
-
95
- # there are k * m variables in this problem where m is the number of nodes in the graph
96
- node_map = self.variables
97
- G = self.G
98
- m = len(G.nodes)
99
- n = self.k * m
100
- self.upper_bound = np.ones((n,))
101
-
102
- self._build_objective()
103
- if multiplier is None:
104
- multiplier = np.max(np.abs(self._objective[1]))
105
- self._build_constraints()
106
-
107
- self._C, self._J = self.buildH(multiplier)
108
- self.sum_constraint = m
109
-
110
- def buildH(self, multiplier):
111
- """ Combine the objective and penalties using the multiplier """
112
-
113
- objC, objJ = self.objective
114
- lhs, rhs = self.constraints
115
- Pq = lhs.T@lhs
116
- Pl = -2 * rhs.T@lhs
117
- offset = rhs.T@rhs
118
- n = self.n
119
- J = np.zeros((n, n), np.float32)
120
- C = np.zeros([n, 1], np.float32)
121
- C += objC
122
- J[:,:] += objJ
123
- C += multiplier * Pl.reshape((n, 1))
124
- J[:,:] += multiplier * Pq
125
- return C, J
94
+ return A, b
126
95
 
127
96
  @property
128
97
  def constraints(self):
129
98
  """ Return LHS, RHS in numpy matrix format """
130
- if self.rhs is None:
131
- self.build()
99
+
132
100
  return self.lhs, self.rhs
133
101
 
134
102
  @property
135
103
  def objective(self):
136
104
  """ Return the quadratic objective as NxN+1 matrix """
137
105
 
138
- if self._objective is None:
139
- self.build()
140
106
  return self._objective
141
107
 
142
- @property
143
- def H(self):
144
- """ Return the Hamiltonian as parts C, J """
145
-
146
- if self._C is None:
147
- self.build()
148
- return self._C, self._J
149
-
150
108
  class WeightedMaxKCutModel(MaxKCutModel):
151
109
 
152
110
  def __init__(self, G: nx.Graph, k: int, weight_label : str = "weight"):
153
- super().__init__(G, k)
111
+ super(WeightedMaxCutModel).__init__(G, k)
154
112
 
155
113
  self.weight_label = weight_label
156
114
 
157
115
  def _build_objective(self):
158
116
 
159
- node_map = self.variables
160
117
  G = self.G
118
+ node_map = list(G.nodes)
161
119
  m = len(G.nodes)
162
120
  n = self.k * m
163
121
  # construct the quadratic portion of the objective
@@ -174,7 +132,7 @@ class WeightedMaxKCutModel(MaxKCutModel):
174
132
  idx1 = ibase + incr1
175
133
  idx2 = jbase + incr2
176
134
  objective[idx1, idx2] += G[u][v][self.weight_label]
177
- self._objective = (np.zeros((n, 1)), objective)
135
+ return (np.zeros((n, 1)), objective)
178
136
 
179
137
  def getCutSize(self, partition):
180
138
  cut_size = 0
@@ -68,10 +68,19 @@ class QciClientMixin:
68
68
  client = QciClient(url=self.url, api_token=self.api_token)
69
69
  return client
70
70
 
71
+ def getMetrics(self, job_id : str) -> Dict:
72
+ """
73
+ Returns a dictionary containing the job metrics.
74
+
75
+ """
76
+ client = self.client
77
+ metrics = client.get_job_metrics(job_id=job_id)
78
+ return metrics
79
+
71
80
  class Dirac1Mixin:
72
81
  sampler_type = "dirac-1"
73
82
  requires_operator = "qubo"
74
- max_upper_bound = 1
83
+ max_upper_bound = 205 # log encoding beyond this level has inherent issues
75
84
  job_params_names = ["num_samples", "alpha", "atol"]
76
85
 
77
86
  class QuboSolverMixin:
@@ -141,7 +150,7 @@ class Dirac3Mixin:
141
150
  poly_indices = polynomial.indices
142
151
  data = []
143
152
  # must find these attributes of the polynomial before uploading
144
- max_degree = 0
153
+ max_degree = 2
145
154
  min_degree = len(poly_indices[-1])
146
155
  num_variables = 0
147
156
  for i in range(len(poly_coeffs)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: eqc-models
3
- Version: 0.10.0
3
+ Version: 0.10.2
4
4
  Summary: Optimization and ML modeling package targeting EQC devices
5
5
  Author: Quantum Computing Inc.
6
6
  Author-email: support@quantumcomputinginc.com
@@ -48,7 +48,9 @@ eqc_models/base/polyeval.c
48
48
  eqc_models/base/polyeval.pyx
49
49
  eqc_models/base/polynomial.py
50
50
  eqc_models/base/quadratic.py
51
+ eqc_models/combinatorics/__init__.py
51
52
  eqc_models/combinatorics/setcover.py
53
+ eqc_models/combinatorics/setpartition.py
52
54
  eqc_models/graph/__init__.py
53
55
  eqc_models/graph/base.py
54
56
  eqc_models/graph/hypergraph.py
@@ -1,5 +0,0 @@
1
- # (C) Quantum Computing Inc., 2024.
2
-
3
- from .maxcut import MaxCutModel
4
-
5
- __all__ = ["MaxCutModel"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes