eqc-models 0.9.8__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 (52) hide show
  1. eqc_models-0.9.8.data/platlib/compile_extensions.py +23 -0
  2. eqc_models-0.9.8.data/platlib/eqc_models/__init__.py +15 -0
  3. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/__init__.py +4 -0
  4. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/base.py +10 -0
  5. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/penaltymultiplier.py +169 -0
  6. eqc_models-0.9.8.data/platlib/eqc_models/allocation/__init__.py +6 -0
  7. eqc_models-0.9.8.data/platlib/eqc_models/allocation/allocation.py +367 -0
  8. eqc_models-0.9.8.data/platlib/eqc_models/allocation/portbase.py +128 -0
  9. eqc_models-0.9.8.data/platlib/eqc_models/allocation/portmomentum.py +137 -0
  10. eqc_models-0.9.8.data/platlib/eqc_models/assignment/__init__.py +5 -0
  11. eqc_models-0.9.8.data/platlib/eqc_models/assignment/qap.py +82 -0
  12. eqc_models-0.9.8.data/platlib/eqc_models/assignment/setpartition.py +170 -0
  13. eqc_models-0.9.8.data/platlib/eqc_models/base/__init__.py +72 -0
  14. eqc_models-0.9.8.data/platlib/eqc_models/base/base.py +150 -0
  15. eqc_models-0.9.8.data/platlib/eqc_models/base/constraints.py +276 -0
  16. eqc_models-0.9.8.data/platlib/eqc_models/base/operators.py +201 -0
  17. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.c +11363 -0
  18. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
  19. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.pyx +72 -0
  20. eqc_models-0.9.8.data/platlib/eqc_models/base/polynomial.py +274 -0
  21. eqc_models-0.9.8.data/platlib/eqc_models/base/quadratic.py +250 -0
  22. eqc_models-0.9.8.data/platlib/eqc_models/decoding.py +20 -0
  23. eqc_models-0.9.8.data/platlib/eqc_models/graph/__init__.py +5 -0
  24. eqc_models-0.9.8.data/platlib/eqc_models/graph/base.py +63 -0
  25. eqc_models-0.9.8.data/platlib/eqc_models/graph/hypergraph.py +307 -0
  26. eqc_models-0.9.8.data/platlib/eqc_models/graph/maxcut.py +155 -0
  27. eqc_models-0.9.8.data/platlib/eqc_models/graph/maxkcut.py +184 -0
  28. eqc_models-0.9.8.data/platlib/eqc_models/ml/__init__.py +15 -0
  29. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierbase.py +99 -0
  30. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqboost.py +423 -0
  31. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqsvm.py +237 -0
  32. eqc_models-0.9.8.data/platlib/eqc_models/ml/clustering.py +323 -0
  33. eqc_models-0.9.8.data/platlib/eqc_models/ml/clusteringbase.py +112 -0
  34. eqc_models-0.9.8.data/platlib/eqc_models/ml/decomposition.py +363 -0
  35. eqc_models-0.9.8.data/platlib/eqc_models/ml/forecast.py +255 -0
  36. eqc_models-0.9.8.data/platlib/eqc_models/ml/forecastbase.py +139 -0
  37. eqc_models-0.9.8.data/platlib/eqc_models/ml/regressor.py +220 -0
  38. eqc_models-0.9.8.data/platlib/eqc_models/ml/regressorbase.py +97 -0
  39. eqc_models-0.9.8.data/platlib/eqc_models/ml/reservoir.py +106 -0
  40. eqc_models-0.9.8.data/platlib/eqc_models/sequence/__init__.py +5 -0
  41. eqc_models-0.9.8.data/platlib/eqc_models/sequence/tsp.py +217 -0
  42. eqc_models-0.9.8.data/platlib/eqc_models/solvers/__init__.py +12 -0
  43. eqc_models-0.9.8.data/platlib/eqc_models/solvers/qciclient.py +707 -0
  44. eqc_models-0.9.8.data/platlib/eqc_models/utilities/__init__.py +6 -0
  45. eqc_models-0.9.8.data/platlib/eqc_models/utilities/fileio.py +38 -0
  46. eqc_models-0.9.8.data/platlib/eqc_models/utilities/polynomial.py +137 -0
  47. eqc_models-0.9.8.data/platlib/eqc_models/utilities/qplib.py +375 -0
  48. eqc_models-0.9.8.dist-info/LICENSE.txt +202 -0
  49. eqc_models-0.9.8.dist-info/METADATA +139 -0
  50. eqc_models-0.9.8.dist-info/RECORD +52 -0
  51. eqc_models-0.9.8.dist-info/WHEEL +5 -0
  52. eqc_models-0.9.8.dist-info/top_level.txt +2 -0
@@ -0,0 +1,707 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+ from typing import Dict, List, Tuple
3
+ import logging
4
+ import datetime
5
+ import numpy as np
6
+ from qci_client import QciClient
7
+ from eqc_models.base.base import ModelSolver, EqcModel
8
+
9
+ log = logging.getLogger(name=__name__)
10
+
11
+ class QciClientMixin:
12
+ """
13
+ This class provides an instance method and property that manage the connection to
14
+ the REST API.
15
+
16
+ Methods
17
+ -------
18
+
19
+ connect
20
+
21
+ Properties
22
+ ----------
23
+
24
+ client : QciClient
25
+
26
+ """
27
+
28
+ url = None
29
+ api_token = None
30
+
31
+ def connect(self, url: str = None, api_token: str = None):
32
+ """
33
+ Use this method to define connection parameters manually. Returns the string "SUCCESS"
34
+ on a successful connection, raises an a RuntimeError on failure.
35
+
36
+ Parameters
37
+ --------------
38
+
39
+ url: The URL used to connect to the Dirac machine.
40
+
41
+ api_token: Authentication token.
42
+
43
+ """
44
+ self.url = url
45
+ self.api_token = api_token
46
+ client = self.client
47
+ if client is None:
48
+ raise RuntimeError("Failed to connect to Qatalyst service")
49
+ return "SUCCESS"
50
+
51
+ @property
52
+ def client(self) -> QciClient:
53
+ """
54
+ Returns a new client object every time. If the connection was not
55
+ configured in code, then QciClient attempts to use environment variables
56
+ to connect to the service
57
+
58
+ """
59
+ # pull from environment variables if the class variables are not set
60
+ if self.url is None and self.api_token is None:
61
+ log.debug(
62
+ "Getting QciClient connection using environment variables"
63
+ )
64
+ client = QciClient()
65
+ else:
66
+ log.debug("Getting QciClient connection using class variables")
67
+ client = QciClient(url=self.url, api_token=self.api_token)
68
+ return client
69
+
70
+ class Dirac1Mixin:
71
+ sampler_type = "dirac-1"
72
+ job_params_names = ["num_samples", "alpha", "atol"]
73
+
74
+ class QuboSolverMixin:
75
+ job_type = "qubo"
76
+
77
+ def uploadJobFiles(self, client:QciClient, model:EqcModel):
78
+ """
79
+ This method retrieves a QUBO representation from the model's
80
+ :code:`qubo` property and uploads it, returning :code:`qubo_file_id`
81
+ for submission with a job request.
82
+
83
+ Parameters
84
+ --------------
85
+
86
+ client: The QciClient instance.
87
+
88
+ model: The EqcModel instance.
89
+
90
+ """
91
+
92
+ # C, J = model.H
93
+ # Q = J + np.diag(C)
94
+ Q = model.qubo.Q
95
+ qubo_file = {
96
+ "file_name": f"{model.__class__.__name__}-qubo",
97
+ "file_config": {
98
+ "qubo": {"data": Q, "num_variables": Q.shape[0]}
99
+ },
100
+ }
101
+ qubo_file_id = client.upload_file(file=qubo_file)["file_id"]
102
+ return {"qubo_file_id": qubo_file_id}
103
+
104
+ class Dirac3Mixin:
105
+ """
106
+ Defines the specifics required for using Dirac-3 as the sampler
107
+
108
+ """
109
+
110
+ sampler_type = "dirac-3"
111
+ job_params_names = [
112
+ "num_samples",
113
+ "solution_precision",
114
+ "relaxation_schedule",
115
+ "mean_photon_number",
116
+ "normalized_loss_rate",
117
+ ]
118
+
119
+ def uploadJobFiles(self, client: QciClient, model: EqcModel):
120
+ """
121
+ Upload a Hamiltonian in polynomial format.
122
+
123
+ Parameters
124
+ --------------
125
+
126
+ client: The QciClient instance.
127
+
128
+ model: The EqcModel instance.
129
+
130
+ """
131
+
132
+ # poly_coeffs, poly_indices = model.sparse
133
+ polynomial = model.polynomial
134
+ poly_coeffs = polynomial.coefficients
135
+ poly_indices = polynomial.indices
136
+ data = []
137
+ # must find these attributes of the polynomial before uploading
138
+ max_degree = 0
139
+ min_degree = len(poly_indices[-1])
140
+ num_variables = 0
141
+ for i in range(len(poly_coeffs)):
142
+ idx = 0
143
+ if num_variables < max(poly_indices[i]):
144
+ num_variables = max(poly_indices[i])
145
+ while max_degree < len(poly_indices[i]) and idx < len(
146
+ poly_indices[i]
147
+ ):
148
+ if (
149
+ poly_indices[i][idx] > 0
150
+ and max_degree < len(poly_indices[i]) - idx
151
+ ):
152
+ max_degree = len(poly_indices[i]) - idx
153
+ idx += len(poly_indices[i])
154
+ else:
155
+ idx += 1
156
+ idx = len(poly_indices[i]) - 1
157
+ while min_degree > 1 and idx > 0:
158
+ if (
159
+ poly_indices[i][idx] > 0
160
+ and min_degree > len(poly_indices[i]) - idx
161
+ ):
162
+ min_degree = len(poly_indices[i]) - idx
163
+ idx = 0
164
+ else:
165
+ idx -= 1
166
+ data.append(
167
+ {
168
+ "idx": poly_indices[i],
169
+ "val": float(poly_coeffs[i]),
170
+ }
171
+ )
172
+ log.debug("Min degree of polynomial %d", min_degree)
173
+ log.debug("Max degree of polynomial %d", max_degree)
174
+ log.debug("Number of polynomial elements %d", len(poly_coeffs))
175
+ polynomial = {
176
+ "file_name": f"{model.__class__.__name__}",
177
+ "file_config": {
178
+ "polynomial": {
179
+ "num_variables": int(num_variables)
180
+ + model.machine_slacks,
181
+ "max_degree": max_degree,
182
+ "min_degree": min_degree,
183
+ "data": data,
184
+ }
185
+ },
186
+ }
187
+ log.debug(polynomial)
188
+ file_id = client.upload_file(file=polynomial)["file_id"]
189
+ log.debug("Upload polynomial file produced file id %s", file_id)
190
+ return {"polynomial_file_id": file_id}
191
+
192
+ class QciClientSolver(QciClientMixin, ModelSolver):
193
+ """
194
+ Parameters
195
+ -----------
196
+
197
+ url : string
198
+ optional value specifying the QCi API URL
199
+ api_token : string
200
+ optional value specifying the authentication token for the QCi API
201
+
202
+ QCi API client wrapper for solving an EQC model. This class provides the
203
+ common method for uploading a file to the API for solving. Since the file
204
+ types change for the job types, the specific files required for the job are
205
+ specified in subclasses within the `uploadFiles` method.
206
+
207
+ """
208
+
209
+ def __init__(self, url=None, api_token=None):
210
+ self.url = url
211
+ self.api_token = api_token
212
+
213
+ @staticmethod
214
+ def uploadFile(
215
+ file_data: np.ndarray,
216
+ file_name: str = None,
217
+ file_type: str = None,
218
+ client: QciClient = None,
219
+ ) -> str:
220
+ """
221
+ Upload the operator file, return the file ID.
222
+
223
+ Parameters
224
+ --------------
225
+
226
+ file_data: numpy array, dictionary or list
227
+ contains file data to be uploaded
228
+
229
+ file_name: str
230
+ Name of the file to be uploaded.
231
+
232
+ file_type: str
233
+ Type of the file to be uploaded.
234
+
235
+ client: QciClient
236
+ QciClient instance
237
+
238
+ """
239
+
240
+ n = file_data.shape[0]
241
+ log.debug("Uploading %s file of %d variables", file_type, n)
242
+ if client is None:
243
+ log.debug("Retrieving instance client")
244
+ client = self.client
245
+ file_obj = {"file_config": {file_type: {"data": file_data}}}
246
+ if file_type in (
247
+ "constraints",
248
+ "hamiltonian",
249
+ "qubo",
250
+ "objective",
251
+ ):
252
+ file_obj["file_config"][file_type]["num_variables"] = n
253
+ # print(ham_file)
254
+ if file_name is None:
255
+ ts = datetime.datetime.now().timestamp()
256
+ file_name = f"{file_type}{n}-{ts}"
257
+ log.debug("Using file name %s", file_name)
258
+ file_obj["file_name"] = file_name
259
+ file_id = client.upload_file(file=file_obj)["file_id"]
260
+ return file_id
261
+
262
+ def uploadJobFiles(self, client: QciClient, model: EqcModel):
263
+ raise NotImplementedError("Subclass must override uploadJobFiles")
264
+
265
+ def solve(
266
+ self,
267
+ model: EqcModel,
268
+ name: str = None,
269
+ tags: List = None,
270
+ num_samples: int = 1,
271
+ wait=True,
272
+ job_type=None,
273
+ **job_kwargs,
274
+ ) -> Dict:
275
+ """
276
+ Parameters
277
+ --------------
278
+
279
+ model: EqcModel
280
+ Instance of a model for solving.
281
+
282
+ name: str
283
+ Name of the job; default is None.
284
+
285
+ tags: list
286
+ A list of job tags; default is None.
287
+
288
+ num_samples: int
289
+ Number of samples used; default is 1.
290
+
291
+ wait: bool
292
+ The wait flag indicating whether to wait for the job to complete
293
+ before returning the complete job data otherwise return a job ID
294
+ as soon as a job is submitted; default is True.
295
+
296
+ job_type: str
297
+ Type of the job; default is None. When None, it is constructed
298
+ from the instance `job_type` property.
299
+
300
+ Returns
301
+ ----------
302
+ job response dictionary
303
+
304
+ This method takes the particulars of the instance model and handles
305
+ the QciClient.solve call.
306
+
307
+ """
308
+
309
+ job_config = {}
310
+ job_config.update(
311
+ {"num_samples": num_samples, "device_type": self.sampler_type}
312
+ )
313
+ # set the job parameters
314
+ for name in self.job_params_names:
315
+ if name in job_kwargs:
316
+ job_config[name] = job_kwargs.pop(name)
317
+ elif hasattr(model, name):
318
+ job_config[name] = getattr(model, name)
319
+ leftovers = ",".join(job_kwargs.keys())
320
+ if leftovers:
321
+ raise ValueError(
322
+ f"Unused job parameters given to solve method: {leftovers}"
323
+ )
324
+ # prep files in the API
325
+ client = self.client
326
+ job_files = self.uploadJobFiles(client, model)
327
+ log.debug(f"Building job body: {job_config}")
328
+ if job_type is None:
329
+ job_type = f"sample-{self.job_type}"
330
+ job_body = client.build_job_body(
331
+ job_type=job_type,
332
+ job_params=job_config,
333
+ job_tags=tags,
334
+ **job_files,
335
+ )
336
+ response = client.process_job(job_body=job_body, wait=wait)
337
+ return response
338
+
339
+ def getResults(self, response: Dict) -> Dict[str, List]:
340
+ """
341
+ Extract the results from response.
342
+
343
+ Parameters
344
+ --------------
345
+ response: The responce from QciClient.
346
+
347
+ Returns
348
+ --------------
349
+ The results json object.
350
+
351
+ """
352
+
353
+ results = response["results"]
354
+ log.debug("Got results object: %s", results)
355
+ return results
356
+
357
+ # Some example usage
358
+ class Dirac1CloudSolver(Dirac1Mixin, QuboSolverMixin, QciClientSolver):
359
+ """
360
+ Overview
361
+ ---------
362
+ Dirac1CloudSolver is a class that encapsulates the different calls to Qatalyst
363
+ for Dirac-1 jobs, which are quadratic binary optimization problems.
364
+
365
+ Examples
366
+ -------------------
367
+
368
+ >>> C = np.array([[-1], [-1]])
369
+ >>> J = np.array([[0, 1.0], [1.0, 0]])
370
+ >>> from eqc_models.base.quadratic import QuadraticModel
371
+ >>> model = QuadraticModel(C, J)
372
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
373
+ >>> solver = Dirac1CloudSolver()
374
+ >>> response = solver.solve(model, num_samples=5) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
375
+ 2... submitted... COMPLETED...
376
+ >>> response["results"]["energies"][0] <= 1.0
377
+ True
378
+ """
379
+
380
+ class Dirac3CloudSolver(Dirac3Mixin, QciClientSolver):
381
+ """
382
+
383
+ Dirac3CloudSolver is a class that encapsulates the different calls to Qatalyst
384
+ for Dirac-3 jobs. Currently, there are two different jobs, one for integer and
385
+ another for continuous solutions. Calling the solve method with different arguments
386
+ controls which job is submitted. The continuous job requires :code:`sum_constraint`
387
+ and optionally takes the :code:`solution_precision` argument. The integer job
388
+ does not accept either of these parameters, so specifying a sum constraint forces
389
+ the job type to be continuous and not specifying it results in the integer job being
390
+ called.
391
+
392
+ Continuous Solver
393
+ -------------------
394
+
395
+ Utilizing Dirac-3 as a continuous solver involves encoding the variables in single time bins
396
+ with the values of each determined by a normalized photon count value.
397
+
398
+ Integer Solver
399
+ -------------------
400
+
401
+ Utilizing Dirac-3 as an integer solver involves encoding the variables in multiple time bins,
402
+ each representing a certain value for that variable, or "qudit".
403
+
404
+ Examples
405
+ -------------------
406
+
407
+ >>> C = np.array([[1], [1]])
408
+ >>> J = np.array([[-1.0, 0], [0, -1.0]])
409
+ >>> from eqc_models.base.quadratic import QuadraticModel
410
+ >>> model = QuadraticModel(C, J)
411
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
412
+ >>> solver = Dirac3CloudSolver()
413
+ >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1,
414
+ ... solution_precision=None) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
415
+ 2... submitted... COMPLETED...
416
+ >>> response["results"]["energies"][0] <= 1.0
417
+ True
418
+ >>> C = np.array([-1, -1], dtype=np.float32)
419
+ >>> J = np.array([[0, 1], [1, 0]], dtype=np.float32)
420
+ >>> model = QuadraticModel(C, J)
421
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
422
+ >>> response = solver.solve(model, relaxation_schedule=1) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
423
+ 2... submitted... COMPLETED...
424
+ >>> response["results"]["energies"][0] == -1.0
425
+ True
426
+ """
427
+
428
+ job_type = "hamiltonian"
429
+ job_params_names = Dirac3Mixin.job_params_names + ["num_levels", "sum_constraint"]
430
+
431
+ def solve(
432
+ self,
433
+ model: EqcModel,
434
+ name: str = None,
435
+ tags: List = None,
436
+ sum_constraint: float = None,
437
+ relaxation_schedule: int = None,
438
+ solution_precision: float = None,
439
+ num_samples: int = 1,
440
+ wait: bool = True,
441
+ mean_photon_number: float = None,
442
+ normalized_loss_rate: int = None,
443
+ **job_kwargs,
444
+ ):
445
+ """
446
+ Parameters
447
+ --------------
448
+
449
+ model: EqcModel
450
+ a model object which supplies a hamiltonian operator for the
451
+ device to sample. Must support the polynomial operator property.
452
+ tags: List
453
+ a list of strings to save with the job
454
+ sum_constraint : float
455
+ a value which applies a constraint to the solution, forcing
456
+ all variables to sum to this value, changes method to continuous
457
+ solver
458
+ relaxation_schedule : int
459
+ a predefined schedule indicator which sets parameters
460
+ on the device to control the sampling through photon
461
+ measurement
462
+ solution_precision : float
463
+ a value which, when not None, indicates the numerical
464
+ precision desired in the solution: 1 for integer, 0.1
465
+ for tenths place, 0.01 for hundreths and None for raw,
466
+ only used with continuous solver
467
+ num_samples : int
468
+ the number of samples to take, defaults to 1
469
+ wait : bool
470
+ a flag for waiting for the response or letting it run asynchronously.
471
+ Asynchronous runs must retrieve results directly using qci-client and
472
+ the job_id.
473
+ mean_photon_number : float
474
+ an optional decimal value which sets the average number
475
+ of photons that are present in a given quantum state.
476
+ Modify this value to control the relaxation schedule more
477
+ precisely than the four presets given in schedules 1
478
+ through 4. Allowed values are decimals between 0.1 and 2.
479
+ normalized_loss_rate : int
480
+ an integer value which Sets the amount of loss introduced
481
+ into the system for each loop during the measurement process.
482
+ Modify this value to control the relaxation schedule more
483
+ precisely than the four presets given in schedules 1
484
+ through 4. Allowed values range from 1 to 50.
485
+
486
+ """
487
+ # choose integer or continuous solver
488
+ continuous = sum_constraint is not None
489
+ if continuous:
490
+ job_kwargs["sum_constraint"] = sum_constraint
491
+ if relaxation_schedule not in (1, 2, 3, 4):
492
+ raise ValueError(
493
+ "relaxation_schedule must be one of 1, 2, 3 or 4"
494
+ )
495
+ job_kwargs["relaxation_schedule"] = relaxation_schedule
496
+ job_kwargs["mean_photon_number"] = mean_photon_number
497
+ job_kwargs["normalized_loss_rate"] = normalized_loss_rate
498
+ job_kwargs["solution_precision"] = solution_precision
499
+ job_type = "sample-" + self.job_type
500
+ return super().solve(
501
+ model,
502
+ name,
503
+ tags=tags,
504
+ num_samples=num_samples,
505
+ wait=wait,
506
+ job_type=job_type,
507
+ **job_kwargs,
508
+ )
509
+
510
+ else:
511
+ job_kwargs["mean_photon_number"] = mean_photon_number
512
+ job_kwargs["normalized_loss_rate"] = normalized_loss_rate
513
+ job_kwargs["relaxation_schedule"] = relaxation_schedule
514
+ job_kwargs["num_levels"] = ub = [val + 1 for val in model.upper_bound.tolist()]
515
+ job_type = "sample-" + self.job_type + "-integer"
516
+ return super().solve(
517
+ model,
518
+ name,
519
+ tags=tags,
520
+ num_samples=num_samples,
521
+ wait=wait,
522
+ job_type=job_type,
523
+ **job_kwargs,
524
+ )
525
+
526
+ class Dirac3IntegerCloudSolver(Dirac3Mixin, QciClientSolver):
527
+ """
528
+
529
+ >>> C = np.array([-1, -1], dtype=np.float32)
530
+ >>> J = np.array([[0, 1], [1, 0]], dtype=np.float32)
531
+ >>> from eqc_models.base.quadratic import QuadraticModel
532
+ >>> model = QuadraticModel(C, J)
533
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
534
+ >>> solver = Dirac3IntegerCloudSolver()
535
+ >>> model = QuadraticModel(C, J)
536
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
537
+ >>> response = solver.solve(model, relaxation_schedule=1) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
538
+ 2... submitted... COMPLETED...
539
+ >>> response["results"]["energies"][0] == -1.0
540
+ True
541
+
542
+ """
543
+ job_type = "hamiltonian-integer"
544
+ job_params_names = Dirac3Mixin.job_params_names + ["num_levels"]
545
+
546
+ def solve(self,
547
+ model : EqcModel,
548
+ name: str = None,
549
+ tags: List = None,
550
+ relaxation_schedule: int = None,
551
+ num_samples: int = 1,
552
+ wait: bool = True,
553
+ mean_photon_number: float = None,
554
+ normalized_loss_rate: int = None,
555
+ **job_kwargs,
556
+ ):
557
+ """
558
+ Parameters
559
+ --------------
560
+
561
+ model: EqcModel
562
+ a model object which supplies a hamiltonian operator for the
563
+ device to sample. Must support the polynomial operator property.
564
+ tags: List
565
+ a list of strings to save with the job
566
+ relaxation_schedule : int
567
+ a predefined schedule indicator which sets parameters
568
+ on the device to control the sampling through photon
569
+ measurement
570
+ num_samples : int
571
+ the number of samples to take, defaults to 1
572
+ wait : bool
573
+ a flag for waiting for the response or letting it run asynchronously.
574
+ Asynchronous runs must retrieve results directly using qci-client and
575
+ the job_id.
576
+ mean_photon_number : float
577
+ an optional decimal value which sets the average number
578
+ of photons that are present in a given quantum state.
579
+ Modify this value to control the relaxation schedule more
580
+ precisely than the four presets given in schedules 1
581
+ through 4. Allowed values are decimals between 0.1 and 2.
582
+ normalized_loss_rate : int
583
+ an integer value which Sets the amount of loss introduced
584
+ into the system for each loop during the measurement process.
585
+ Modify this value to control the relaxation schedule more
586
+ precisely than the four presets given in schedules 1
587
+ through 4. Allowed values range from 1 to 50.
588
+
589
+
590
+ Dirac3IntegerCloudSolver is a class that encapsulates the different calls to
591
+ Qatalyst for Dirac-3 jobs. Utilizing Dirac-3 as an integer solver involves
592
+ encoding the variables in multiple time bins, each representing a certain
593
+ value for that variable, or "qudit".
594
+
595
+
596
+ """
597
+ return super().solve(
598
+ model,
599
+ name,
600
+ tags=tags,
601
+ num_samples=num_samples,
602
+ wait=wait,
603
+ mean_photon_number=mean_photon_number,
604
+ normalized_loss_rate=normalized_loss_rate,
605
+ relaxation_schedule=relaxation_schedule,
606
+ num_levels=[val + 1 for val in model.upper_bound.tolist()],
607
+ **job_kwargs,
608
+ )
609
+
610
+
611
+ class Dirac3ContinuousCloudSolver(Dirac3Mixin, QciClientSolver):
612
+ """
613
+
614
+ >>> C = np.array([[1], [1]])
615
+ >>> J = np.array([[-1.0, 0], [0, -1.0]])
616
+ >>> from eqc_models.base.quadratic import QuadraticModel
617
+ >>> model = QuadraticModel(C, J)
618
+ >>> model.upper_bound = np.array([1, 1]) # set the domain maximum per variable
619
+ >>> solver = Dirac3ContinuousCloudSolver()
620
+ >>> response = solver.solve(model, sum_constraint=1, relaxation_schedule=1,
621
+ ... solution_precision=None) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
622
+ 2... submitted... COMPLETED...
623
+ >>> response["results"]["energies"][0] <= 1.0
624
+ True
625
+
626
+ """
627
+ job_type = "hamiltonian"
628
+ job_params_names = Dirac3Mixin.job_params_names + ["sum_constraint"]
629
+
630
+ def solve(
631
+ self,
632
+ model: EqcModel,
633
+ name: str = None,
634
+ tags: List = None,
635
+ sum_constraint: float = None,
636
+ relaxation_schedule: int = None,
637
+ solution_precision: float = None,
638
+ num_samples: int = 1,
639
+ wait: bool = True,
640
+ mean_photon_number: float = None,
641
+ normalized_loss_rate: int = None,
642
+ **job_kwargs,
643
+ ):
644
+ """
645
+ Parameters
646
+ --------------
647
+
648
+ model: EqcModel
649
+ a model object which supplies a hamiltonian operator for the
650
+ device to sample. Must support the polynomial operator property.
651
+ tags: List
652
+ a list of strings to save with the job
653
+ sum_constraint : float
654
+ a value which applies a constraint to the solution, forcing
655
+ all variables to sum to this value
656
+ relaxation_schedule : int
657
+ a predefined schedule indicator which sets parameters
658
+ on the device to control the sampling through photon
659
+ measurement
660
+ solution_precision : float
661
+ a value which, when not None, indicates the numerical
662
+ precision desired in the solution: 1 for integer, 0.1
663
+ for tenths place, 0.01 for hundreths and None for raw
664
+ num_samples : int
665
+ the number of samples to take, defaults to 1
666
+ wait : bool
667
+ a flag for waiting for the response or letting it run asynchronously.
668
+ Asynchronous runs must retrieve results directly using qci-client and
669
+ the job_id.
670
+ mean_photon_number : float
671
+ an optional decimal value which sets the average number
672
+ of photons that are present in a given quantum state.
673
+ Modify this value to control the relaxation schedule more
674
+ precisely than the four presets given in schedules 1
675
+ through 4. Allowed values are decimals between 0.1 and 2.
676
+ normalized_loss_rate : int
677
+ an integer value which Sets the amount of loss introduced
678
+ into the system for each loop during the measurement process.
679
+ Modify this value to control the relaxation schedule more
680
+ precisely than the four presets given in schedules 1
681
+ through 4. Allowed values range from 1 to 50.
682
+
683
+ """
684
+ if sum_constraint is None:
685
+ raise ValueError(
686
+ "sum_constraint must be specified as a positive number"
687
+ )
688
+ if relaxation_schedule not in (1, 2, 3, 4):
689
+ raise ValueError(
690
+ "relaxation_schedule must be one of 1, 2, 3 or 4"
691
+ )
692
+ job_type = "sample-" + self.job_type
693
+ return super().solve(
694
+ model,
695
+ name,
696
+ tags=tags,
697
+ num_samples=num_samples,
698
+ wait=wait,
699
+ job_type=job_type,
700
+ solution_precision=solution_precision,
701
+ sum_constraint=sum_constraint,
702
+ relaxation_schedule=relaxation_schedule,
703
+ mean_photon_number=mean_photon_number,
704
+ normalized_loss_rate=normalized_loss_rate,
705
+ **job_kwargs,
706
+ )
707
+