eqc-models 0.12.0__py3-none-any.whl → 0.14.0__py3-none-any.whl

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 (72) hide show
  1. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/base.py +11 -0
  2. eqc_models-0.14.0.data/platlib/eqc_models/base/binaries.py +33 -0
  3. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/constraints.py +39 -0
  4. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/polyeval.c +443 -267
  5. eqc_models-0.14.0.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
  6. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/polynomial.py +21 -2
  7. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/results.py +81 -1
  8. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/__init__.py +2 -0
  9. eqc_models-0.14.0.data/platlib/eqc_models/graph/rcshortestpath.py +81 -0
  10. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/shortestpath.py +52 -19
  11. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/classifierbase.py +0 -5
  12. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/classifierqboost.py +115 -72
  13. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/clustering.py +29 -1
  14. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/clusteringbase.py +29 -7
  15. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/decomposition.py +50 -11
  16. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/regressor.py +14 -0
  17. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/regressorbase.py +24 -6
  18. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/reservoir.py +17 -2
  19. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/solvers/__init__.py +9 -1
  20. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/solvers/eqcdirect.py +27 -6
  21. eqc_models-0.14.0.data/platlib/eqc_models/solvers/mip.py +115 -0
  22. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/solvers/qciclient.py +11 -22
  23. eqc_models-0.14.0.data/platlib/eqc_models/solvers/responselog.py +47 -0
  24. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/utilities/__init__.py +2 -1
  25. eqc_models-0.14.0.data/platlib/eqc_models/utilities/general.py +83 -0
  26. {eqc_models-0.12.0.dist-info → eqc_models-0.14.0.dist-info}/METADATA +10 -9
  27. eqc_models-0.14.0.dist-info/RECORD +70 -0
  28. eqc_models-0.12.0.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
  29. eqc_models-0.12.0.dist-info/RECORD +0 -65
  30. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/compile_extensions.py +0 -0
  31. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/__init__.py +0 -0
  32. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/algorithms/__init__.py +0 -0
  33. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/algorithms/base.py +0 -0
  34. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/algorithms/penaltymultiplier.py +0 -0
  35. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/allocation/__init__.py +0 -0
  36. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/allocation/allocation.py +0 -0
  37. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/allocation/portbase.py +0 -0
  38. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/allocation/portmomentum.py +0 -0
  39. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/assignment/__init__.py +0 -0
  40. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/assignment/qap.py +0 -0
  41. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/assignment/resource.py +0 -0
  42. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/assignment/setpartition.py +0 -0
  43. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/__init__.py +0 -0
  44. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/operators.py +0 -0
  45. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/polyeval.pyx +0 -0
  46. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/base/quadratic.py +0 -0
  47. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/combinatorics/__init__.py +0 -0
  48. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/combinatorics/setcover.py +0 -0
  49. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/combinatorics/setpartition.py +0 -0
  50. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/decoding.py +0 -0
  51. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/base.py +0 -0
  52. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/hypergraph.py +0 -0
  53. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/maxcut.py +0 -0
  54. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/maxkcut.py +0 -0
  55. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/graph/partition.py +0 -0
  56. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/__init__.py +0 -0
  57. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/classifierqsvm.py +0 -0
  58. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian.pyx +0 -0
  59. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian_c_func.c +0 -0
  60. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian_c_func.h +0 -0
  61. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/forecast.py +0 -0
  62. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/ml/forecastbase.py +0 -0
  63. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/process/base.py +0 -0
  64. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/process/mpc.py +0 -0
  65. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/sequence/__init__.py +0 -0
  66. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/sequence/tsp.py +0 -0
  67. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/utilities/fileio.py +0 -0
  68. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/utilities/polynomial.py +0 -0
  69. {eqc_models-0.12.0.data → eqc_models-0.14.0.data}/platlib/eqc_models/utilities/qplib.py +0 -0
  70. {eqc_models-0.12.0.dist-info → eqc_models-0.14.0.dist-info}/WHEEL +0 -0
  71. {eqc_models-0.12.0.dist-info → eqc_models-0.14.0.dist-info}/licenses/LICENSE.txt +0 -0
  72. {eqc_models-0.12.0.dist-info → eqc_models-0.14.0.dist-info}/top_level.txt +0 -0
@@ -45,6 +45,12 @@ class LinearRegression(RegressorBase):
45
45
 
46
46
  num_samples: Number of samples used by Dirac-3; default: 1.
47
47
 
48
+ solver_access: Solver access type: cloud or direct; default: cloud.
49
+
50
+ ip_addr: IP address of the device when direct access is used; default: None.
51
+
52
+ port: Port number of the device when direct access is used; default: None.
53
+
48
54
  l2_reg_coef: L2 regularization penalty multiplier; default: 0.
49
55
 
50
56
  alpha: A penalty multiplier to ensure the correct sign of a
@@ -72,13 +78,21 @@ class LinearRegression(RegressorBase):
72
78
  self,
73
79
  relaxation_schedule=2,
74
80
  num_samples=1,
81
+ solver_access="cloud",
82
+ ip_addr=None,
83
+ port=None,
75
84
  l2_reg_coef=0,
76
85
  alpha=0,
77
86
  ):
78
87
  super(LinearRegression).__init__()
79
88
 
89
+ assert solver_access in ["cloud", "direct"]
90
+
80
91
  self.relaxation_schedule = relaxation_schedule
81
92
  self.num_samples = num_samples
93
+ self.solver_access = solver_access
94
+ self.ip_addr = ip_addr
95
+ self.port = port
82
96
  self.l2_reg_coef = l2_reg_coef
83
97
  self.alpha = alpha
84
98
  self.params = None
@@ -11,19 +11,27 @@ import numpy as np
11
11
 
12
12
  from eqc_models import QuadraticModel
13
13
  from eqc_models.solvers.qciclient import Dirac3CloudSolver
14
-
14
+ from eqc_models.solvers.eqcdirect import Dirac3DirectSolver
15
15
 
16
16
  class RegressorBase(QuadraticModel):
17
17
  def __init__(
18
18
  self,
19
19
  relaxation_schedule=2,
20
20
  num_samples=1,
21
+ solver_access="cloud",
22
+ ip_addr=None,
23
+ port=None,
21
24
  ):
22
25
 
23
26
  super(self).__init__(None, None, None)
27
+
28
+ assert solver_access in ["cloud", "direct"]
24
29
 
25
30
  self.relaxation_schedule = relaxation_schedule
26
31
  self.num_samples = num_samples
32
+ self.solver_access = solver_access
33
+ self.ip_addr = ip_addr
34
+ self.port = port
27
35
  self.params = None
28
36
 
29
37
  def predict(self, X: np.array):
@@ -51,18 +59,28 @@ class RegressorBase(QuadraticModel):
51
59
  return
52
60
 
53
61
  def solve(self):
54
- solver = Dirac3CloudSolver()
62
+ if self.solver_access == "direct":
63
+ solver = Dirac3DirectSolver()
64
+ solver.connect(self.ip_addr, self.port)
65
+ else:
66
+ solver = Dirac3CloudSolver()
67
+
55
68
  response = solver.solve(
56
69
  self,
57
70
  sum_constraint=self._sum_constraint,
58
71
  relaxation_schedule=self.relaxation_schedule,
59
- solution_precision=1,
60
72
  num_samples=self.num_samples,
61
73
  )
62
74
 
63
- min_id = np.argmin(response["results"]["energies"])
64
-
65
- sol = response["results"]["solutions"][min_id]
75
+ if self.solver_access == "cloud":
76
+ energies = response["results"]["energies"]
77
+ solutions = response["results"]["solutions"]
78
+ elif self.solver_access == "direct":
79
+ energies = response["energy"]
80
+ solutions = response["solution"]
81
+
82
+ min_id = np.argmin(energies)
83
+ sol = solutions[min_id]
66
84
 
67
85
  print(response)
68
86
 
@@ -1,5 +1,20 @@
1
- import numpy as np
2
- from emucore_direct.client import EmuCoreClient
1
+ # (C) Quantum Computing Inc., 2025.
2
+ import logging
3
+
4
+ try:
5
+ from emucore_direct.client import EmuCoreClient
6
+ except ModuleNotFoundError:
7
+ # Only warn here to try to disrupt package behavior as least as possible.
8
+ logging.warning("emucore-direct package not available")
9
+
10
+ class EmuCoreClient:
11
+ """This is a stub for unsupported EmuCoreClient."""
12
+
13
+ def __init__(*_, **__) -> None:
14
+ """Raise exception when client cannot be created."""
15
+ raise ModuleNotFoundError(
16
+ "emucore-direct package not available, likely because Python version is 3.11+"
17
+ )
3
18
 
4
19
  # Parameters
5
20
  VBIAS = 0.31
@@ -2,7 +2,15 @@
2
2
  from .eqcdirect import Dirac3DirectSolver
3
3
  from .qciclient import (Dirac1CloudSolver, Dirac3CloudSolver, QciClientSolver,
4
4
  Dirac3IntegerCloudSolver, Dirac3ContinuousCloudSolver)
5
+ from .mip import MIPMixin
6
+
7
+ class Dirac3MIPCloudSolver(MIPMixin, Dirac3ContinuousCloudSolver):
8
+ pass
9
+
10
+ class Dirac3MIPDirectSolver(MIPMixin, Dirac3DirectSolver):
11
+ pass
5
12
 
6
13
  __all__ = ["Dirac3DirectSolver", "Dirac1CloudSolver", "Dirac3CloudSolver",
7
14
  "EqcDirectSolver", "QciClientSolver", "Dirac3IntegerCloudSolver",
8
- "Dirac3ContinuousCloudSolver"]
15
+ "Dirac3ContinuousCloudSolver", "MILPMixin",
16
+ "Dirac3MIPCloudSolver", "Dirac3MIPDirectSolver"]
@@ -1,8 +1,23 @@
1
1
  # (C) Quantum Computing Inc., 2025.
2
2
  import logging
3
- import numpy as np
4
- from eqc_direct.client import EqcClient
5
- from eqc_models.base.base import ModelSolver
3
+ from typing import Dict
4
+
5
+ try:
6
+ from eqc_direct.client import EqcClient
7
+ except ModuleNotFoundError:
8
+ # Only warn here to try to disrupt package behavior as least as possible.
9
+ logging.warning("eqc-direct package not available")
10
+
11
+ class EqcClient:
12
+ """This is a stub for unsupported EqcClient."""
13
+
14
+ def __init__(*_, **__) -> None:
15
+ """Raise exception when client cannot be created."""
16
+ raise ModuleNotFoundError(
17
+ "eqc-direct package not available, likely because Python version is 3.11+"
18
+ )
19
+
20
+ from eqc_models.base.base import EqcModel, ModelSolver
6
21
  from eqc_models.base.results import SolutionResults
7
22
 
8
23
  log = logging.getLogger(name=__name__)
@@ -19,7 +34,6 @@ class Dirac3DirectSolver(ModelSolver):
19
34
  sum_constraint,
20
35
  relaxation_schedule=1,
21
36
  num_samples=1,
22
- solution_precision=None,
23
37
  mean_photon_number=None,
24
38
  quantum_fluctuation_coefficient=None,
25
39
  ):
@@ -30,7 +44,8 @@ class Dirac3DirectSolver(ModelSolver):
30
44
  for i in range(len(indices)):
31
45
  if max(indices[i]) > num_variables:
32
46
  num_variables = max(indices[i])
33
-
47
+ # add machine slacks
48
+ num_variables += model.machine_slacks
34
49
  print("Num variables:", num_variables)
35
50
 
36
51
  client = self.client
@@ -49,7 +64,6 @@ class Dirac3DirectSolver(ModelSolver):
49
64
  lock_id=lock_id,
50
65
  relaxation_schedule=relaxation_schedule,
51
66
  sum_constraint=sum_constraint,
52
- solution_precision=solution_precision,
53
67
  mean_photon_number=mean_photon_number,
54
68
  quantum_fluctuation_coefficient=quantum_fluctuation_coefficient,
55
69
  )
@@ -68,4 +82,11 @@ class Dirac3DirectSolver(ModelSolver):
68
82
 
69
83
  @property
70
84
  def client(self) -> EqcClient:
85
+ """Return a new client from eqc-direct based on class config."""
86
+
71
87
  return EqcClient(self.ip_addr, self.port, cert_file=self.cert_file)
88
+
89
+ def makeResults(self, model : EqcModel, response : Dict):
90
+ """ Builds the results object """
91
+
92
+ return SolutionResults.from_eqcdirect_response(model, response, self)
@@ -0,0 +1,115 @@
1
+ import logging
2
+ import numpy as np
3
+ from eqc_models.base.base import EqcModel
4
+ from eqc_models.base.binaries import make_binary_penalty
5
+ from eqc_models.base.polynomial import PolynomialModel
6
+
7
+ log = logging.getLogger(name=__name__)
8
+
9
+ class MIPMixin:
10
+ """
11
+ Implements a solve method which intercepts the operator and builds a new
12
+ model with added terms for restriction of discrete variables with an
13
+ upper bound of 1 to take on only values 0 or 1 at minima.
14
+
15
+ Following the submission of a new model with the added penalties,
16
+ the solutions are updated to exclude added slack variables.
17
+
18
+ This is only supported with continuous-capable devices.
19
+
20
+ """
21
+
22
+ def solve(self, model : EqcModel, *args, **kwargs):
23
+ if model.is_discrete is None:
24
+ raise ValueError("Model solved with an MIP solver must have certain variables labeled discrete")
25
+ elif len([b for b in model.is_discrete if b])==0:
26
+ raise ValueError("Model solved with an MIP solver must have certain variables labeled discrete")
27
+ if kwargs.get("sum_constraint", None) is None:
28
+ raise ValueError("sum_constraint must be specified for MIP model sampling")
29
+ # get a polynomial
30
+ poly = model.polynomial
31
+ if hasattr(poly.coefficients, "tolist"):
32
+ coefficients = poly.coefficients.tolist()
33
+ else:
34
+ coefficients = list(poly.coefficients)
35
+ indices = poly.indices
36
+ old_n = model.n
37
+ log.debug("Model coefficients %d", len(coefficients))
38
+ log.debug("Model indices %d", len(indices))
39
+ log.debug("Model size %d", old_n)
40
+ if "penalty_multiplier" in kwargs:
41
+ penalty_multiplier = kwargs["penalty_multiplier"]
42
+ del kwargs["penalty_multiplier"]
43
+ else:
44
+ penalty_multiplier = getattr(model, "penalty_multiplier", 1)
45
+ log.debug("Binary enforcement penalty multiplier %f", penalty_multiplier)
46
+ bin_slacks = []
47
+ offset = 0
48
+ variables = model.variables
49
+ addtl_upper_bound = []
50
+ for i, v in enumerate(model.variables):
51
+ if model.is_discrete[i]:
52
+ bin_slacks.append(f"bin_slacks_{v}")
53
+ penalty_coeff, penalty_indices, penalty_offset = make_binary_penalty(i+1, len(variables)+len(bin_slacks), penalty_multiplier=penalty_multiplier)
54
+ log.debug("Adding penalties coeff: %s indices %s offset %s", penalty_coeff, penalty_indices, penalty_offset)
55
+ coefficients += penalty_coeff
56
+ indices += penalty_indices
57
+ offset += penalty_offset
58
+ addtl_upper_bound.append(1)
59
+ assert len(coefficients) == len(indices)
60
+ variables = variables + bin_slacks
61
+ log.debug("New model binary penalty slacks: %s", bin_slacks)
62
+ new_model = PolynomialModel(coefficients, indices)
63
+ new_model.variables = variables
64
+ log.debug("New model variables: %s", variables)
65
+ new_model.upper_bound = np.array(model.upper_bound.tolist() + addtl_upper_bound)
66
+ new_model.machine_slacks = model.machine_slacks
67
+ coefficients, indices = new_model.H
68
+ log.debug("New model coefficients %d", len(coefficients))
69
+ log.debug("New model indices %d", len(indices))
70
+ log.debug("New model size: %d", new_model.n)
71
+ response = super().solve(new_model, *args, **kwargs)
72
+ # translate the response into results
73
+ results = self.makeResults(new_model, response)
74
+ log.debug(results)
75
+ # update the results to relect the original model
76
+ solutions = results.solutions
77
+ for i in range(len(solutions)):
78
+ log.debug("SolutionResults solution: %s", solutions[i])
79
+ if hasattr(model, "evaluateObjective"):
80
+ new_objectives = []
81
+ else:
82
+ new_objectives = None
83
+ if hasattr(model, "evaluatePenalties"):
84
+ new_penalties = []
85
+ else:
86
+ new_penalties = None
87
+ new_solutions = []
88
+ new_energies = []
89
+ num_vars = len(solutions[0]) - len(bin_slacks)
90
+ log.debug("Num_vars %f", num_vars)
91
+ machine_slacks = model.machine_slacks
92
+ for i, solution in enumerate(solutions):
93
+ log.debug("%d - Raw solution %s", i, solution)
94
+ new_sol = [v for v in solution[:num_vars]]
95
+ new_sol = np.array(new_sol)
96
+ if machine_slacks > 0:
97
+ new_sol[-machine_slacks:] = solution[-machine_slacks:]
98
+ log.debug("%d - New solution %s", i, new_sol)
99
+ if new_objectives is not None:
100
+ try:
101
+ new_objectives.append(model.evaluateObjective(new_sol))
102
+ except NotImplementedError as err:
103
+ pass
104
+ if new_penalties is not None:
105
+ try:
106
+ new_penalties.append(model.evaluatePenalties(new_sol))
107
+ except NotImplementedError as err:
108
+ pass
109
+ new_solutions.append(new_sol)
110
+ new_energies.append(model.evaluate(new_sol))
111
+ results.solutions = new_solutions
112
+ results.penalties = new_penalties
113
+ results.objectives = new_objectives
114
+ results.energies = new_energies
115
+ return results
@@ -6,6 +6,7 @@ import numpy as np
6
6
  from qci_client import QciClient
7
7
  from eqc_models.base.base import ModelSolver, EqcModel
8
8
  from eqc_models.base.operators import OperatorNotAvailableError
9
+ from eqc_models.base.results import SolutionResults
9
10
 
10
11
  log = logging.getLogger(name=__name__)
11
12
 
@@ -77,6 +78,11 @@ class QciClientMixin:
77
78
  metrics = client.get_job_metrics(job_id=job_id)
78
79
  return metrics
79
80
 
81
+ def makeResults(self, model : EqcModel, response : Dict) -> SolutionResults:
82
+ """ Build the results object """
83
+
84
+ return SolutionResults.from_cloud_response(model, response, self)
85
+
80
86
  class Dirac1Mixin:
81
87
  sampler_type = "dirac-1"
82
88
  requires_operator = "qubo"
@@ -125,7 +131,6 @@ class Dirac3Mixin:
125
131
  max_upper_bound = 10000
126
132
  job_params_names = [
127
133
  "num_samples",
128
- "solution_precision",
129
134
  "relaxation_schedule",
130
135
  "mean_photon_number",
131
136
  "quantum_fluctuation_coefficient",
@@ -427,10 +432,9 @@ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
427
432
  Dirac3CloudSolver is a class that encapsulates the different calls to Qatalyst
428
433
  for Dirac-3 jobs. Currently, there are two different jobs, one for integer and
429
434
  another for continuous solutions. Calling the solve method with different arguments
430
- controls which job is submitted. The continuous job requires :code:`sum_constraint`
431
- and optionally takes the :code:`solution_precision` argument. The integer job
432
- does not accept either of these parameters, so specifying a sum constraint forces
433
- the job type to be continuous and not specifying it results in the integer job being
435
+ controls which job is submitted. The continuous job requires :code:`sum_constraint`.
436
+ The integer job does not accept this parameter, so specifying a sum constraint forces
437
+ the job type to be continuous, and not specifying it results in the integer job being
434
438
  called.
435
439
 
436
440
  Continuous Solver
@@ -454,8 +458,7 @@ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
454
458
  >>> model = QuadraticModel(C, J)
455
459
  >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
456
460
  >>> solver = Dirac3CloudSolver()
457
- >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1,
458
- ... solution_precision=None) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
461
+ >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
459
462
  2... submitted... COMPLETED...
460
463
  >>> response["results"]["energies"][0] <= 1.0
461
464
  True
@@ -479,7 +482,6 @@ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
479
482
  tags: List = None,
480
483
  sum_constraint: float = None,
481
484
  relaxation_schedule: int = None,
482
- solution_precision: float = None,
483
485
  num_samples: int = 1,
484
486
  wait: bool = True,
485
487
  mean_photon_number: float = None,
@@ -503,11 +505,6 @@ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
503
505
  a predefined schedule indicator which sets parameters
504
506
  on the device to control the sampling through photon
505
507
  measurement
506
- solution_precision : float
507
- a value which, when not None, indicates the numerical
508
- precision desired in the solution: 1 for integer, 0.1
509
- for tenths place, 0.01 for hundreths and None for raw,
510
- only used with continuous solver
511
508
  num_samples : int
512
509
  the number of samples to take, defaults to 1
513
510
  wait : bool
@@ -540,7 +537,6 @@ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
540
537
  job_kwargs["relaxation_schedule"] = relaxation_schedule
541
538
  job_kwargs["mean_photon_number"] = mean_photon_number
542
539
  job_kwargs["quantum_fluctuation_coefficient"] = quantum_fluctuation_coefficient
543
- job_kwargs["solution_precision"] = solution_precision
544
540
  job_type = "sample-" + self.job_type
545
541
  return super().solve(
546
542
  model,
@@ -663,8 +659,7 @@ class Dirac3ContinuousCloudSolver(Dirac3Mixin, QciClientSolver):
663
659
  >>> model = QuadraticModel(C, J)
664
660
  >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
665
661
  >>> solver = Dirac3ContinuousCloudSolver()
666
- >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1,
667
- ... solution_precision=None) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
662
+ >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
668
663
  2... submitted... COMPLETED...
669
664
  >>> response["results"]["energies"][0] <= 1.0
670
665
  True
@@ -680,7 +675,6 @@ class Dirac3ContinuousCloudSolver(Dirac3Mixin, QciClientSolver):
680
675
  tags: List = None,
681
676
  sum_constraint: float = None,
682
677
  relaxation_schedule: int = None,
683
- solution_precision: float = None,
684
678
  num_samples: int = 1,
685
679
  wait: bool = True,
686
680
  mean_photon_number: float = None,
@@ -703,10 +697,6 @@ class Dirac3ContinuousCloudSolver(Dirac3Mixin, QciClientSolver):
703
697
  a predefined schedule indicator which sets parameters
704
698
  on the device to control the sampling through photon
705
699
  measurement
706
- solution_precision : float
707
- a value which, when not None, indicates the numerical
708
- precision desired in the solution: 1 for integer, 0.1
709
- for tenths place, 0.01 for hundreths and None for raw
710
700
  num_samples : int
711
701
  the number of samples to take, defaults to 1
712
702
  wait : bool
@@ -744,7 +734,6 @@ class Dirac3ContinuousCloudSolver(Dirac3Mixin, QciClientSolver):
744
734
  num_samples=num_samples,
745
735
  wait=wait,
746
736
  job_type=job_type,
747
- solution_precision=solution_precision,
748
737
  sum_constraint=sum_constraint,
749
738
  relaxation_schedule=relaxation_schedule,
750
739
  mean_photon_number=mean_photon_number,
@@ -0,0 +1,47 @@
1
+ import os
2
+ import json
3
+ import time
4
+
5
+ class ResponseLogMixin:
6
+
7
+ def logResponse(self, response, model, metrics=None):
8
+ record = {
9
+ "response": response,
10
+ "metrics": metrics,
11
+ "size": model.n,
12
+ "name": model.__class__.__name__,
13
+ "upper_bound": [u for u in model.upper_bound],
14
+ "machine_slacks": model.machine_slacks,
15
+ "penalty_multiplier": getattr(model, "penalty_multiplier", None)
16
+ }
17
+ fname = f"response-{time.time()}.json"
18
+ dirname = self.getLogDir()
19
+ fullname = os.path.join(dirname, fname)
20
+ if not os.access(fullname, os.W_OK):
21
+ log.warn(f"Response will not be logged because {fullname} is not writable")
22
+ return
23
+ elif os.path.exists(fullname):
24
+ log.warn(f"Response will not be logged because {fullname} exists")
25
+ return
26
+ with open(fullname, "w") as fp:
27
+ log.debug(f"Wrote response to {fullname}")
28
+ json.dump(record, fp)
29
+
30
+ def getLogDir(self):
31
+ """ Ensure the logging directory exists and return the path """
32
+
33
+ dirname = os.path.expanduser("~/.eqc-models")
34
+ if not os.path.exists(dirname):
35
+ try:
36
+ os.mkdir(dirname)
37
+ except OSError:
38
+ log.warn(f"Responses will not be logged because {dirname} is not writable")
39
+ return None
40
+ dirname = os.path.join(dirname, "responses")
41
+ if not os.path.exists(dirname):
42
+ try>
43
+ os.mkdir(dirname)
44
+ except OSError:
45
+ log.warn(f"Responses will not be logged because {dirname} is not writable")
46
+ return None
47
+ return dirname
@@ -1,6 +1,7 @@
1
1
  # (C) Quantum Computing Inc., 2024.
2
2
  from .polynomial import evaluate_polynomial, convert_hamiltonian_to_polynomial
3
3
  from .fileio import read_coefficient_file, read_config_file, read_index_file
4
+ from .general import create_json_problem
4
5
 
5
6
  __all__ = ["evaluate_polynomial",
6
- "read_coefficient_file", "read_index_file", "read_config_file", "convert_hamiltonian_to_polynomial"]
7
+ "read_coefficient_file", "read_index_file", "read_config_file", "convert_hamiltonian_to_polynomial", "create_json_problem"]
@@ -0,0 +1,83 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+ import numpy as np
3
+ from eqc_models.utilities.polynomial import convert_hamiltonian_to_polynomial
4
+
5
+ def create_json_problem(
6
+ A: np.array,
7
+ B: np.array,
8
+ C: np.array,
9
+ D: np.array,
10
+ num_vars: int,
11
+ sum_constraint: float = None,
12
+ num_levels: int = None,
13
+ ):
14
+
15
+ """Converts a hamiltonian of up to fourth order to a polynomial.
16
+
17
+ D_{ijkl} x_i x_j x_k x_l + C_{ijk} x_i x_j x_k + B_{ij} x_i x_j
18
+ + A_i x_i
19
+
20
+ Input:
21
+
22
+ A: First order hamiltonian.
23
+ B: Second order hamiltonian.
24
+ C: Third order hamiltonian.
25
+ D: Fourth order hamiltonian.
26
+ num_vars: Number of variables.
27
+
28
+ Output:
29
+
30
+ Problem in json format.
31
+
32
+ """
33
+
34
+ if D is not None:
35
+ assert len(D.shape) == 4, "Incorrect shape!"
36
+ assert D.shape[0] == num_vars, "Inconsistent dimensions!"
37
+ assert D.shape[1] == num_vars, "Inconsistent dimensions!"
38
+ assert D.shape[2] == num_vars, "Inconsistent dimensions!"
39
+ assert D.shape[3] == num_vars, "Inconsistent dimensions!"
40
+ degree = 4
41
+ elif C is not None:
42
+ assert len(C.shape) == 3, "Incorrect shape!"
43
+ assert C.shape[0] == num_vars, "Inconsistent dimensions!"
44
+ assert C.shape[1] == num_vars, "Inconsistent dimensions!"
45
+ assert C.shape[2] == num_vars, "Inconsistent dimensions!"
46
+ degree = 3
47
+ elif B is not None:
48
+ assert len(B.shape) == 2, "Incorrect shape!"
49
+ assert B.shape[0] == num_vars, "Inconsistent dimensions!"
50
+ assert B.shape[1] == num_vars, "Inconsistent dimensions!"
51
+ degree = 2
52
+ elif A is not None:
53
+ assert len(A.shape) in [1, 2], "Incorrect shape!"
54
+ if len(A.shape) == 2:
55
+ if A.shape[1] == 1:
56
+ A = A.reshape((A.shape[0]))
57
+ else:
58
+ assert False, "Incorrect shape!"
59
+
60
+ assert A.shape[0] == num_vars, "Inconsistent dimensions!"
61
+
62
+ degree = 1
63
+ else:
64
+ assert False, "No hamiltonian provided!"
65
+
66
+ poly_indices, poly_coefs = convert_hamiltonian_to_polynomial(
67
+ A, B, C, D, num_vars,
68
+ )
69
+
70
+ json_object = {
71
+ "degree": degree,
72
+ "num_variables": num_vars,
73
+ "poly_indices": poly_indices,
74
+ "poly_coefficients": poly_coefs,
75
+ }
76
+
77
+ if num_levels is not None:
78
+ json_object["levels"] = [num_levels] * num_vars
79
+
80
+ if sum_constraint is not None:
81
+ json_object["sum_constraint"] = sum_constraint
82
+
83
+ return json_object
@@ -1,22 +1,23 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: eqc-models
3
- Version: 0.12.0
3
+ Version: 0.14.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/
10
- Requires-Python: <3.11,>=3.9
9
+ Requires-Python: <3.14,>=3.9
11
10
  Description-Content-Type: text/markdown
12
11
  License-File: LICENSE.txt
12
+ Requires-Dist: emucore-direct==1.0.7; python_version < "3.11"
13
+ Requires-Dist: eqc-direct==2.0.2; python_version < "3.11"
13
14
  Requires-Dist: numpy<2,>=1.22.1
14
15
  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
- Requires-Dist: eqc-direct<3,>=2.0.1
16
+ Requires-Dist: pandas<3,>=2.1.0
17
+ Requires-Dist: scikit-learn<2,>=1.2.1
18
+ Requires-Dist: lightgbm<5,>=4.6.0
19
+ Requires-Dist: xgboost<2,>=1.7.4
20
+ Requires-Dist: qci-client<6,>=5
20
21
  Provides-Extra: dev
21
22
  Requires-Dist: pytest<8,>=7.1.0; extra == "dev"
22
23
  Requires-Dist: pytest-cov; extra == "dev"