def run(qobj, executable): """ Run simulation on C++ simulator inside a subprocess. Args: qobj (Qobj): qobj dictionary defining the simulation to run executable (string): filename (with path) of the simulator executable Returns: dict: A dict of simulation results """ # Open subprocess and execute external command try: with subprocess.Popen([executable, '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE) as proc: cin = json.dumps(qobj_to_dict(qobj, version='0.0.1'), cls=QASMSimulatorEncoder).encode() cout, cerr = proc.communicate(cin) if cerr: logger.error('ERROR: Simulator encountered a runtime error: %s', cerr.decode()) sim_output = cout.decode() return json.loads(sim_output, cls=QASMSimulatorDecoder) except FileNotFoundError: msg = "ERROR: Simulator exe not found at: %s" % executable logger.error(msg) return {"status": msg, "success": False}
def _run_job(self, qobj): """Run circuits in q_job""" result_list = [] self._validate(qobj) qobj_old_format = qobj_to_dict(qobj, version='0.0.1') s = JKUSimulatorWrapper(self._configuration['exe'], silent=self.silent) #self._shots = qobj['config']['shots'] s.shots = qobj_old_format['config']['shots'] start = time.time() for circuit in qobj_old_format['circuits']: result_list.append(s.run_on_qobj_circuit(circuit)) end = time.time() job_id = str(uuid.uuid4()) result = { 'backend': self._configuration['name'], 'id': qobj_old_format['id'], 'job_id': job_id, 'result': result_list, 'status': 'COMPLETED', 'success': True, 'time_taken': (end - start) } return result_from_old_style_dict( result, [circuit.header.name for circuit in qobj.experiments])
def get_execution_list(self, qobj, print_func=print): """Print the compiled circuits that are ready to run. Note: This method is intended to be used during interactive sessions, and prints directly to stdout instead of using the logger by default. If you set print_func with a log function (eg. log.info) it will be used instead of the stdout. Returns: list(str): names of the circuits in `qobj` """ if not qobj: print_func("no executions to run") execution_list = [] qobj = qobj_to_dict(qobj, version='0.0.1') print_func("id: %s" % qobj['id']) print_func("backend: %s" % qobj['config']['backend_name']) print_func("qobj config:") for key in qobj['config']: if key != 'backend': print_func(' ' + key + ': ' + str(qobj['config'][key])) for circuit in qobj['circuits']: execution_list.append(circuit["name"]) print_func(' circuit name: ' + str(circuit["name"])) print_func(' circuit config:') for key in circuit['config']: print_func(' ' + key + ': ' + str(circuit['config'][key])) return execution_list
def __init__(self, api, is_device, qobj=None, job_id=None, backend_name=None, creation_date=None): """IBMQJob init function. We can instantiate jobs from two sources: A QObj, and an already submitted job returned by the API servers. Args: api (IBMQuantumExperience): IBM Q API is_device (bool): whether backend is a real device # TODO: remove this after Qobj qobj (Qobj): The Quantum Object. See notes below job_id (String): The job ID of an already submitted job. backend_name(String): The name of the backend that run the job. creation_date(String): When the job was run. Notes: It is mandatory to pass either ``qobj`` or ``job_id``. Passing a ``qobj`` will ignore ``job_id`` and will create an instance representing an already-created job retrieved from the API server. """ super().__init__() self._job_data = None if qobj is not None: # TODO: No need for this conversion, just use the new equivalent members above old_qobj = qobj_to_dict(qobj, version='0.0.1') self._job_data = { 'circuits': old_qobj['circuits'], 'hpc': old_qobj['config'].get('hpc'), 'seed': old_qobj['circuits'][0]['config']['seed'], 'shots': old_qobj['config']['shots'], 'max_credits': old_qobj['config']['max_credits'] } self._future_captured_exception = None self._api = api self._id = job_id self._backend_name = qobj.header.backend_name if qobj is not None else backend_name self._status = JobStatus.INITIALIZING # In case of not providing a qobj, it assumes job_id has been provided # and query the API for updating the status. if qobj is None: self.status() self._queue_position = None self._cancelled = False self._is_device = is_device def current_utc_time(): """Gets the current time in UTC format""" datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat() self._creation_date = creation_date or current_utc_time() self._future = None self._api_error_msg = None
def _run_job(self, qobj): """Run qobj. This is a blocking call. Args: qobj (Qobj): job description Returns: Result: Result object """ result_list = [] qobj_converted = qobj_to_dict(qobj, version='0.0.1') for circuit in qobj_converted['circuits']: result_list.append(self.run_circuit(circuit)) job_id = str(uuid.uuid4()) return Result( {'job_id': job_id, 'result': result_list, 'status': 'COMPLETED'})
def __init__(self, backend, job_id, api, is_device, qobj=None, creation_date=None, api_status=None, **kwargs): """IBMQJob init function. We can instantiate jobs from two sources: A QObj, and an already submitted job returned by the API servers. Args: backend (str): The backend instance used to run this job. job_id (str): The job ID of an already submitted job. Pass `None` if you are creating a new one. api (IBMQuantumExperience): IBM Q API is_device (bool): whether backend is a real device # TODO: remove this after Qobj qobj (Qobj): The Quantum Object. See notes below creation_date (str): When the job was run. api_status (str): `status` field directly from the API response. kwargs (dict): You can pass `backend_name` to this function although it has been deprecated. Notes: It is mandatory to pass either ``qobj`` or ``job_id``. Passing a ``qobj`` will ignore ``job_id`` and will create an instance representing an already-created job retrieved from the API server. """ if 'backend_name' in kwargs: warnings.warn('Passing the parameter `backend_name` is deprecated, ' 'pass the `backend` parameter with the instance of ' 'the backend running the job.', DeprecationWarning) super().__init__(backend, job_id) self._job_data = None if qobj is not None: validate_qobj_against_schema(qobj) self._qobj_payload = qobj_to_dict(qobj, version='1.0.0') # TODO: No need for this conversion, just use the new equivalent members above old_qobj = qobj_to_dict(qobj, version='0.0.1') self._job_data = { 'circuits': old_qobj['circuits'], 'hpc': old_qobj['config'].get('hpc'), 'seed': old_qobj['circuits'][0]['config']['seed'], 'shots': old_qobj['config']['shots'], 'max_credits': old_qobj['config']['max_credits'] } self._future_captured_exception = None self._api = api self._backend = backend self._cancelled = False self._status = JobStatus.INITIALIZING # In case of not providing a `qobj`, it is assumed the job already # exists in the API (with `job_id`). if qobj is None: # Some API calls (`get_status_jobs`, `get_status_job`) provide # enough information to recreate the `Job`. If that is the case, try # to make use of that information during instantiation, as # `self.status()` involves an extra call to the API. if api_status == 'VALIDATING': self._status = JobStatus.VALIDATING elif api_status == 'COMPLETED': self._status = JobStatus.DONE elif api_status == 'CANCELLED': self._status = JobStatus.CANCELLED self._cancelled = True else: self.status() self._queue_position = None self._is_device = is_device def current_utc_time(): """Gets the current time in UTC format""" datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() self._creation_date = creation_date or current_utc_time() self._future = None self._api_error_msg = None
def _submit(self): """Submit job to IBM Q. Returns: dict: submission info including job id from server Raises: QISKitError: The backend name in the job doesn't match this backend. ResultError: If the API reported an error with the submitted job. RegisterSizeError: If the requested register size exceeded device capability. """ qobj = qobj_to_dict(self._qobj, version='0.0.1') api_jobs = [] for circuit in qobj['circuits']: job = {} if not circuit.get('compiled_circuit_qasm', None): compiled_circuit = transpile(circuit['circuit']) circuit['compiled_circuit_qasm'] = compiled_circuit.qasm( qeflag=True) if isinstance(circuit['compiled_circuit_qasm'], bytes): job['qasm'] = circuit['compiled_circuit_qasm'].decode() else: job['qasm'] = circuit['compiled_circuit_qasm'] if circuit.get('name', None): job['name'] = circuit['name'] # convert numpy types for json serialization compiled_circuit = json.loads( json.dumps(circuit['compiled_circuit'], default=_numpy_type_converter)) job['metadata'] = {'compiled_circuit': compiled_circuit} api_jobs.append(job) seed0 = qobj['circuits'][0]['config']['seed'] hpc = None if qobj['config'].get('hpc', None): try: # Use CamelCase when passing the hpc parameters to the API. hpc = { 'multiShotOptimization': qobj['config']['hpc']['multi_shot_optimization'], 'ompNumThreads': qobj['config']['hpc']['omp_num_threads'] } except (KeyError, TypeError): hpc = None backend_name = qobj['config']['backend_name'] if backend_name != self._backend_name: raise QISKitError("inconsistent qobj backend " "name ({0} != {1})".format( backend_name, self._backend_name)) try: submit_info = self._api.run_job( api_jobs, backend=backend_name, shots=qobj['config']['shots'], max_credits=qobj['config']['max_credits'], seed=seed0, hpc=hpc) # pylint: disable=broad-except except Exception as err: self._status = JobStatus.ERROR self._status_msg = str(err) self._exception = err return None if 'error' in submit_info: self._status = JobStatus.ERROR self._status_msg = str(submit_info['error']) self._exception = IBMQJobError(self._status_msg) return submit_info self._id = submit_info.get('id') self.creation_date = submit_info.get('creationDate') self._status = JobStatus.QUEUED return submit_info
def cache_circuit(self, qobj, circuits, chunk): """ A method for caching compiled qobjs by storing the compiled qobj and constructing a mapping array from the uncompiled operations in the circuit to the instructions in the qobj. Note that the "qobjs" list in the cache dict is a list of the cached chunks, each element of which contains a single qobj with as many experiments as is allowed by the execution backend. E.g. if the backend allows 300 experiments per job and the user wants to run 500 circuits, len(circuit_cache['qobjs']) == 2, len(circuit_cache['qobjs'][0].experiments) == 300, and len(circuit_cache['qobjs'][1].experiments) == 200. This feature is only applied if 'circuit_caching' is True in the 'problem' Aqua dictionary section. Args: qobj (Qobj): A compiled qobj to be saved circuits (list): The original uncompiled QuantumCircuits chunk (int): If a larger list of circuits was broken into chunks by run_algorithm for separate runs, which chunk number `circuits` represents """ self.qobjs.insert(chunk, copy.deepcopy(qobj)) self.mappings.insert(chunk, [{} for i in range(len(circuits))]) for circ_num, input_circuit in enumerate(circuits): qreg_sizes = [ reg.size for reg in input_circuit.qregs if isinstance(reg, QuantumRegister) ] qreg_indeces = { reg.name: sum(qreg_sizes[0:i]) for i, reg in enumerate(input_circuit.qregs) } op_graph = {} # Unroll circuit in case of composite gates raw_gates = [] for gate in input_circuit.data: if isinstance(gate, CompositeGate): raw_gates += gate.instruction_list() else: raw_gates += [gate] for i, uncompiled_gate in enumerate(raw_gates): if not hasattr(uncompiled_gate, 'params') or len(uncompiled_gate.params) < 1: continue if uncompiled_gate.name == 'snapshot': continue regs = [(reg, qubit) for (reg, qubit) in uncompiled_gate.qargs] qubits = [ qubit + qreg_indeces[reg.name] for reg, qubit in regs if isinstance(reg, QuantumRegister) ] gate_type = uncompiled_gate.name type_and_qubits = gate_type + qubits.__str__() op_graph[type_and_qubits] = \ op_graph.get(type_and_qubits, []) + [i] mapping = {} for compiled_gate_index, compiled_gate in enumerate( qobj.experiments[circ_num].instructions): if not hasattr(compiled_gate, 'params') or len(compiled_gate.params) < 1: continue if compiled_gate.name == 'snapshot': continue type_and_qubits = compiled_gate.name + compiled_gate.qubits.__str__( ) if len(op_graph[type_and_qubits]) > 0: uncompiled_gate_index = op_graph[type_and_qubits].pop(0) uncompiled_gate = raw_gates[uncompiled_gate_index] regs = [(reg, qubit) for (reg, qubit) in uncompiled_gate.qargs] qubits = [ qubit + qreg_indeces[reg.name] for reg, qubit in regs if isinstance(reg, QuantumRegister) ] if (compiled_gate.name == uncompiled_gate.name) and ( compiled_gate.qubits.__str__() == qubits.__str__()): mapping[compiled_gate_index] = uncompiled_gate_index else: raise Exception( "Circuit shape does not match qobj, found extra {} instruction in qobj" .format(type_and_qubits)) self.mappings[chunk][circ_num] = mapping for type_and_qubits, ops in op_graph.items(): if len(ops) > 0: raise Exception( "Circuit shape does not match qobj, found extra {} in circuit" .format(type_and_qubits)) if self.cache_file is not None and len(self.cache_file) > 0: cache_handler = open(self.cache_file, 'wb') qobj_dicts = [qobj_to_dict(qob) for qob in self.qobjs] pickle.dump({ 'qobjs': qobj_dicts, 'mappings': self.mappings }, cache_handler, protocol=pickle.HIGHEST_PROTOCOL) cache_handler.close() logger.debug("Circuit cache saved to file: {}".format( self.cache_file))
def __init__(self, backend, job_id, api, is_device, qobj=None, creation_date=None, **kwargs): """IBMQJob init function. We can instantiate jobs from two sources: A QObj, and an already submitted job returned by the API servers. Args: api (IBMQuantumExperience): IBM Q API is_device (bool): whether backend is a real device # TODO: remove this after Qobj qobj (Qobj): The Quantum Object. See notes below job_id (String): The job ID of an already submitted job. Pass `None` if you are creating a new one. creation_date(String): When the job was run. backend (str): The backend instance used to run this job. kwargs (dict): You can pass `backend_name` to this function although it has been deprecated. Notes: It is mandatory to pass either ``qobj`` or ``job_id``. Passing a ``qobj`` will ignore ``job_id`` and will create an instance representing an already-created job retrieved from the API server. """ if 'backend_name' in kwargs: warnings.warn( 'Passing the parameter `backend_name` is deprecated, ' 'pass the `backend` parameter with the instance of ' 'the backend running the job.', DeprecationWarning) super().__init__(backend, job_id) self._job_data = None if qobj is not None: validate_qobj_against_schema(qobj) self._qobj_payload = qobj_to_dict(qobj, version='1.0.0') # TODO: No need for this conversion, just use the new equivalent members above old_qobj = qobj_to_dict(qobj, version='0.0.1') self._job_data = { 'circuits': old_qobj['circuits'], 'hpc': old_qobj['config'].get('hpc'), 'seed': old_qobj['circuits'][0]['config']['seed'], 'shots': old_qobj['config']['shots'], 'max_credits': old_qobj['config']['max_credits'] } self._future_captured_exception = None self._api = api self._backend = backend self._status = JobStatus.INITIALIZING # In case of not providing a qobj, it assumes job_id has been provided # and query the API for updating the status. if qobj is None: self.status() self._queue_position = None self._cancelled = False self._is_device = is_device def current_utc_time(): """Gets the current time in UTC format""" datetime.datetime.utcnow().replace( tzinfo=datetime.timezone.utc).isoformat() self._creation_date = creation_date or current_utc_time() self._future = None self._api_error_msg = None