def test_api_auth_token(self): ''' Authentication with Quantum Experience Platform ''' api = IBMQuantumExperience(API_TOKEN) credential = api.check_credentials() self.assertTrue(credential)
def test_api_devices_availables(self): ''' Check the devices availables ''' api = IBMQuantumExperience(API_TOKEN) devices = api.available_devices() self.assertGreaterEqual(len(devices), 2)
def test_api_device_parameters(self): ''' Check the parameters of calibration of a real chip ''' api = IBMQuantumExperience(API_TOKEN) parameters = api.device_parameters() self.assertIsNotNone(parameters)
def test_api_device_status(self): ''' Check the status of a real chip ''' api = IBMQuantumExperience(API_TOKEN) is_available = api.device_status() self.assertIsNotNone(is_available)
def test_api_device_calibration(self): ''' Check the calibration of a real chip ''' api = IBMQuantumExperience(API_TOKEN) calibration = api.device_calibration() self.assertIsNotNone(calibration)
def test_api_backend_simulators_available(self): ''' Check the backend simulators available ''' api = IBMQuantumExperience(API_TOKEN) backends = api.available_backend_simulators() self.assertGreaterEqual(len(backends), 1)
def test_api_get_jobs(self): ''' Check get jobs by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) jobs = api.get_jobs(2) self.assertEqual(len(jobs), 2)
def test_api_backends_availables(self): ''' Check the backends availables ''' api = IBMQuantumExperience(API_TOKEN) backends = api.available_backends() self.assertGreaterEqual(len(backends), 2)
def _setup_api(self, token, url): try: self.__api = IBMQuantumExperience(token, {"url": url}) return True except BaseException: print('---- Error: Exception connect to servers ----') return False
def test_api_run_job(self): ''' Check run an job by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) device = 'simulator' shots = 1 job = api.run_job(qasms, device, shots) self.assertIsNotNone(job['status'])
def test_api_run_experiment(self): ''' Check run an experiment by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) device = 'simulator' shots = 1 experiment = api.run_experiment(qasm, device, shots) self.assertIsNotNone(experiment['status'])
def test_api_run_experiment_with_seed(self): ''' Check run an experiment with seed by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) device = 'simulator' shots = 1 seed = 815 experiment = api.run_experiment(qasm, device, shots, seed=seed) self.assertEqual(int(experiment['result']['extraInfo']['seed']), seed)
def test_api_run_job_fail_device(self): ''' Check run an job by user authenticated is not run because the device does not exist ''' api = IBMQuantumExperience(API_TOKEN) device = 'real5' shots = 1 job = api.run_job(qasms, device, shots) self.assertIsNotNone(job['error'])
def test_api_get_my_credits(self): ''' Check the credits of the user ''' api = IBMQuantumExperience(API_TOKEN) my_credits = api.get_my_credits() check_credits = None if 'remaining' in my_credits: check_credits = my_credits['remaining'] self.assertIsNotNone(check_credits)
def test_api_run_experiment_fail_device(self): ''' Check run an experiment by user authenticated is not run because the device does not exist ''' api = IBMQuantumExperience(API_TOKEN) device = '5qreal' shots = 1 experiment = api.run_experiment(qasm, device, shots) self.assertIsNotNone(experiment['error'])
def test_api_run_experiment(self): ''' Check run an experiment by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) backend = api.available_backend_simulators()[0]['name'] shots = 1 experiment = api.run_experiment(self.qasm, backend, shots) check_status = None if 'status' in experiment: check_status = experiment['status'] self.assertIsNotNone(check_status)
def test_api_run_job(self): ''' Check run an job by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) backend = 'simulator' shots = 1 job = api.run_job(self.qasms, backend, shots) check_status = None if 'status' in job: check_status = job['status'] self.assertIsNotNone(check_status)
def test_api_run_experiment_with_seed(self): ''' Check run an experiment with seed by user authenticated ''' api = IBMQuantumExperience(API_TOKEN) backend = api.available_backend_simulators()[0]['name'] shots = 1 seed = 815 experiment = api.run_experiment(self.qasm, backend, shots, seed=seed) check_seed = -1 if ('result' in experiment) and \ ('extraInfo' in experiment['result']) and \ ('seed' in experiment['result']['extraInfo']): check_seed = int(experiment['result']['extraInfo']['seed']) self.assertEqual(check_seed, seed)
def test_run_remote_simulator_compile(self): quantum_job = QuantumJob(self.qc, do_compile=True, backend='ibmqx_qasm_simulator') api = IBMQuantumExperience(self.QE_TOKEN, {"url": self.QE_URL}, verify=True) jobprocessor.run_remote_backend(quantum_job.qobj, api)
def setUpClass(cls): super().setUpClass() try: import Qconfig cls.QE_TOKEN = Qconfig.APItoken cls.QE_URL = Qconfig.config['url'] except ImportError: if 'QE_TOKEN' in os.environ: cls.QE_TOKEN = os.environ['QE_TOKEN'] if 'QE_URL' in os.environ: cls.QE_URL = os.environ['QE_URL'] n_circuits = 20 min_depth = 1 max_depth = 40 min_qubits = 1 max_qubits = 5 random_circuits = RandomCircuitGenerator(100, min_qubits=min_qubits, max_qubits=max_qubits, min_depth=min_depth, max_depth=max_depth) random_circuits.add_circuits(n_circuits) cls.rqg = random_circuits if hasattr(cls, 'QE_TOKEN'): cls._api = IBMQuantumExperience(cls.QE_TOKEN, {"url": cls.QE_URL}, verify=True) qiskit.backends.discover_remote_backends(cls._api)
def test_run_remote_simulator(self): compiled_circuit = openquantumcompiler.compile(self.qc.qasm()) quantum_job = QuantumJob(compiled_circuit, do_compile=False, backend='ibmqx_qasm_simulator') api = IBMQuantumExperience(self.QE_TOKEN, {"url": self.QE_URL}, verify=True) jobprocessor.run_remote_backend(quantum_job.qobj, api)
def _set_api(token, config): api = IBMQuantumExperience(token=token, config=config) qiskit.register(token=token, url=config.get('url'), hub=config.get('hub'), group=config.get('group'), project=config.get('project')) return api
def test_api_run_job_fail_backend(self): ''' Check run an job by user authenticated is not run because the backend does not exist ''' api = IBMQuantumExperience(API_TOKEN) backend = 'real5' shots = 1 self.assertRaises(BadBackendError, api.run_job, self.qasms, backend, shots)
def __init__(self, q_jobs, callback, max_workers=1, token=None, url=None, api=None): """ Args: q_jobs (list(QuantumJob)): List of QuantumJob objects. callback (fn(results)): The function that will be called when all jobs finish. The signature of the function must be: fn(results) results: A list of Result objects. max_workers (int): The maximum number of workers to use. token (str): Server API token url (str): Server URL. api (IBMQuantumExperience): API instance to use. If set, /token/ and /url/ are ignored. """ self.q_jobs = q_jobs self.max_workers = max_workers # check whether any jobs are remote self._local_backends = backends.local_backends() self.online = any(qj.backend not in self._local_backends for qj in q_jobs) self.futures = {} self.lock = Lock() # Set a default dummy callback just in case the user doesn't want # to pass any callback. self.callback = (lambda rs: ()) if callback is None else callback self.num_jobs = len(self.q_jobs) self.jobs_results = [] if self.online: self._api = api if api else IBMQuantumExperience( token, {"url": url}, verify=True) self._online_backends = remote_backends(self._api) # Check for the existance of the backend for q_job in q_jobs: if q_job.backend not in self._online_backends + self._local_backends: raise QISKitError("Backend %s not found!" % q_job.backend) self._api_config = {} self._api_config["token"] = token self._api_config["url"] = {"url": url} else: self._api = None self._online_backends = None self._api_config = None if self.online: # I/O intensive -> use ThreadedPoolExecutor self.executor_class = futures.ThreadPoolExecutor else: # CPU intensive -> use ProcessPoolExecutor self.executor_class = futures.ProcessPoolExecutor
def test_quantum_program_online(self, QE_TOKEN, QE_URL): self._init_api(QE_TOKEN, QE_URL) qp = QuantumProgram() qr = qp.create_quantum_register('qr', 2) cr = qp.create_classical_register('cr', 2) qc = qp.create_circuit('qc', [qr], [cr]) qc.h(qr[0]) qc.measure(qr[0], cr[0]) backend = 'ibmqx_qasm_simulator' # the backend to run on shots = 1024 # the number of shots in the experiment. api = IBMQuantumExperience(QE_TOKEN, {'url': QE_URL}) qiskit.backends.discover_remote_backends(api) _ = qp.execute(['qc'], backend=backend, shots=shots, seed=78)
def set_api(self, token, url, verify=True): """ Setup the API. Does not catch exceptions from IBMQuantumExperience. Args: Token (str): The token used to register on the online backend such as the quantum experience. URL (str): The url used for online backend such as the quantum experience. Returns: Nothing but fills the __ONLINE_BACKENDS, __api, and __api_config """ self.__api = IBMQuantumExperience(token, {"url": url}, verify) self.__ONLINE_BACKENDS = self.online_backends() self.__api_config["token"] = token self.__api_config["url"] = {"url": url}
def _set_api(token, config): api = IBMQuantumExperience(token=token, config=config) _ = qiskit.backends.update_backends(api) return api
import json from IBMQuantumExperience.IBMQuantumExperience import IBMQuantumExperience token = "a6c65f024279c033c9368bd14e6f9079b71fdfd00625b1f3d3573475c97d84d429c8b4316aa572709dd2670e9a069e735f127342360a7ec6bd8f17f82a997ba2" qe: IBMQuantumExperience = IBMQuantumExperience(token) print(qe.available_backends()) print(qe.check_credentials()) print(qe.backend_parameters("ibmqx2"))
class QuantumProgram(object): """ Quantum Program Class Class internal properties """ __online_devices = [ "real", "ibmqx2", "ibmqx3", "simulator", "ibmqx_qasm_simulator" ] __local_devices = ["local_unitary_simulator", "local_qasm_simulator"] __quantum_program = {} __api = {} __api_config = {} """ Elements that are not python identifiers or string constants are denoted by "--description (type)--". For example, a circuit's name is denoted by "--circuit name (string)--" and might have the value "teleport". __quantum_program = { "circuits": { --circuit name (string)--: { "circuit": --circuit object (TBD)--, "execution": { #### FILLED IN AFTER RUN -- JAY WANTS THIS MOVED DOWN ONE LAYER #### --device name (string)--: { "coupling_map": --adjacency list (dict)--, "basis_gates": --comma separated gate names (string)--, "compiled_circuit": --compiled quantum circuit (currently QASM text)--, "shots": --shots (int)--, "max_credits": --credits (int)--, "result": { "data": { #### DATA CAN BE A DIFFERENT DICTIONARY FOR EACH BACKEND #### "counts": {’00000’: XXXX, ’00001’: XXXXX}, "time" : xx.xxxxxxxx }, "date" : "2017−05−09Txx:xx:xx.xxxZ", "status": --status (string)-- } }, } } } __to_execute = { --device name (string)--: [ { "name": --circuit name (string)--, "coupling_map": --adjacency list (dict)--, "basis_gates": --comma separated gate names (string)--, "compiled_circuit": --compiled quantum circuit (currently QASM text)--, "shots": --shots (int)--, "max_credits": --credits (int)-- "seed": --initial seed for the simulator (int) -- }, ... ] } """ # -- FUTURE IMPROVEMENTS -- # TODO: for status results choose ALL_CAPS, or This but be consistent # TODO: coupling_map, basis_gates will move to compiled_circuit object # TODO: compiled_circuit is currently QASM text. In the future we will # make a method in the QuantumCircuit object that makes an object # to be passed to the runner and this will live in compiled_circuit. def __init__(self, specs=None, name=""): self.__quantum_program = {"circuits": {}} self.__quantum_registers = {} self.__classical_registers = {} self.__init_circuit = None self.__last_device_backend = "" self.__to_execute = {} self.mapper = mapper if specs: self.__init_specs(specs) # API functions def get_api_config(self): """Return the program specs""" return self.__api.req.credential.config def _setup_api(self, token, url): try: self.__api = IBMQuantumExperience(token, {"url": url}) return True except BaseException: print('---- Error: Exception connect to servers ----') return False def set_api(self, token=None, url=None): """Set the API conf""" if not token: token = self.__api_config["token"] else: self.__api_config["token"] = token if not url: url = self.__api_config["url"] else: self.__api_config["url"] = {"url": url} api = self._setup_api(token, url) return api def set_api_token(self, token): """ Set the API Token """ self.set_api(token=token) def set_api_url(self, url): """ Set the API url """ self.set_api(url=url) def get_device_status(self, device): """Return the online device status via QX API call device is the name of the real chip """ if device in self.__online_devices: return self.__api.device_status(device) else: return {"status": "Error", "result": "This device doesn't exist"} def get_device_calibration(self, device): """Return the online device calibrations via QX API call device is the name of the real chip """ if device in self.__online_devices: return self.__api.device_calibration(device) else: return {"status": "Error", "result": "This device doesn't exist"} # Building parts of the program def create_quantum_registers(self, name, size): """Create a new set of Quantum Registers""" self.__quantum_registers[name] = QuantumRegister(name, size) print(">> quantum_registers created:", name, size) return self.__quantum_registers[name] def create_quantum_registers_group(self, registers_array): """Create a new set of Quantum Registers based on a array of that""" new_registers = [] for register in registers_array: register = self.create_quantum_registers(register["name"], register["size"]) new_registers.append(register) return new_registers def create_classical_registers(self, name, size): """Create a new set of Classical Registers""" self.__classical_registers[name] = ClassicalRegister(name, size) print(">> classical_registers created:", name, size) return self.__classical_registers[name] def create_classical_registers_group(self, registers_array): """Create a new set of Classical Registers based on a array of that""" new_registers = [] for register in registers_array: new_registers.append( self.create_classical_registers(register["name"], register["size"])) return new_registers def create_circuit(self, name, qregisters=None, cregisters=None, circuit_object=None): """Create a new Quantum Circuit into the Quantum Program name is a string, the name of the circuit qregisters is an Array of Quantum Registers, can be String, by name or the object reference cregisters is an Array of Classical Registers, can be String, by name or the object reference """ if not qregisters: qregisters = [] if not cregisters: cregisters = [] if not circuit_object: circuit_object = QuantumCircuit() self.__quantum_program['circuits'][name] = { "name": name, "circuit": circuit_object } for register in qregisters: if isinstance(register, str): self.__quantum_program['circuits'][name]['circuit'].add( self.__quantum_registers[register]) else: self.__quantum_program['circuits'][name]['circuit'].add( register) for register in cregisters: if isinstance(register, str): self.__quantum_program['circuits'][name]['circuit'].add( self.__classical_registers[register]) else: self.__quantum_program['circuits'][name]['circuit'].add( register) return self.__quantum_program['circuits'][name]['circuit'] def get_quantum_registers(self, name): """Return a Quantum Register by name""" return self.__quantum_registers[name] def get_classical_registers(self, name): """Return a Classical Register by name""" return self.__classical_registers[name] def get_circuit(self, name): """Return a Circuit Object by name""" return self.__quantum_program['circuits'][name]['circuit'] def get_quantum_elements(self, specs=None): """Return the basic elements, Circuit, Quantum Registers, Classical Registers""" return self.__init_circuit, \ self.__quantum_registers[list(self.__quantum_registers)[0]], \ self.__classical_registers[list(self.__classical_registers)[0]] def load_qasm(self, name="", qasm_file=None, basis_gates=None): """ Load qasm file qasm_file qasm file name """ if not qasm_file: print('"Not filename provided') return {"status": "Error", "result": "Not filename provided"} if not basis_gates: basis_gates = "u1,u2,u3,cx,id" # QE target basis if name == "": name = qasm_file circuit_object = qasm.Qasm(filename=qasm_file).parse() # Node (AST) # TODO: add method to convert to QuantumCircuit object from Node self.__quantum_program['circuits'][name] = {"circuit": circuit_object} return {"status": "COMPLETED", "result": 'all done'} def __init_specs(self, specs): """Populate the Quantum Program Object with initial Specs""" quantumr = [] classicalr = [] if "api" in specs: if specs["api"]["token"]: self.__api_config["token"] = specs["api"]["token"] if specs["api"]["url"]: self.__api_config["url"] = specs["api"]["url"] if "circuits" in specs: for circuit in specs["circuits"]: quantumr = self.create_quantum_registers_group( circuit["quantum_registers"]) classicalr = self.create_classical_registers_group( circuit["classical_registers"]) self.__init_circuit = self.create_circuit( name=circuit["name"], qregisters=quantumr, cregisters=classicalr) else: if "quantum_registers" in specs: print(">> quantum_registers created") quantumr = specs["quantum_registers"] self.create_quantum_registers(quantumr["name"], quantumr["size"]) if "classical_registers" in specs: print(">> quantum_registers created") classicalr = specs["classical_registers"] self.create_classical_registers(classicalr["name"], classicalr["size"]) if quantumr and classicalr: self.create_circuit(name=specs["name"], qregisters=quantumr["name"], cregisters=classicalr["name"]) def add_circuit(self, name, circuit_object): """Add a new circuit based on an Object representation. name is the name or index of one circuit.""" self.__quantum_program['circuits'][name] = { "name": name, "circuit": circuit_object } return circuit_object def get_qasm_image(self, circuit): """Get image circuit representation from API.""" pass def get_qasm(self, name): """get the circut by name. name of the circuit""" if name in self.__quantum_program['circuits']: return self.__quantum_program['circuits'][name]['circuit'].qasm() else: return {"status": "Error", "result": 'Circuit not found'} def get_qasms(self, list_circuit_name): """get the circut by name. name of the circuit""" qasm_source = [] for name in list_circuit_name: qasm_source.append(self.get_qasm(name)) return qasm_source # Compiling methods def unroller_code(self, circuit, basis_gates=None): """ Unroll the code circuit is circuits to unroll basis_gates are the base gates, which by default are: u1,u2,u3,cx,id """ if not basis_gates: basis_gates = "u1,u2,u3,cx,id" # QE target basis unrolled_circuit = unroll.Unroller( qasm.Qasm(data=circuit.qasm()).parse(), unroll.CircuitBackend(basis_gates.split(","))) unrolled_circuit.execute() circuit_unrolled = unrolled_circuit.backend.circuit # circuit DAG qasm_source = circuit_unrolled.qasm(qeflag=True) return qasm_source, circuit_unrolled def compile(self, name_of_circuits, device="local_qasm_simulator", shots=1024, max_credits=3, basis_gates=None, coupling_map=None, seed=None): """Compile the name_of_circuits by names. name_of_circuits is a list of circuit names to compile. device is the target device name. basis_gates are the base gates by default are: u1,u2,u3,cx,id coupling_map is the adjacency list for coupling graph This method adds elements of the following form to the self.__to_execute list corresponding to the device: --device name (string)--: [ { "name": --circuit name (string)--, "coupling_map": --adjacency list (dict)--, "basis_gates": --comma separated gate names (string)--, "compiled_circuit": --compiled quantum circuit (currently QASM text)--, "shots": --shots (int)--, "max_credits": --credits (int)-- "seed": --initial seed for the simulator (int) -- }, ... ] } """ if name_of_circuits == []: return {"status": "Error", "result": 'No circuits'} for name in name_of_circuits: if name not in self.__quantum_program["circuits"]: return { "status": "Error", "result": "%s not in QuantumProgram" % name } # TODO: The circuit object has to have .qasm() method (be careful) qasm_compiled, dag_unrolled = self.unroller_code( self.__quantum_program['circuits'][name]['circuit'], basis_gates) if coupling_map: print("pre-mapping properties: %s" % dag_unrolled.property_summary()) # Insert swap gates coupling = self.mapper.Coupling(coupling_map) dag_unrolled, final_layout = self.mapper.swap_mapper( dag_unrolled, coupling) print("layout: %s" % final_layout) # Expand swaps qasm_compiled, dag_unrolled = self.unroller_code(dag_unrolled) # Change cx directions dag_unrolled = mapper.direction_mapper(dag_unrolled, coupling) # Simplify cx gates mapper.cx_cancellation(dag_unrolled) # Simplify single qubit gates dag_unrolled = mapper.optimize_1q_gates(dag_unrolled) qasm_compiled = dag_unrolled.qasm(qeflag=True) print("post-mapping properties: %s" % dag_unrolled.property_summary()) # TODO: add timestamp, compilation if device not in self.__to_execute: self.__to_execute[device] = [] job = {} job["name"] = name job["coupling_map"] = coupling_map job["basis_gates"] = basis_gates job["shots"] = shots job["max_credits"] = max_credits # TODO: This will become a new compiled circuit object in the # future. See future improvements at the top of this # file. job["compiled_circuit"] = qasm_compiled job["seed"] = random.random() if seed is not None: job["seed"] = seed self.__to_execute[device].append(job) return {"status": "COMPLETED", "result": 'all done'} def get_compiled_qasm(self, name, device=None): """Get the compiled qasm for the named circuit and device. If device is None, it defaults to the last device. """ if not device: device = self.__last_device_backend try: return self.__quantum_program["circuits"][name]["execution"][ device]["compiled_circuit"] except KeyError: return "No compiled qasm for this circuit" def print_execution_list(self, verbose=False): """Print the compiled circuits that are ready to run. verbose controls how much is returned. """ for device, jobs in self.__to_execute.items(): print("%s:" % device) for job in jobs: print(" %s:" % job["name"]) print(" shots = %d" % job["shots"]) print(" max_credits = %d" % job["max_credits"]) print(" seed (simulator only) = %d" % job["seed"]) if verbose: print(" compiled_circuit =") print("// *******************************************") print(job["compiled_circuit"], end="") print("// *******************************************") #runners def run(self, wait=5, timeout=60): """Run a program (a pre-compiled quantum program). All input for run comes from self.__to_execute wait time is how long to check if the job is completed timeout is time until the execution stopa """ for backend in self.__to_execute: self.__last_device_backend = backend if backend in self.__online_devices: last_shots = -1 last_max_credits = -1 jobs = [] for job in self.__to_execute[backend]: jobs.append({'qasm': job["compiled_circuit"]}) shots = job["shots"] max_credits = job["max_credits"] if last_shots == -1: last_shots = shots else: if last_shots != shots: # Clear the list of compiled programs to execute self.__to_execute = {} return { "status": "Error", "result": 'Online devices only support job batches with equal numbers of shots' } if last_max_credits == -1: last_max_credits = max_credits else: if last_max_credits != max_credits: # Clear the list of compiled programs to execute self.__to_execute = {} return { "status": "Error", "result": 'Online devices only support job batches with equal max credits' } print("running on backend: %s" % (backend)) output = self.__api.run_job(jobs, backend, last_shots, last_max_credits) if 'error' in output: # Clear the list of compiled programs to execute self.__to_execute = {} return {"status": "Error", "result": output['error']} job_result = self.wait_for_job(output['id'], wait=wait, timeout=timeout) if job_result['status'] == 'Error': # Clear the list of compiled programs to execute self.__to_execute = {} return job_result else: jobs = [] for job in self.__to_execute[backend]: jobs.append({ "compiled_circuit": job["compiled_circuit"], "shots": job["shots"], "seed": job["seed"] }) print("running on backend: %s" % (backend)) if backend == "local_qasm_simulator": job_result = self.run_local_qasm_simulator(jobs) elif backend == "local_unitary_simulator": job_result = self.run_local_unitary_simulator(jobs) else: # Clear the list of compiled programs to execute self.__to_execute = {} return { "status": "Error", "result": 'Not a local simulator' } assert len(self.__to_execute[backend]) == len( job_result["qasms"] ), "Internal error in QuantumProgram.run(), job_result" # Fill data into self.__quantum_program for this backend index = 0 for job in self.__to_execute[backend]: name = job["name"] if name not in self.__quantum_program["circuits"]: # Clear the list of compiled programs to execute self.__to_execute = {} return { "status": "Error", "result": "Internal error, circuit not found" } if not "execution" in self.__quantum_program["circuits"][name]: self.__quantum_program["circuits"][name]["execution"] = {} # We override the results if backend not in self.__quantum_program["circuits"][name][ "execution"]: self.__quantum_program["circuits"][name]["execution"][ backend] = {} # TODO: return date, executionId, ... for field in [ "coupling_map", "basis_gates", "compiled_circuit", "shots", "max_credits", "seed" ]: self.__quantum_program["circuits"][name]["execution"][ backend][field] = job[field] self.__quantum_program["circuits"][name]["execution"][backend][ "result"] = job_result["qasms"][index]["result"] self.__quantum_program["circuits"][name]["execution"][backend][ "status"] = job_result["qasms"][index]["status"] index += 1 # Clear the list of compiled programs to execute self.__to_execute = {} return {"status": "COMPLETED", "result": 'all done'} def wait_for_job(self, jobid, wait=5, timeout=60): """Wait until all status results are 'COMPLETED'. jobids is a list of id strings. api is an IBMQuantumExperience object. wait is the time to wait between requests, in seconds timeout is how long we wait before failing, in seconds Returns an list of results that correspond to the jobids. """ timer = 0 timeout_over = False job = self.__api.get_job(jobid) while job['status'] == 'RUNNING': if timer == timeout: return {"status": "Error", "result": "Time Out"} time.sleep(wait) timer += wait print("status = %s (%d seconds)" % (job['status'], timer)) job = self.__api.get_job(jobid) if job['status'] == 'ERROR_CREATING_JOB' or job[ 'status'] == 'ERROR_RUNNING_JOB': return {"status": "Error", "result": job['status']} # Get the results return job def run_local_qasm_simulator(self, jobs): """run_local_qasm_simulator, run a program (precompile of quantum circuits). jobs is list of dicts {"compiled_circuit": simulator input data, "shots": integer num shots} returns job_results = { "qasms": [ { "result": DATA, "status": DATA, }, ... ] } """ job_results = {"qasms": []} for job in jobs: one_result = {'result': None, 'status': "Error"} qasm_circuit = simulators.QasmSimulator(job["compiled_circuit"], job["shots"], job["seed"]).run() one_result["result"] = {} one_result["result"]["data"] = qasm_circuit["data"] one_result["status"] = qasm_circuit["status"] job_results['qasms'].append(one_result) return job_results def run_local_unitary_simulator(self, jobs): """run_local_unitary_simulator, run a program (precompile of quantum circuits). jobs is list of dicts {"compiled_circuit": simulator input data} returns job_results = { "qasms": [ { "result": DATA, "status": DATA, }, ... ] } """ job_results = {"qasms": []} for job in jobs: one_result = {'result': None, 'status': "Error"} unitary_circuit = simulators.UnitarySimulator( job["compiled_circuit"]).run() one_result["result"] = {} one_result["result"]["data"] = unitary_circuit["data"] one_result["status"] = unitary_circuit["status"] job_results['qasms'].append(one_result) return job_results def execute(self, name_of_circuits, device="local_qasm_simulator", shots=1024, max_credits=3, wait=5, timeout=60, basis_gates=None, coupling_map=None, seed=None): """Execute, compile, and run a program (array of quantum circuits). program is a list of quantum_circuits api is the api for the device device is a string for real or simulator shots is the number of shots max_credits is the maximum credits for the experiments basis_gates are the base gates, which by default are: u1,u2,u3,cx,id """ self.compile(name_of_circuits, device, shots, max_credits, basis_gates, coupling_map, seed) output = self.run(wait, timeout) return output # method to process the data def get_result(self, name, device=None): """get the get_result from one circut and backend name of the circuit device that is use to compile, run, or execute """ if not device: device = self.__last_device_backend if name in self.__quantum_program["circuits"]: return self.__quantum_program["circuits"][name]['execution'][ device]['result'] else: return {"status": "Error", "result": 'Circuit not found'} def get_data(self, name, device=None): """Get the dict of labels and counts from the output of get_job. results are the list of results name is the name or index of one circuit.""" if not device: device = self.__last_device_backend return self.__quantum_program["circuits"][name]['execution'][device][ 'result']['data'] def get_counts(self, name, device=None): """Get the dict of labels and counts from the output of get_job. name is the name or index of one circuit.""" if not device: device = self.__last_device_backend try: return self.__quantum_program["circuits"][name]['execution'][ device]['result']['data']['counts'] except KeyError: return {"status": "Error", "result": 'Error in circuit name'} def plotter(self, name, device=None, method="histogram", number_to_keep=None): """Plot the results method: histogram/qsphere circuit: Print one circuit """ data = self.get_counts(name, device) if method == "histogram": basicplotter.plot_histogram(data, number_to_keep) else: pass # TODO: add basicplotter.plot_qsphere(data) for unitary simulator def average_data(self, name, observable): """Compute the mean value of an diagonal observable. Takes in an observable in dictionary format and then calculates the sum_i value(i) P(i) where value(i) is the value of the observable for state i. returns a double """ counts = self.get_counts(name) temp = 0 tot = sum(counts.values()) for key in counts: if key in observable: temp += counts[key] * observable[key] / tot return temp
def _init_api(self, QE_TOKEN, QE_URL): api = IBMQuantumExperience(QE_TOKEN, {"url": QE_URL}, verify=True) qiskit.backends.discover_remote_backends(api)
sys.path.append('IBMQuantumExperience') sys.path.append('../IBMQuantumExperience') # pylint: disable=C0413 try: if sys.version_info.major > 2: # Python 3 from IBMQuantumExperience.IBMQuantumExperience import IBMQuantumExperience # noqa else: # Python 2 from IBMQuantumExperience import IBMQuantumExperience # noqa from IBMQuantumExperience.IBMQuantumExperience import ApiError from IBMQuantumExperience.IBMQuantumExperience import BadBackendError # noq except: sys.stderr.write( "IBMQuantumExperience library not installed. Use : pip install IBMQuantumExperience" ) sys.exit(0) api = IBMQuantumExperience(sys.argv[1]) # my_credits = api.get_my_credits() # print "Credits: ", ast.literal_eval(json.dumps(my_credits)) # print "Backend: ",api.available_backend_simulators()[0]['name'] # qasm = 'OPENQASM 2.0;\n\ninclude "qelib1.inc";\nqreg q[5];\ncreg c[5];\nh q[0];\ncx q[0],q[2];\nmeasure q[0] -> c[0];\nmeasure q[2] -> c[1];\n' # qasm = 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[5];\ncreg c[5];\nh q[0];\nx q[1];\ncx q[0],q[1];\nmeasure q[0] -> c[0];\nmeasure q[1] -> c[1];' # print "qasm: ",qasm backend = api.available_backend_simulators()[0]['name'] shots = sys.argv[2] experiment = api.run_experiment(qasm, backend, shots) print ast.literal_eval(json.dumps(experiment))
class QuantumProgram(object): """Quantum Program Class. Class internal properties. Elements that are not python identifiers or string constants are denoted by "--description (type)--". For example, a circuit's name is denoted by "--circuit name (string)--" and might have the value "teleport". Internal:: __quantum_registers (list[dic]): An dictionary of quantum registers used in the quantum program. __quantum_registers = { --register name (string)--: QuantumRegistor, } __classical_registers (list[dic]): An ordered list of classical registers used in the quantum program. __classical_registers = { --register name (string)--: ClassicalRegistor, } __quantum_program (dic): An dictionary of quantum circuits __quantum_program = { --circuit name (string)--: --circuit object --, } __init_circuit (obj): A quantum circuit object for the initial quantum circuit __ONLINE_BACKENDS (list[str]): A list of online backends __LOCAL_BACKENDS (list[str]): A list of local backends """ # -- FUTURE IMPROVEMENTS -- # TODO: for status results make ALL_CAPS (check) or some unified method # TODO: Jay: coupling_map, basis_gates will move into a config object # only exists once you set the api to use the online backends __api = {} __api_config = {} def __init__(self, specs=None): self.__quantum_registers = {} self.__classical_registers = {} self.__quantum_program = {} # stores all the quantum programs self.__init_circuit = None # stores the intial quantum circuit of the # program self.__ONLINE_BACKENDS = [] self.__LOCAL_BACKENDS = self.local_backends() self.mapper = mapper if specs: self.__init_specs(specs) ############################################################### # methods to initiate an build a quantum program ############################################################### def __init_specs(self, specs, verbose=False): """Populate the Quantum Program Object with initial Specs. Args: specs (dict): Q_SPECS = { "circuits": [{ "name": "Circuit", "quantum_registers": [{ "name": "qr", "size": 4 }], "classical_registers": [{ "name": "cr", "size": 4 }] }], verbose (bool): controls how information is returned. Returns: Sets up a quantum circuit. """ quantumr = [] classicalr = [] if "circuits" in specs: for circuit in specs["circuits"]: quantumr = self.create_quantum_registers( circuit["quantum_registers"]) classicalr = self.create_classical_registers( circuit["classical_registers"]) self.create_circuit(name=circuit["name"], qregisters=quantumr, cregisters=classicalr) # TODO: Jay: I think we should return function handles for the registers # and circuit. So that we dont need to get them after we create them # with get_quantum_register etc def create_quantum_register(self, name, size, verbose=False): """Create a new Quantum Register. Args: name (str): the name of the quantum register size (int): the size of the quantum register verbose (bool): controls how information is returned. Returns: internal reference to a quantum register in __quantum_register s """ if name in self.__quantum_registers: if size != len(self.__quantum_registers[name]): raise QISKitError("Can't make this register: Already in" " program with different size") if verbose == True: print(">> quantum_register exists:", name, size) else: if verbose == True: print(">> new quantum_register created:", name, size) self.__quantum_registers[name] = QuantumRegister(name, size) return self.__quantum_registers[name] def create_quantum_registers(self, register_array): """Create a new set of Quantum Registers based on a array of them. Args: register_array (list[dict]): An array of quantum registers in dictionay format:: "quantum_registers": [ { "name": "qr", "size": 4 }, ... ] Returns: Array of quantum registers objects """ new_registers = [] for register in register_array: register = self.create_quantum_register( register["name"], register["size"]) new_registers.append(register) return new_registers def create_classical_register(self, name, size, verbose=False): """Create a new Classical Register. Args: name (str): the name of the quantum register size (int): the size of the quantum register verbose (bool): controls how information is returned. Returns: internal reference to a quantum register in __quantum_register """ if name in self.__classical_registers: if size != len(self.__classical_registers[name]): raise QISKitError("Can't make this register: Already in" " program with different size") if verbose == True: print(">> classical register exists:", name, size) else: if verbose == True: print(">> new classical register created:", name, size) self.__classical_registers[name] = ClassicalRegister(name, size) return self.__classical_registers[name] def create_classical_registers(self, registers_array): """Create a new set of Classical Registers based on a array of them. Args: register_array (list[dict]): An array of classical registers in dictionay fromat:: "classical_registers": [ { "name": "qr", "size": 4 }, ... ] Returns: Array of clasical registers objects """ new_registers = [] for register in registers_array: new_registers.append(self.create_classical_register( register["name"], register["size"])) return new_registers def create_circuit(self, name, qregisters=None, cregisters=None): """Create a empty Quantum Circuit in the Quantum Program. Args: name (str): the name of the circuit qregisters list(object): is an Array of Quantum Registers by object reference cregisters list(object): is an Array of Classical Registers by object reference Returns: A quantum circuit is created and added to the Quantum Program """ if not qregisters: qregisters = [] if not cregisters: cregisters = [] quantum_circuit = QuantumCircuit() if not self.__init_circuit: self.__init_circuit = quantum_circuit for register in qregisters: quantum_circuit.add(register) for register in cregisters: quantum_circuit.add(register) self.add_circuit(name, quantum_circuit) return self.__quantum_program[name] def add_circuit(self, name, quantum_circuit): """Add a new circuit based on an Object representation. Args: name (str): the name of the circuit to add. quantum_circuit: a quantum circuit to add to the program-name Returns: the quantum circuit is added to the object. """ for qname, qreg in quantum_circuit.get_qregs().items(): self.create_quantum_register(qname, len(qreg)) for cname, creg in quantum_circuit.get_cregs().items(): self.create_classical_register(cname, len(creg)) self.__quantum_program[name] = quantum_circuit def load_qasm_file(self, qasm_file, name=None, verbose=False): """ Load qasm file into the quantum program. Args: qasm_file (str): a string for the filename including its location. name (str or None, optional): the name of the quantum circuit after loading qasm text into it. If no name is give the name is of the text file. verbose (bool, optional): controls how information is returned. Retuns: Adds a quantum circuit with the gates given in the qasm file to the quantum program and returns the name to be used to get this circuit """ if not os.path.exists(qasm_file): raise QISKitError('qasm file "{0}" not found'.format(qasm_file)) if not name: name = os.path.splitext(os.path.basename(qasm_file))[0] node_circuit = qasm.Qasm(filename=qasm_file).parse() # Node (AST) if verbose == True: print("circuit name: " + name) print("******************************") print(node_circuit.qasm()) # current method to turn it a DAG quantum circuit. basis_gates = "u1,u2,u3,cx,id" # QE target basis unrolled_circuit = unroll.Unroller(node_circuit, unroll.CircuitBackend(basis_gates.split(","))) circuit_unrolled = unrolled_circuit.execute() self.add_circuit(name, circuit_unrolled) return name def load_qasm_text(self, qasm_string, name=None, verbose=False): """ Load qasm string in the quantum program. Args: qasm_string (str): a string for the file name. name (str): the name of the quantum circuit after loading qasm text into it. If no name is give the name is of the text file. verbose (bool): controls how information is returned. Retuns: Adds a quantum circuit with the gates given in the qasm string to the quantum program. """ node_circuit = qasm.Qasm(data=qasm_string).parse() # Node (AST) if not name: # Get a random name if none is give name = "".join([random.choice(string.ascii_letters+string.digits) for n in range(10)]) if verbose == True: print("circuit name: " + name) print("******************************") print(node_circuit.qasm()) # current method to turn it a DAG quantum circuit. basis_gates = "u1,u2,u3,cx,id" # QE target basis unrolled_circuit = unroll.Unroller(node_circuit, unroll.CircuitBackend(basis_gates.split(","))) circuit_unrolled = unrolled_circuit.execute() self.add_circuit(name, circuit_unrolled) return name ############################################################### # methods to get elements from a QuantumProgram ############################################################### def get_quantum_register(self, name): """Return a Quantum Register by name. Args: name (str): the name of the quantum circuit Returns: The quantum registers with this name """ try: return self.__quantum_registers[name] except KeyError: raise KeyError('No quantum register "{0}"'.format(name)) def get_classical_register(self, name): """Return a Classical Register by name. Args: name (str): the name of the quantum circuit Returns: The classical registers with this name """ try: return self.__classical_registers[name] except KeyError: raise KeyError('No classical register "{0}"'.format(name)) def get_quantum_register_names(self): """Return all the names of the quantum Registers.""" return self.__quantum_registers.keys() def get_classical_register_names(self): """Return all the names of the classical Registers.""" return self.__classical_registers.keys() def get_circuit(self, name): """Return a Circuit Object by name Args: name (str): the name of the quantum circuit Returns: The quantum circuit with this name """ try: return self.__quantum_program[name] except KeyError: raise KeyError('No quantum circuit "{0}"'.format(name)) def get_circuit_names(self): """Return all the names of the quantum circuits.""" return self.__quantum_program.keys() def get_qasm(self, name): """Get qasm format of circuit by name. Args: name (str): name of the circuit Returns: The quantum circuit in qasm format """ quantum_circuit = self.get_circuit(name) return quantum_circuit.qasm() def get_qasms(self, list_circuit_name): """Get qasm format of circuit by list of names. Args: list_circuit_name (list[str]): names of the circuit Returns: List of quantum circuit in qasm format """ qasm_source = [] for name in list_circuit_name: qasm_source.append(self.get_qasm(name)) return qasm_source def get_initial_circuit(self): """Return the initialization Circuit.""" return self.__init_circuit ############################################################### # methods for working with backends ############################################################### def set_api(self, token, url, verify=True): """ Setup the API. Does not catch exceptions from IBMQuantumExperience. Args: Token (str): The token used to register on the online backend such as the quantum experience. URL (str): The url used for online backend such as the quantum experience. Returns: Nothing but fills the __ONLINE_BACKENDS, __api, and __api_config """ self.__api = IBMQuantumExperience(token, {"url": url}, verify) self.__ONLINE_BACKENDS = self.online_backends() self.__api_config["token"] = token self.__api_config["url"] = {"url": url} def get_api_config(self): """Return the program specs.""" return self.__api_config def get_api(self): """Returns a function handle to the API.""" return self.__api def save(self, file_name=None, beauty=False): """ Save Quantum Program in a Json file. Args: file_name (str): file name and path. beauty (boolean): save the text with indent 4 to make it readable. Returns: The dictionary with the status and result of the operation Raises: When you don't provide a correct file name raise a LookupError. When something happen with the file management raise a LookupError. """ if file_name is None: error = {"status": "Error", "result": "Not filename provided"} raise LookupError(error['result']) if beauty: indent = 4 else: indent = 0 elemements_to_save = self.__quantum_program elements_saved = {} for circuit in elemements_to_save: elements_saved[circuit] = {} elements_saved[circuit]["qasm"] = elemements_to_save[circuit].qasm() try: with open(file_name, 'w') as save_file: json.dump(elements_saved, save_file, indent = indent) return {'status': 'Done', 'result': elemements_to_save} except ValueError: error = {'status': 'Error', 'result': 'Some Problem happened to save the file'} raise LookupError(error['result']) def load(self, file_name=None): """ Load Quantum Program Json file into the Quantum Program object. Args: file_name (str): file name and path. Returns: The dictionary with the status and result of the operation Raises: When you don't provide a correct file name raise a LookupError. When something happen with the file management raise a LookupError. """ if file_name is None: error = {"status": "Error", "result": "Not filename provided"} raise LookupError(error['result']) elemements_to_load = {} try: with open(file_name, 'r') as load_file: elemements_loaded = json.load(load_file) for circuit in elemements_loaded: circuit_qasm = elemements_loaded[circuit]["qasm"] elemements_loaded[circuit] = qasm.Qasm(data=circuit_qasm).parse() self.__quantum_program = elemements_loaded return {"status": 'Done', 'result': self.__quantum_program} except ValueError: error = {'status': 'Error', 'result': 'Some Problem happened to load the file'} raise LookupError(error['result']) def available_backends(self): """All the backends that are seen by QISKIT.""" return self.__ONLINE_BACKENDS + self.__LOCAL_BACKENDS def local_backends(self): """Get the local backends.""" return simulators._localsimulator.local_backends() def online_backends(self): """Get the online backends. Queries network API if it exists and gets the backends that are online. Returns: List of online backends if the online api has been set or an empty list of it has not been set. """ if self.get_api(): return [backend['name'] for backend in self.__api.available_backends() ] else: return [] def online_simulators(self): """Gets online simulators via QX API calls. Returns: List of online simulator names. """ simulators = [] if self.get_api(): for backend in self.__api.available_backends(): if backend['simulator']: simulators.append(backend['name']) return simulators def online_devices(self): """Gets online devices via QX API calls. Returns: List of online simulator names. """ devices = [] if self.get_api(): for backend in self.__api.available_backends(): if not backend['simulator']: devices.append(backend['name']) return devices def get_backend_status(self, backend): """Return the online backend status. It uses QX API call or by local backend is the name of the local or online simulator or experiment. Args: banckend (str): The backend to check """ if backend in self.__ONLINE_BACKENDS: return self.__api.backend_status(backend) elif backend in self.__LOCAL_BACKENDS: return {'available': True} else: err_str = 'the backend "{0}" is not available'.format(backend) raise ValueError(err_str) def get_backend_configuration(self, backend, list_format=False): """Return the configuration of the backend. The return is via QX API call. Args: backend (str): Name of the backend. Returns: The configuration of the named backend. Raises: If a configuration for the named backend can't be found raise a LookupError. """ if self.get_api(): configuration_edit = {} for configuration in self.__api.available_backends(): if configuration['name'] == backend: for key in configuration: new_key = convert(key) # TODO: removed these from the API code if new_key not in ['id', 'serial_number', 'topology_id', 'status', 'coupling_map']: configuration_edit[new_key] = configuration[key] if new_key == 'coupling_map': if configuration[key] == 'all-to-all': configuration_edit[new_key] = \ configuration[key] else: if not list_format: cmap = mapper.coupling_list2dict( configuration[key]) else: cmap = configuration[key] configuration_edit[new_key] = cmap return configuration_edit for configuration in simulators.local_configuration: if configuration['name'] == backend: return configuration raise LookupError( 'backend configuration for "{0}" not found'.format(backend)) def get_backend_calibration(self, backend): """Return the online backend calibrations. The return is via QX API call. Args: backend (str): Name of the backend. Returns: The configuration of the named backend. Raises: If a configuration for the named backend can't be found raise a LookupError. """ if backend in self.__ONLINE_BACKENDS: calibrations = self.__api.backend_calibration(backend) calibrations_edit = {} for key, vals in calibrations.items(): new_key = convert(key) calibrations_edit[new_key] = vals return calibrations_edit elif backend in self.__LOCAL_BACKENDS: return {'backend': backend, 'calibrations': None} else: raise LookupError( 'backend calibration for "{0}" not found'.format(backend)) def get_backend_parameters(self, backend): """Return the online backend parameters. The return is via QX API call. Args: backend (str): Name of the backend. Returns: The configuration of the named backend. Raises: If a configuration for the named backend can't be found raise a LookupError. """ if backend in self.__ONLINE_BACKENDS: parameters = self.__api.backend_parameters(backend) parameters_edit = {} for key, vals in parameters.items(): new_key = convert(key) parameters_edit[new_key] = vals return parameters_edit elif backend in self.__LOCAL_BACKENDS: return {'backend': backend, 'parameters': None} else: raise LookupError( 'backend parameters for "{0}" not found'.format(backend)) ############################################################### # methods to compile quantum programs into qobj ############################################################### def compile(self, name_of_circuits, backend="local_qasm_simulator", config=None, silent=True, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=3, seed=None, qobjid=None): """Compile the circuits into the exectution list. This builds the internal "to execute" list which is list of quantum circuits to run on different backends. Args: name_of_circuits (list[str]): circuit names to be compiled. backend (str): a string representing the backend to compile to config (dict): a dictionary of configurations parameters for the compiler silent (bool): is an option to print out the compiling information or not basis_gates (str): a comma seperated string and are the base gates, which by default are: u1,u2,u3,cx,id coupling_map (dict): A directed graph of coupling:: { control(int): [ target1(int), target2(int), , ... ], ... } eg. {0: [2], 1: [2], 3: [2]} initial_layout (dict): A mapping of qubit to qubit:: { ("q", strart(int)): ("q", final(int)), ... } eg. { ("q", 0): ("q", 0), ("q", 1): ("q", 1), ("q", 2): ("q", 2), ("q", 3): ("q", 3) } shots (int): the number of shots max_credits (int): the max credits to use 3, or 5 seed (int): the intial seed the simulatros use Returns: the job id and populates the qobj:: qobj = { id: --job id (string), config: -- dictionary of config settings (dict)--, { "max_credits" (online only): -- credits (int) --, "shots": -- number of shots (int) --. "backend": -- backend name (str) -- } circuits: [ { "name": --circuit name (string)--, "compiled_circuit": --compiled quantum circuit (DAG format)--, "config": --dictionary of additional config settings (dict)--, { "coupling_map": --adjacency list (dict)--, "basis_gates": --comma separated gate names (string)--, "layout": --layout computed by mapper (dict)--, "seed": (simulator only)--initial seed for the simulator (int)--, } }, ... ] } """ # TODO: Jay: currently basis_gates, coupling_map, initial_layout, shots, # max_credits and seed are extra inputs but I would like them to go # into the config. qobj = {} if not qobjid: qobjid = "".join([random.choice(string.ascii_letters+string.digits) for n in range(30)]) qobj['id'] = qobjid qobj["config"] = {"max_credits": max_credits, 'backend': backend, "shots": shots} qobj["circuits"] = [] if not name_of_circuits: raise ValueError('"name_of_circuits" must be specified') if isinstance(name_of_circuits, str): name_of_circuits = [name_of_circuits] for name in name_of_circuits: if name not in self.__quantum_program: raise QISKitError('circuit "{0}" not found in program'.format(name)) if not basis_gates: basis_gates = "u1,u2,u3,cx,id" # QE target basis # TODO: The circuit object going into this is to have .qasm() method (be careful) dag_circuit = self._unroller_code(self.__quantum_program[name], basis_gates=basis_gates) final_layout = None # if a coupling map is given compile to the map if coupling_map: if not silent: print("pre-mapping properties: %s" % dag_circuit.property_summary()) # Insert swap gates coupling = self.mapper.Coupling(coupling_map) if not silent: print("initial layout: %s" % initial_layout) dag_circuit, final_layout = self.mapper.swap_mapper( dag_circuit, coupling, initial_layout, trials=20, verbose=False) if not silent: print("final layout: %s" % final_layout) # Expand swaps dag_circuit = self._unroller_code(dag_circuit) # Change cx directions dag_circuit = mapper.direction_mapper(dag_circuit, coupling) # Simplify cx gates mapper.cx_cancellation(dag_circuit) # Simplify single qubit gates dag_circuit = mapper.optimize_1q_gates(dag_circuit) if not silent: print("post-mapping properties: %s" % dag_circuit.property_summary()) # making the job to be added to qojj job = {} job["name"] = name # config parameters used by the runner if config is None: config = {} # default to empty config dict job["config"] = config # TODO: Jay: make config options optional for different backends job["config"]["coupling_map"] = mapper.coupling_dict2list(coupling_map) # Map the layout to a format that can be json encoded list_layout = None if final_layout: list_layout = [[k, v] for k, v in final_layout.items()] job["config"]["layout"] = list_layout job["config"]["basis_gates"] = basis_gates if seed is None: job["config"]["seed"] = random.getrandbits(128) # int.from_bytes(os.urandom(4), byteorder="big") else: job["config"]["seed"] = seed # the compuled circuit to be run saved as a dag job["compiled_circuit"] = self._dag2json(dag_circuit) job["compiled_circuit_qasm"] = dag_circuit.qasm(qeflag=True) # add job to the qobj qobj["circuits"].append(job) return qobj def get_execution_list(self, qobj, verbose=False): """Print the compiled circuits that are ready to run. Args: verbose (bool): controls how much is returned. """ if not qobj: if verbose: print("no exectuions to run") execution_list_all = {} execution_list = [] if verbose: print("id: %s" % qobj['id']) print("backend: %s" % qobj['config']['backend']) print("qobj config:") for key in qobj['config']: if key != 'backend': print(' '+ key + ': ' + str(qobj['config'][key])) for circuit in qobj['circuits']: execution_list.append(circuit["name"]) if verbose: print(' circuit name: ' + circuit["name"]) print(' circuit config:') for key in circuit['config']: print(' '+ key + ': ' + str(circuit['config'][key])) return execution_list def get_compiled_configuration(self, qobj, name): """Get the compiled layout for the named circuit and backend. Args: name (str): the circuit name qobj (str): the name of the qobj Returns: the config of the circuit. """ try: for index in range(len(qobj["circuits"])): if qobj["circuits"][index]['name'] == name: return qobj["circuits"][index]["config"] except KeyError: raise QISKitError('No compiled configurations for circuit "{0}"'.format(name)) def get_compiled_qasm(self, qobj, name): """Print the compiled cricuit in qasm format. Args: qobj (str): the name of the qobj name (str): name of the quantum circuit """ try: for index in range(len(qobj["circuits"])): if qobj["circuits"][index]['name'] == name: return qobj["circuits"][index]["compiled_circuit_qasm"] except KeyError: raise QISKitError('No compiled qasm for circuit "{0}"'.format(name)) def _dag2json(self, dag_circuit): """Make a Json representation of the circuit. Takes a circuit dag and returns json circuit obj. This is an internal function. Args: dag_ciruit (dag object): a dag representation of the circuit Returns: the json version of the dag """ # TODO: Jay: I think this needs to become a method like .qasm() for the DAG. circuit_string = dag_circuit.qasm(qeflag=True) basis_gates = "u1,u2,u3,cx,id" # QE target basis unroller = unroll.Unroller(qasm.Qasm(data=circuit_string).parse(), unroll.JsonBackend(basis_gates.split(","))) json_circuit = unroller.execute() return json_circuit def _unroller_code(self, dag_ciruit, basis_gates=None): """ Unroll the code. Circuit is the circuit to unroll using the DAG representation. This is an internal function. Args: dag_ciruit (dag object): a dag representation of the circuit basis_gates (str): a comma seperated string and are the base gates, which by default are: u1,u2,u3,cx,id Return: dag_ciruit (dag object): a dag representation of the circuit unrolled to basis gates """ if not basis_gates: basis_gates = "u1,u2,u3,cx,id" # QE target basis unrolled_circuit = unroll.Unroller(qasm.Qasm(data=dag_ciruit.qasm()).parse(), unroll.DAGBackend(basis_gates.split(","))) dag_circuit_unrolled = unrolled_circuit.execute() return dag_circuit_unrolled ############################################################### # methods to run quantum programs (run ) ############################################################### def run(self, qobj, wait=5, timeout=60, silent=True): """Run a program (a pre-compiled quantum program). All input for run comes from qobj Args: qobj(dict): the dictionary of the quantum object to run wait (int): wait time is how long to check if the job is completed timeout (int): is time until the execution stops silent (bool): is an option to print out the running information or not Returns: status done and populates the internal __quantum_program with the data """ backend = qobj['config']['backend'] if not silent: print("running on backend: %s" % (backend)) if backend in self.__ONLINE_BACKENDS: max_credits = qobj["config"]["max_credits"] shots = qobj["config"]["shots"] jobs = [] for job in qobj["circuits"]: jobs.append({'qasm': job["compiled_circuit_qasm"]}) output = self.__api.run_job(jobs, backend, shots, max_credits) if 'error' in output: raise ResultError(output['error']) qobj_result = self._wait_for_job(output['id'], wait=wait, timeout=timeout, silent=silent) else: # making a list of jobs just for local backends. Name is droped # but the list is made ordered jobs = [] for job in qobj["circuits"]: jobs.append({"compiled_circuit": job["compiled_circuit"], "config": {**job["config"], **qobj["config"]}}) qobj_result = self._run_local_simulator(backend, jobs, silent) if qobj_result['status'] == 'COMPLETED': assert len(qobj["circuits"]) == len(qobj_result['result']), ( 'Internal error in QuantumProgram.run(), job_result') results = Result(qobj_result, qobj) return results def _wait_for_job(self, jobid, wait=5, timeout=60, silent=True): """Wait until all online ran jobs are 'COMPLETED'. Args: jobids: is a list of id strings. wait (int): is the time to wait between requests, in seconds timeout (int): is how long we wait before failing, in seconds silent (bool): is an option to print out the running information or not Returns: Dictionary of form:: job_result_return = [ { "data": DATA, "status": DATA, }, ... ] """ timer = 0 timeout_over = False job_result = self.__api.get_job(jobid) if 'status' not in job_result: from pprint import pformat raise QISKitError("get_job didn't return status: %s" % (pformat(job))) while job_result['status'] == 'RUNNING': if timer >= timeout: return {"status": "ERROR", "result": ["Time Out"]} time.sleep(wait) timer += wait if not silent: print("status = %s (%d seconds)" % (job_result['status'], timer)) job_result = self.__api.get_job(jobid) if 'status' not in job_result: from pprint import pformat raise QISKitError("get_job didn't return status: %s" % (pformat(job_result))) if job_result['status'] == 'ERROR_CREATING_JOB' or job_result['status'] == 'ERROR_RUNNING_JOB': return {"status": "ERROR", "result": [job_result['status']]} # Get the results job_result_return = [] for index in range(len(job_result["qasms"])): job_result_return.append({"data": job_result["qasms"][index]["data"], "status": job_result["qasms"][index]["status"]}) return {'status': job_result['status'], 'result': job_result_return} def _run_local_simulator(self, backend, jobs, silent=True): """Run a program of compiled quantum circuits on the local machine. Args: backend (str): the name of the local simulator to run jobs: list of dicts {"compiled_circuit": simulator input data, "config": integer num shots} Returns: Dictionary of form, job_results = [ { "data": DATA, "status": DATA, }, ... ] """ job_results = [] for job in jobs: local_simulator = simulators.LocalSimulator(backend, job) local_simulator.run(silent=silent) this_result = local_simulator.result() job_results.append(this_result) return {'status': 'COMPLETED', 'result': job_results} def execute(self, name_of_circuits, backend="local_qasm_simulator", config=None, wait=5, timeout=60, silent=True, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=3, seed=None): """Execute, compile, and run an array of quantum circuits). This builds the internal "to execute" list which is list of quantum circuits to run on different backends. Args: name_of_circuits (list[str]): circuit names to be compiled. backend (str): a string representing the backend to compile to config (dict): a dictionary of configurations parameters for the compiler wait (int): wait time is how long to check if the job is completed timeout (int): is time until the execution stops silent (bool): is an option to print out the compiling information or not basis_gates (str): a comma seperated string and are the base gates, which by default are: u1,u2,u3,cx,id coupling_map (dict): A directed graph of coupling:: { control(int): [ target1(int), target2(int), , ... ], ... } eg. {0: [2], 1: [2], 3: [2]} initial_layout (dict): A mapping of qubit to qubit { ("q", strart(int)): ("q", final(int)), ... } eg. { ("q", 0): ("q", 0), ("q", 1): ("q", 1), ("q", 2): ("q", 2), ("q", 3): ("q", 3) } shots (int): the number of shots max_credits (int): the max credits to use 3, or 5 seed (int): the intial seed the simulatros use Returns: status done and populates the internal __quantum_program with the data """ # TODO: Jay: currently basis_gates, coupling_map, intial_layout, shots, # max_credits, and seed are extra inputs but I would like them to go # into the config qobj = self.compile(name_of_circuits, backend=backend, config=config, silent=silent, basis_gates=basis_gates, coupling_map=coupling_map, initial_layout=initial_layout, shots=shots, max_credits=max_credits, seed=seed) result = self.run(qobj, wait=wait, timeout=timeout, silent=silent) return result