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()] # only one device type is supported at a time 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"]) # interrogate to determine the device type 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: # decode the qubo new_solutions = [] for solution in solutions: solution = np.array(solution) # build an operator to map the bit vector to scalar 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 # for i in range(solutions.shape[0]): # try: # objective = model.evaluateObjective(solutions[i]) # except NotImplementedError: # warnings.warn(f"Cannot set objective value in results for {model.__class__}") # objectives = None # break # objectives[i] = objective 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"]) # interrogate to determine the device type 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