Source code for eqc_models.base.results
- import dataclasses
- from typing import Dict
- import warnings
- import numpy as np
- [docs]
- @dataclasses.dataclass
- class SolutionResults:
- """
- The class is meant to provide a uniform interface to results, no matter
- the method of running the job. If available, the metrics are reported
- in nanoseconds.
- Properties
- ------------
- solutions : np.ndarray
- 2-d array of solution vectors
- energies : np.ndarray
- 1-d array of energies computed from the device for each sample
- counts : np.ndarray
- 1-d array of counts the particular sample occurred during sampling
- objectives : np.ndarray
- 1-d array of objective values. Is None if the model does not provide
- a separate objective function
- run_time : np.ndarray
- 1-d array of runtimes reported by the device.
- preprocessing_time : int
- Single value for time spent preprocessing before sampling occurs.
- postprocessing_time : np.ndarray
- 1-d array of time spent post-processing samples.
- penalties : np.ndarray
- 1-d array of penalty values for each sample. Is None if the model does
- not have constraints.
- device : str
- String that represents the device used to solve the model.
- raw_solutions : np.ndarray
- Numpy array of the solutions as returned by the solver device.
- calibration_time : float
- Total time spend during job exectution where the device performed calibration.
- The calibration is not directly affected by the job submission and the time
- is not included in run_time.
- time_units : str
- String indicator of the unit of time reported in the metrics. Only
- ns is supported at this time.
- """
- solutions : np.ndarray
- energies : np.ndarray
- counts : np.ndarray
- objectives : np.ndarray
- run_time : np.ndarray
- preprocessing_time : int
- postprocessing_time : np.ndarray
- penalties : np.ndarray = None
- device : str = None
- raw_solutions : np.ndarray = None
- time_units : str = "ns"
- @property
- def device_time(self) -> np.ndarray:
- """
- 1-d array of device usage computed from preprocessing, runtime
- and postprocessing time.
- """
- if self.run_time:
- pre = self.preprocessing_time
- runtime = np.sum(self.run_time)
- post = np.sum(self.postprocessing_time)
- return pre + runtime + post
- else:
- return None
- @property
- def total_samples(self):
- return np.sum(self.counts)
- @property
- def best_energy(self):
- return np.min(self.energies)
- [docs]
- @classmethod
- def determine_device_type(cls, device_config):
- """
- Use the device config object from a cloud response
- to get the device info. It will have a device and job type
- identifiers in it.
- """
- devices = [k for k in device_config.keys()]
-
- return devices[0]
- [docs]
- @classmethod
- def from_cloud_response(cls, model, response, solver):
- """
- Fill in the details from the cloud
- Parameters
- ------------
- model : eqc_models.base.EqcModel
- EqcModel object describing the problem solved in response
- response : Dict
- Dictionary of the repsonse from the solver device.
- solver : eqc_models.base.ModelSolver
- ModelSolver object which is used to obtain job metrics.
- """
- solutions = np.array(response["results"]["solutions"])
- if model.machine_slacks > 0:
- solutions = solutions[:,:-model.machine_slacks]
- energies = np.array(response["results"]["energies"])
-
- try:
- device_type = cls.determine_device_type(response["job_info"]["job_submission"]["device_config"])
- except KeyError:
- print(response.keys())
- raise
- if "dirac-1" in device_type:
-
- new_solutions = []
- for solution in solutions:
- solution = np.array(solution)
-
- base_count = np.floor(np.log2(model.upper_bound))+1
- assert np.sum(base_count) == solution.shape[0], "Incorrect solution-upper bound match"
- m = model.upper_bound.shape[0]
- n = solution.shape[0]
- D = np.zeros((m, n), dtype=np.int32)
- j = 0
- for i in range(m):
- k = int(base_count[i])
- D[i, j:j+k] = 2**np.arange(k)
- j += k
- solution = D@solution
- new_solutions.append(solution)
- solutions = np.array(new_solutions)
- if hasattr(model, "evaluateObjective"):
- objectives = np.zeros((solutions.shape[0],), dtype=np.float32)
- try:
- objectives[:] = model.evaluateObjective(solutions)
- except NotImplementedError:
- warnings.warn(f"Cannot evaluate objective value in results for {model.__class__}. Method not implemented.")
- objectives = None
-
-
-
-
-
-
-
-
- else:
- objectives = None
- if hasattr(model, "evaluatePenalties"):
- penalties = np.zeros((solutions.shape[0],), dtype=np.float32)
- for i in range(solutions.shape[0]):
- penalties[i] = model.evaluatePenalties(solutions[i]) + model.offset
- else:
- penalties = None
- counts = np.array(response["results"]["counts"])
- job_id = response["job_info"]["job_id"]
- try:
- metrics = solver.client.get_job_metrics(job_id=job_id)
- metrics = metrics["job_metrics"]
- time_ns = metrics["time_ns"]
- device = time_ns["device"][device_type]
- runtime = device["samples"]["runtime"]
- post = device["samples"].get("postprocessing_time", [0 for t in runtime])
- pre = device["samples"].get("preprocessing_time", 0)
- except KeyError:
- time_ns = []
- runtime = []
- post = []
- pre = None
- results = SolutionResults(solutions, energies, counts, objectives,
- runtime, pre, post, penalties=penalties,
- device=device_type, time_units="ns")
- return results
- [docs]
- @classmethod
- def from_eqcdirect_response(cls, model, response, solver):
- """
- Fill in details from the response dictionary and possibly the solver device.
- Parameters
- ------------
- model : eqc_models.base.EqcModel
- EqcModel object describing the problem solved in response
- response : Dict
- Dictionary of the repsonse from the solver device.
- solver : eqc_models.base.ModelSolver
- ModelSolver object which is used to obtain device information.
- """
- solutions = np.array(response["solution"])
- if model.machine_slacks > 0:
- solutions = solutions[:,:-model.machine_slacks]
- energies = np.array(response["energy"])
-
- info_dict = solver.client.system_info()
- device_type = info_dict["device_type"]
- if hasattr(model, "evaluateObjective"):
- objectives = np.zeros((solutions.shape[0],), dtype=np.float32)
- try:
- objectives[:] = model.evaluateObjective(solutions)
- except NotImplementedError:
- warnings.warn(f"Cannot evaluate objective value in results for {model.__class__}. Method not implemented.")
- objectives = None
- else:
- objectives = None
- if hasattr(model, "evaluatePenalties"):
- penalties = np.zeros((solutions.shape[0],), dtype=np.float32)
- for i in range(solutions.shape[0]):
- penalties[i] = model.evaluatePenalties(solutions[i]) + model.offset
- else:
- penalties = None
- counts = np.ones(solutions.shape[0])
- runtime = response["runtime"]
- post = response["postprocessing_time"]
- pre = response["preprocessing_time"]
- results = SolutionResults(solutions, energies, counts, objectives,
- runtime, pre, post, penalties=penalties,
- device=device_type, time_units="s")
- return results