From 99b81e5d838cb14cd49b13a48aace8981deeccd8 Mon Sep 17 00:00:00 2001 From: Ali Javadi-Abhari Date: Mon, 11 Jun 2018 04:28:37 -0400 Subject: [PATCH 1/5] fix circuit_drawer for linux/mac (#554) --- qiskit/tools/visualization.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/tools/visualization.py b/qiskit/tools/visualization.py index e44dc492f6b2..f345e2dab323 100644 --- a/qiskit/tools/visualization.py +++ b/qiskit/tools/visualization.py @@ -731,12 +731,12 @@ def circuit_drawer(circuit, 'Skipping circuit drawing...') else: try: + base = os.path.join(tmpdirname, filename) subprocess.run(["pdftocairo", "-singlefile", "-png", "-q", - "{}".format(os.path.join(tmpdirname, filename + '.pdf'))]) - pngfile = os.path.join(tmpdirname, "{0}.png".format(filename)) - im = Image.open(pngfile) + base + '.pdf', base]) + im = Image.open(base + '.png') im = trim(im) - os.remove(pngfile) + os.remove(base + '.png') except OSError as e: if e.errno == os.errno.ENOENT: logger.warning('WARNING: Unable to convert pdf to image. ' From ae01c908f3193be8903ccfd19042908fb5114fc6 Mon Sep 17 00:00:00 2001 From: Ali Javadi-Abhari Date: Mon, 11 Jun 2018 05:53:39 -0400 Subject: [PATCH 2/5] Preserve i->i initial layout when coupling_map satisfied (#527) * Preserve i->i initial layout when already satisfied. * skip_translation deprecated in favor of skip_transpiler. --- CHANGELOG.rst | 1 + qiskit/_compiler.py | 75 ++++++++++++++++--- qiskit/_gate.py | 19 +++++ qiskit/_instruction.py | 2 +- qiskit/_quantumprogram.py | 23 ++++-- qiskit/wrapper/_wrapper.py | 27 +++++-- ...translation.py => test_skip_transpiler.py} | 10 +-- 7 files changed, 127 insertions(+), 30 deletions(-) rename test/python/{test_skip_translation.py => test_skip_transpiler.py} (84%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6c9ead856bc7..4e32e0af05fe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -45,6 +45,7 @@ Fixed - Fix hardcoded backend mapping tests (#521) - Removed ``_modifiers call`` from ``reapply`` (#534) - Fix circuit drawer issue with filename location on windows (#543) +- Change initial qubit layout only if the backend coupling map is not satisfied (#527) `0.5.3`_ - 2018-05-29 diff --git a/qiskit/_compiler.py b/qiskit/_compiler.py index 7d1910332c4c..656c38417a3c 100644 --- a/qiskit/_compiler.py +++ b/qiskit/_compiler.py @@ -12,6 +12,7 @@ import copy import uuid +import warnings import numpy as np import scipy.sparse as sp @@ -27,6 +28,7 @@ from .unroll import DagUnroller, DAGBackend, JsonBackend, Unroller, CircuitBackend from .mapper import (Coupling, optimize_1q_gates, coupling_list2dict, swap_mapper, cx_cancellation, direction_mapper) +from ._gate import Gate logger = logging.getLogger(__name__) @@ -34,7 +36,7 @@ def compile(circuits, backend, config=None, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=10, seed=None, qobj_id=None, hpc=None, - skip_translation=False): + skip_transpiler=False, skip_translation=False): """Compile a list of circuits into a qobj. FIXME THIS FUNCTION WILL BE REWRITTEN IN VERSION 0.6. It will be a thin wrapper @@ -52,8 +54,9 @@ def compile(circuits, backend, seed (int): random seed for simulators qobj_id (int): identifier for the generated qobj hpc (dict): HPC simulator parameters - skip_translation (bool): If True, bypass most of the compilation process and + skip_transpiler (bool): If True, bypass most of the compilation process and creates a qobj with minimal check nor translation + skip_translation (bool): DEPRECATED. Use skip_transpiler instead. Returns: obj: the qobj to be run on the backends @@ -62,6 +65,12 @@ def compile(circuits, backend, QISKitError: if any of the circuit names cannot be found on the Quantum Program. """ + if skip_translation: + warnings.warn( + "skip_translation will be called skip_transpiler in future versions.", + DeprecationWarning) + skip_transpiler = True + if isinstance(circuits, QuantumCircuit): circuits = [circuits] @@ -114,22 +123,18 @@ def compile(circuits, backend, else: job["config"]["seed"] = seed - if skip_translation: # Just return the qobj, without any transformation or analysis + if skip_transpiler: # Just return the qobj, without any transformation or analysis job["config"]["layout"] = None job["compiled_circuit_qasm"] = circuit.qasm() job["compiled_circuit"] = DagUnroller( DAGCircuit.fromQuantumCircuit(circuit), JsonBackend(job['config']['basis_gates'].split(','))).execute() else: - # Pick good initial layout if None is given and not simulator if initial_layout is None and not backend.configuration['simulator']: - best_sub = best_subset(backend, num_qubits) - initial_layout = {} - map_iter = 0 - for key, value in circuit.get_qregs().items(): - for i in range(value.size): - initial_layout[(key, i)] = ('q', best_sub[map_iter]) - map_iter += 1 + # if coupling_map is not already satisfied, pick a good initial layout + # otherwise leave as q[i]->q[i] + if not _matches_coupling_map(circuit.data, backend.configuration['coupling_map']): + initial_layout = _pick_best_layout(backend, num_qubits, circuit.get_qregs()) dag_circuit, final_layout = compile_circuit( circuit, @@ -265,7 +270,7 @@ def load_unroll_qasm_file(filename, basis_gates='u1,u2,u3,cx,id'): return circuit_unrolled -def best_subset(backend, n_qubits): +def _best_subset(backend, n_qubits): """Computes the qubit mapping with the best connectivity. @@ -317,6 +322,52 @@ def best_subset(backend, n_qubits): return best_map +def _matches_coupling_map(instructions, coupling_map): + """ Iterate over circuit instructions set to check if multi-qubit instruction coupling + graphs match the qubit coupling graph in the backend. + + Parameters: + instructions (List): List of circuit instructions. Not all instructions + are Gates, hence not multi-qubit. + coupling_map (List): Backend coupling map, represented as an + adjacency list. + + Returns: + True: If there's at least one instruction that uses multiple qubits and + these qubits coupling matches one of the backend couplings. + + False: If there's no match between any of the instructions multiple qubits + coupling, and one of the couplings from the backend. + """ + for instruction in instructions: + if isinstance(instruction, Gate) and instruction.is_multi_qubit(): + if instruction.get_qubit_coupling() not in coupling_map: + return False + return True + + +def _pick_best_layout(backend, num_qubits, qregs): + """ Pick a convenient layout depending on the best matching qubit connectivity + + Parameters: + backend (BaseBackend) : The backend with the coupling_map for searching + num_qubits (int): Number of qubits + qregs (List): The list of quantum registers + + Returns: + initial_layout: A special ordered layout + + """ + best_sub = _best_subset(backend, num_qubits) + layout = {} + map_iter = 0 + for key, value in qregs.items(): + for i in range(value.size): + layout[(key, i)] = ('q', best_sub[map_iter]) + map_iter += 1 + return layout + + class QISKitCompilerError(QISKitError): """Exceptions raised during compilation""" pass diff --git a/qiskit/_gate.py b/qiskit/_gate.py index 800f21a787e4..16c79e778af6 100644 --- a/qiskit/_gate.py +++ b/qiskit/_gate.py @@ -24,10 +24,19 @@ def __init__(self, name, param, args, circuit=None): arg = list of pairs (Register, index) circuit = QuantumCircuit or CompositeGate containing this gate """ + self._is_multi_qubit = False + self._qubit_coupling = [] + number_of_arguments = 0 for argument in args: + number_of_arguments += 1 + if number_of_arguments > 1: + self._qubit_coupling.append(argument) + self._is_multi_qubit = True + if not isinstance(argument[0], QuantumRegister): raise QISKitError("argument not (QuantumRegister, int) " + "tuple") + super().__init__(name, param, args, circuit) def inverse(self): @@ -38,3 +47,13 @@ def q_if(self, *qregs): """Add controls to this gate.""" # pylint: disable=unused-argument raise QISKitError("control not implemented") + + def is_multi_qubit(self): + """Returns True if this Gate is uses multiple qubits as arguments""" + return self._is_multi_qubit + + def get_qubit_coupling(self): + """Gets the coupling graph of the qubits in case this is a multi-qubit gate""" + if not self.is_multi_qubit(): + raise QISKitError("Can't get the qubit coupling of non multi-qubit gates!") + return self._qubit_coupling diff --git a/qiskit/_instruction.py b/qiskit/_instruction.py index 96ec5859c35f..3a2d5ce3670a 100644 --- a/qiskit/_instruction.py +++ b/qiskit/_instruction.py @@ -47,7 +47,7 @@ def check_circuit(self): raise QISKitError("Instruction's circuit not assigned") def c_if(self, classical, val): - """Add classical control on register clasical and value val.""" + """Add classical control on register classical and value val.""" self.check_circuit() self.circuit._check_creg(classical) if val < 0: diff --git a/qiskit/_quantumprogram.py b/qiskit/_quantumprogram.py index 5aa650ad29a8..8698fceaa3cf 100644 --- a/qiskit/_quantumprogram.py +++ b/qiskit/_quantumprogram.py @@ -932,14 +932,19 @@ def get_backend_parameters(self, backend): def compile(self, name_of_circuits=None, backend="local_qasm_simulator", config=None, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=10, seed=None, - qobj_id=None, hpc=None, skip_translation=False): + qobj_id=None, hpc=None, skip_transpiler=False, skip_translation=False): """Compile the circuits into the execution list. .. deprecated:: 0.5 The `coupling_map` parameter as a dictionary will be deprecated in upcoming versions. Using the coupling_map as a list is recommended. """ - + # pylint: disable=missing-param-doc, missing-type-doc + if skip_translation: + warnings.warn( + "skip_translation will be called skip_transpiler in future versions.", + DeprecationWarning) + skip_transpiler = True if isinstance(coupling_map, dict): coupling_map = coupling_dict2list(coupling_map) warnings.warn( @@ -961,7 +966,7 @@ def compile(self, name_of_circuits=None, backend="local_qasm_simulator", qobj = qiskit.wrapper.compile(list_of_circuits, my_backend, config, basis_gates, coupling_map, initial_layout, shots, max_credits, seed, qobj_id, hpc, - skip_translation) + skip_transpiler) return qobj def reconfig(self, qobj, backend=None, config=None, shots=None, max_credits=None, seed=None): @@ -1126,7 +1131,7 @@ def _run_internal(self, qobj_list): def execute(self, name_of_circuits=None, backend="local_qasm_simulator", config=None, timeout=60, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, - max_credits=3, seed=None, hpc=None, skip_translation=False): + max_credits=3, seed=None, hpc=None, skip_transpiler=False, skip_translation=False): """Execute, compile, and run an array of quantum circuits). This builds the internal "to execute" list which is list of quantum @@ -1174,7 +1179,7 @@ def execute(self, name_of_circuits=None, backend="local_qasm_simulator", 'omp_num_threads': Numeric } - skip_translation (bool): If True, bypass most of the compilation process and + skip_transpiler (bool): If True, bypass most of the compilation process and creates a qobj with minimal check nor translation Returns: Result: status done and populates the internal __quantum_program with the data. @@ -1183,6 +1188,12 @@ def execute(self, name_of_circuits=None, backend="local_qasm_simulator", The `coupling_map` parameter as a dictionary will be deprecated in upcoming versions. Using the coupling_map as a list is recommended. """ + # pylint: disable=missing-param-doc, missing-type-doc + if skip_translation: + warnings.warn( + "skip_translation will be called skip_transpiler in future versions.", + DeprecationWarning) + skip_transpiler = True # 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 @@ -1191,7 +1202,7 @@ def execute(self, name_of_circuits=None, backend="local_qasm_simulator", basis_gates=basis_gates, coupling_map=coupling_map, initial_layout=initial_layout, shots=shots, max_credits=max_credits, seed=seed, - hpc=hpc, skip_translation=skip_translation) + hpc=hpc, skip_transpiler=skip_transpiler) result = self.run(qobj, timeout=timeout) return result diff --git a/qiskit/wrapper/_wrapper.py b/qiskit/wrapper/_wrapper.py index 1429075635d2..ca21b94e2eb4 100644 --- a/qiskit/wrapper/_wrapper.py +++ b/qiskit/wrapper/_wrapper.py @@ -8,6 +8,7 @@ """Helper module for simplified QISKit usage.""" import os +import warnings import qiskit._compiler from qiskit import QISKitError from qiskit.backends.ibmq.ibmqprovider import IBMQProvider @@ -135,7 +136,7 @@ def get_backend(name): def compile(circuits, backend, config=None, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=10, seed=None, qobj_id=None, hpc=None, - skip_translation=False): + skip_transpiler=False, skip_translation=False): """Compile a list of circuits into a qobj. Args: @@ -150,24 +151,31 @@ def compile(circuits, backend, seed (int): random seed for simulators qobj_id (int): identifier for the generated qobj hpc (dict): HPC simulator parameters - skip_translation (bool): If True, bypass most of the compilation process and + skip_transpiler (bool): If True, bypass most of the compilation process and creates a qobj with minimal check nor translation + skip_translation (bool): DEPRECATED. Use skip_transpiler instead. Returns: obj: the qobj to be run on the backends """ # pylint: disable=redefined-builtin + if skip_translation: + warnings.warn( + "skip_translation will be called skip_transpiler in future versions.", + DeprecationWarning) + skip_transpiler = True + if isinstance(backend, str): backend = _DEFAULT_PROVIDER.get_backend(backend) return qiskit._compiler.compile(circuits, backend, config, basis_gates, coupling_map, initial_layout, shots, max_credits, seed, qobj_id, hpc, - skip_translation) + skip_transpiler) def execute(circuits, backend, config=None, basis_gates=None, coupling_map=None, initial_layout=None, shots=1024, max_credits=10, seed=None, qobj_id=None, hpc=None, - skip_translation=False): + skip_transpiler=False, skip_translation=False): """Executes a set of circuits. Args: @@ -182,17 +190,24 @@ def execute(circuits, backend, seed (int): random seed for simulators qobj_id (int): identifier for the generated qobj hpc (dict): HPC simulator parameters - skip_translation (bool): skip most of the compile steps and produce qobj directly + skip_transpiler (bool): skip most of the compile steps and produce qobj directly Returns: BaseJob: returns job instance derived from BaseJob """ + # pylint: disable=missing-param-doc, missing-type-doc + if skip_translation: + warnings.warn( + "skip_translation will be called skip_transpiler in future versions.", + DeprecationWarning) + skip_transpiler = True + if isinstance(backend, str): backend = _DEFAULT_PROVIDER.get_backend(backend) qobj = compile(circuits, backend, config, basis_gates, coupling_map, initial_layout, shots, max_credits, seed, qobj_id, hpc, - skip_translation) + skip_transpiler) # XXX When qobj is done this should replace q_job q_job = QuantumJob(qobj, backend=backend, preformatted=True, resources={ 'max_credits': qobj['config']['max_credits']}) diff --git a/test/python/test_skip_translation.py b/test/python/test_skip_transpiler.py similarity index 84% rename from test/python/test_skip_translation.py rename to test/python/test_skip_transpiler.py index db62aa0ee130..bfa1f2ff5b8e 100644 --- a/test/python/test_skip_translation.py +++ b/test/python/test_skip_transpiler.py @@ -19,7 +19,7 @@ class CompileSkipTranslationTest(QiskitTestCase): def test_simple_compile(self): """ - Compares with and without skip_translation + Compares with and without skip_transpiler """ name = 'test_simple' qp = QuantumProgram() @@ -31,9 +31,9 @@ def test_simple_compile(self): qc.measure(qr, cr) rtrue = qp.compile([name], backend='local_qasm_simulator', shots=1024, - skip_translation=True) + skip_transpiler=True) rfalse = qp.compile([name], backend='local_qasm_simulator', shots=1024, - skip_translation=False) + skip_transpiler=False) self.assertEqual(rtrue['config'], rfalse['config']) self.assertEqual(rtrue['circuits'], rfalse['circuits']) @@ -48,8 +48,8 @@ def test_simple_execute(self): qc.u2(3.14, 1.57, qr[0]) qc.measure(qr, cr) - rtrue = qp.execute(name, seed=seed, skip_translation=True) - rfalse = qp.execute(name, seed=seed, skip_translation=False) + rtrue = qp.execute(name, seed=seed, skip_transpiler=True) + rfalse = qp.execute(name, seed=seed, skip_transpiler=False) self.assertEqual(rtrue.get_counts(), rfalse.get_counts()) From 26a897d603414df037101db5e9476064ecc32b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20M=2E=20Rodr=C3=ADguez?= Date: Mon, 11 Jun 2018 12:24:06 +0200 Subject: [PATCH 3/5] Bump api version to 1.9.2 (#558) --- requirements.txt | 2 +- setup.py.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8883ee46b901..954a4356a625 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -IBMQuantumExperience>=1.9.1 +IBMQuantumExperience>=1.9.2 matplotlib>=2.1,<2.2 networkx>=2.0,<2.1 numpy>=1.13,<1.15 diff --git a/setup.py.in b/setup.py.in index 74573fb2ab33..835c24e068f7 100755 --- a/setup.py.in +++ b/setup.py.in @@ -17,7 +17,7 @@ from setuptools.dist import Distribution requirements = [ - "IBMQuantumExperience>=1.9.1", + "IBMQuantumExperience>=1.9.2", "matplotlib>=2.1,<2.2", "networkx>=2.0,<2.1", "numpy>=1.13,<1.15", From 9d84eb40d7c5b9fd9f3fc5ff9b7291ddfc00765b Mon Sep 17 00:00:00 2001 From: Ali Javadi-Abhari Date: Mon, 11 Jun 2018 06:29:33 -0400 Subject: [PATCH 4/5] fix combining/extending of circuits with simulator instructions (#556) * Fix for simulator extension instructions * Adding tests for extending/combining circuits with init/barrier/snapshot --- CHANGELOG.rst | 1 + qiskit/_result.py | 63 ++++---- qiskit/extensions/simulator/load.py | 2 +- qiskit/extensions/simulator/noise.py | 2 +- qiskit/extensions/simulator/save.py | 2 +- qiskit/extensions/simulator/snapshot.py | 2 +- test/python/test_circuit_combine_extend.py | 176 +++++++++++++++++++++ test/python/test_quantumprogram.py | 126 --------------- 8 files changed, 213 insertions(+), 161 deletions(-) create mode 100644 test/python/test_circuit_combine_extend.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4e32e0af05fe..ce093168cd24 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -46,6 +46,7 @@ Fixed - Removed ``_modifiers call`` from ``reapply`` (#534) - Fix circuit drawer issue with filename location on windows (#543) - Change initial qubit layout only if the backend coupling map is not satisfied (#527) +- Fix issue with simulator extension commands not reapplying correctly (#556) `0.5.3`_ - 2018-05-29 diff --git a/qiskit/_result.py b/qiskit/_result.py index 2bbc7ba50dc5..0d63a34fed3b 100644 --- a/qiskit/_result.py +++ b/qiskit/_result.py @@ -160,7 +160,7 @@ def get_ran_qasm(self, name): pass raise QISKitError('No qasm for circuit "{0}"'.format(name)) - def get_data(self, circuit_name=None): + def get_data(self, circuit=None): """Get the data of circuit name. The data format will depend on the backend. For a real device it @@ -189,7 +189,7 @@ def get_data(self, circuit_name=None): XX + XXj]] Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. @@ -207,36 +207,36 @@ def get_data(self, circuit_name=None): raise exception else: raise QISKitError(str(exception)) - if isinstance(circuit_name, QuantumCircuit): - circuit_name = circuit_name.name + if isinstance(circuit, QuantumCircuit): + circuit = circuit.name - if circuit_name is None: + if circuit is None: circuits = list([i['name'] for i in self._qobj['circuits']]) if len(circuits) == 1: - circuit_name = circuits[0] + circuit = circuits[0] else: raise QISKitError("You have to select a circuit when there is more than" "one available") - if not isinstance(circuit_name, str): - circuit_name = str(circuit_name) + if not isinstance(circuit, str): + circuit = str(circuit) try: qobj = self._qobj for index in range(len(qobj['circuits'])): - if qobj['circuits'][index]['name'] == circuit_name: + if qobj['circuits'][index]['name'] == circuit: return self._result['result'][index]['data'] except (KeyError, TypeError): pass - raise QISKitError('No data for circuit "{0}"'.format(circuit_name)) + raise QISKitError('No data for circuit "{0}"'.format(circuit)) - def get_counts(self, circuit_name=None): + def get_counts(self, circuit=None): """Get the histogram data of circuit name. The data from the a qasm circuit is dictionary of the format {'00000': XXXX, '00001': XXXXX}. Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. @@ -247,18 +247,18 @@ def get_counts(self, circuit_name=None): QISKitError: if there are no counts for the circuit. """ try: - return self.get_data(circuit_name)['counts'] + return self.get_data(circuit)['counts'] except KeyError: - raise QISKitError('No counts for circuit "{0}"'.format(circuit_name)) + raise QISKitError('No counts for circuit "{0}"'.format(circuit)) - def get_statevector(self, circuit_name=None): + def get_statevector(self, circuit=None): """Get the final statevector of circuit name. The data is a list of complex numbers [1.+0.j, 0.+0.j]. Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. @@ -269,18 +269,18 @@ def get_statevector(self, circuit_name=None): QISKitError: if there is no statevector for the circuit. """ try: - return self.get_data(circuit_name)['statevector'] + return self.get_data(circuit)['statevector'] except KeyError: - raise QISKitError('No statevector for circuit "{0}"'.format(circuit_name)) + raise QISKitError('No statevector for circuit "{0}"'.format(circuit)) - def get_unitary(self, circuit_name=None): + def get_unitary(self, circuit=None): """Get the final unitary of circuit name. The data is a matrix of complex numbers [[1.+0.j, 0.+0.j], .. ]. Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. @@ -291,11 +291,11 @@ def get_unitary(self, circuit_name=None): QISKitError: if there is no unitary for the circuit. """ try: - return self.get_data(circuit_name)['unitary'] + return self.get_data(circuit)['unitary'] except KeyError: - raise QISKitError('No unitary for circuit "{0}"'.format(circuit_name)) + raise QISKitError('No unitary for circuit "{0}"'.format(circuit)) - def get_snapshots(self, circuit_name=None): + def get_snapshots(self, circuit=None): """Get snapshots recorded during the run. The data is a dictionary: @@ -303,7 +303,7 @@ def get_snapshots(self, circuit_name=None): and values are a dictionary of the snapshots themselves. Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. @@ -314,18 +314,19 @@ def get_snapshots(self, circuit_name=None): QISKitError: if there are no snapshots for the circuit. """ try: - return self.get_data(circuit_name)['snapshots'] + return self.get_data(circuit)['snapshots'] except KeyError: - raise QISKitError('No snapshots for circuit "{0}"'.format(circuit_name)) + raise QISKitError('No snapshots for circuit "{0}"'.format(circuit)) - def get_snapshot(self, circuit_name=None, slot=None): + def get_snapshot(self, slot=None, circuit=None): """Get snapshot at a specific slot. Args: - circuit_name (str or QuantumCircuit or None): reference to a quantum circuit + slot (str): snapshot slot to retrieve. If None and there is only one + slot, return that one. + circuit (str or QuantumCircuit or None): reference to a quantum circuit If None and there is only one circuit available, returns that one. - slot (str): snapshot slot to retrieve Returns: dict[slot: dict[str: array]]: list of 2^n_qubits complex amplitudes. @@ -334,7 +335,7 @@ def get_snapshot(self, circuit_name=None, slot=None): QISKitError: if there is no snapshot at all, or in this slot """ try: - snapshots_dict = self.get_snapshots(circuit_name) + snapshots_dict = self.get_snapshots(circuit) if slot is None: slots = list(snapshots_dict.keys()) @@ -356,7 +357,7 @@ def get_snapshot(self, circuit_name=None, slot=None): return snapshot_dict except KeyError: raise QISKitError('No snapshot at slot {0} for ' - 'circuit "{1}"'.format(slot, circuit_name)) + 'circuit "{1}"'.format(slot, circuit)) def get_names(self): """Get the circuit names of the results. diff --git a/qiskit/extensions/simulator/load.py b/qiskit/extensions/simulator/load.py index fe91ec02d76c..b8ccfdeacf78 100644 --- a/qiskit/extensions/simulator/load.py +++ b/qiskit/extensions/simulator/load.py @@ -42,7 +42,7 @@ def qasm(self): def reapply(self, circ): """Reapply this instruction to corresponding qubits in circ.""" - self._modifiers(circ.load(self.param[0], *self.arg)) + self._modifiers(circ.load(self.param[0])) def load(self, slot): diff --git a/qiskit/extensions/simulator/noise.py b/qiskit/extensions/simulator/noise.py index 15e2e80d0df1..4d6f78ccb19a 100644 --- a/qiskit/extensions/simulator/noise.py +++ b/qiskit/extensions/simulator/noise.py @@ -42,7 +42,7 @@ def qasm(self): def reapply(self, circ): """Reapply this instruction to corresponding qubits in circ.""" - self._modifiers(circ.noise(self.param[0], *self.arg)) + self._modifiers(circ.noise(self.param[0])) def noise(self, switch): diff --git a/qiskit/extensions/simulator/save.py b/qiskit/extensions/simulator/save.py index 659b42442819..8ffda7c3381a 100644 --- a/qiskit/extensions/simulator/save.py +++ b/qiskit/extensions/simulator/save.py @@ -42,7 +42,7 @@ def qasm(self): def reapply(self, circ): """Reapply this instruction to corresponding qubits in circ.""" - self._modifiers(circ.save(self.param[0], *self.arg)) + self._modifiers(circ.save(self.param[0])) def save(self, slot): diff --git a/qiskit/extensions/simulator/snapshot.py b/qiskit/extensions/simulator/snapshot.py index 83281c3965a1..ca845caa315d 100644 --- a/qiskit/extensions/simulator/snapshot.py +++ b/qiskit/extensions/simulator/snapshot.py @@ -42,7 +42,7 @@ def qasm(self): def reapply(self, circ): """Reapply this instruction to corresponding qubits in circ.""" - self._modifiers(circ.snapshot(self.param[0], *self.arg)) + self._modifiers(circ.snapshot(self.param[0])) def snapshot(self, slot): diff --git a/test/python/test_circuit_combine_extend.py b/test/python/test_circuit_combine_extend.py new file mode 100644 index 000000000000..ad8a641638c5 --- /dev/null +++ b/test/python/test_circuit_combine_extend.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- + +# Copyright 2018, IBM. +# +# This source code is licensed under the Apache License, Version 2.0 found in +# the LICENSE.txt file in the root directory of this source tree. + +# pylint: disable=invalid-name, unused-import + +"""Tests for combining and extending circuits across width and depth""" + +import qiskit.extensions.simulator +from qiskit import (ClassicalRegister, QISKitError, QuantumCircuit, + QuantumRegister, execute) +from qiskit.tools.qi.qi import state_fidelity +from .common import QiskitTestCase + + +class TestCircuitCombineExtend(QiskitTestCase): + """Test combining and extending of QuantumCircuits.""" + + def test_combine_circuit_common(self): + """Test combining two circuits with same registers. + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr, cr) + qc2 = QuantumCircuit(qr, cr) + qc1.h(qr[0]) + qc1.measure(qr[0], cr[0]) + qc2.measure(qr[1], cr[1]) + new_circuit = qc1 + qc2 + backend = 'local_qasm_simulator' + shots = 1024 + result = execute(new_circuit, backend=backend, shots=shots, seed=78).result() + counts = result.get_counts() + target = {'00': shots / 2, '01': shots / 2} + threshold = 0.04 * shots + self.assertDictAlmostEqual(counts, target, threshold) + + def test_combine_circuit_different(self): + """Test combining two circuits with different registers. + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr) + qc1.x(qr) + qc2 = QuantumCircuit(qr, cr) + qc2.measure(qr, cr) + new_circuit = qc1 + qc2 + backend = 'local_qasm_simulator' + shots = 1024 + result = execute(new_circuit, backend=backend, shots=shots, seed=78).result() + counts = result.get_counts() + target = {'11': shots} + self.assertEqual(counts, target) + + def test_combine_circuit_fail(self): + """Test combining two circuits fails if registers incompatible. + + If two circuits have same name register of different size or type + it should raise a QISKitError. + """ + q1 = QuantumRegister(1, "q") + q2 = QuantumRegister(2, "q") + c1 = ClassicalRegister(1, "q") + qc1 = QuantumCircuit(q1) + qc2 = QuantumCircuit(q2) + qc3 = QuantumCircuit(c1) + + self.assertRaises(QISKitError, qc1.__add__, qc2) + self.assertRaises(QISKitError, qc1.__add__, qc3) + + def test_combine_circuit_extension_instructions(self): + """Test combining circuits contining barrier, initializer, snapshot + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr) + desired_vector = [0.5, 0.5, 0.5, 0.5] + qc1.initialize(desired_vector, qr) + qc1.barrier() + qc2 = QuantumCircuit(qr, cr) + qc2.snapshot(slot='1') + qc2.measure(qr, cr) + new_circuit = qc1 + qc2 + backend = 'local_qasm_simulator_py' + shots = 1024 + result = execute(new_circuit, backend=backend, shots=shots, seed=78).result() + + snapshot_vectors = result.get_snapshot() + fidelity = state_fidelity(snapshot_vectors[0], desired_vector) + self.assertGreater(fidelity, 0.99) + + counts = result.get_counts() + target = {'00': shots/4, '01': shots/4, '10': shots/4, '11': shots/4} + threshold = 0.04 * shots + self.assertDictAlmostEqual(counts, target, threshold) + + def test_extend_circuit(self): + """Test extending a circuit with same registers. + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr, cr) + qc2 = QuantumCircuit(qr, cr) + qc1.h(qr[0]) + qc1.measure(qr[0], cr[0]) + qc2.measure(qr[1], cr[1]) + qc1 += qc2 + backend = 'local_qasm_simulator' + shots = 1024 + result = execute(qc1, backend=backend, shots=shots, seed=78).result() + counts = result.get_counts() + target = {'00': shots / 2, '01': shots / 2} + threshold = 0.04 * shots + self.assertDictAlmostEqual(counts, target, threshold) + + def test_extend_circuit_different_registers(self): + """Test extending a circuit with different registers. + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr) + qc1.x(qr) + qc2 = QuantumCircuit(qr, cr) + qc2.measure(qr, cr) + qc1 += qc2 + backend = 'local_qasm_simulator' + shots = 1024 + result = execute(qc1, backend=backend, shots=shots, seed=78).result() + counts = result.get_counts() + target = {'11': shots} + self.assertEqual(counts, target) + + def test_extend_circuit_fail(self): + """Test extending a circuits fails if registers incompatible. + + If two circuits have same name register of different size or type + it should raise a QISKitError. + """ + q1 = QuantumRegister(1, "q") + q2 = QuantumRegister(2, "q") + c1 = ClassicalRegister(1, "q") + qc1 = QuantumCircuit(q1) + qc2 = QuantumCircuit(q2) + qc3 = QuantumCircuit(c1) + + self.assertRaises(QISKitError, qc1.__iadd__, qc2) + self.assertRaises(QISKitError, qc1.__iadd__, qc3) + + def test_extend_circuit_extension_instructions(self): + """Test extending circuits contining barrier, initializer, snapshot + """ + qr = QuantumRegister(2) + cr = ClassicalRegister(2) + qc1 = QuantumCircuit(qr) + desired_vector = [0.5, 0.5, 0.5, 0.5] + qc1.initialize(desired_vector, qr) + qc1.barrier() + qc2 = QuantumCircuit(qr, cr) + qc2.snapshot(slot='1') + qc2.measure(qr, cr) + qc1 += qc2 + backend = 'local_qasm_simulator_py' + shots = 1024 + result = execute(qc1, backend=backend, shots=shots, seed=78).result() + + snapshot_vectors = result.get_snapshot('1') + fidelity = state_fidelity(snapshot_vectors[0], desired_vector) + self.assertGreater(fidelity, 0.99) + + counts = result.get_counts() + target = {'00': shots/4, '01': shots/4, '10': shots/4, '11': shots/4} + threshold = 0.04 * shots + self.assertDictAlmostEqual(counts, target, threshold) diff --git a/test/python/test_quantumprogram.py b/test/python/test_quantumprogram.py index 2bcbc68f7074..41dd46168f84 100644 --- a/test/python/test_quantumprogram.py +++ b/test/python/test_quantumprogram.py @@ -1297,132 +1297,6 @@ def test_online_qasm_simulator_two_registers(self, QE_TOKEN, QE_URL, # More test cases for interesting examples ############################################################### - def test_combine_circuit_common(self): - """Test combining two circuits with same registers. - - If all correct should return the data - """ - q_program = QuantumProgram() - qr = q_program.create_quantum_register("qr", 2) - cr = q_program.create_classical_register("cr", 2) - qc1 = q_program.create_circuit("qc1", [qr], [cr]) - qc2 = q_program.create_circuit("qc2", [qr], [cr]) - qc1.h(qr[0]) - qc1.measure(qr[0], cr[0]) - qc2.measure(qr[1], cr[1]) - new_circuit = qc1 + qc2 - name = 'test_circuit' - q_program.add_circuit(name, new_circuit) - backend = 'local_qasm_simulator' - shots = 1024 - result = q_program.execute(name, backend=backend, shots=shots, - seed=78) - counts = result.get_counts(name) - target = {'00': shots / 2, '01': shots / 2} - threshold = 0.04 * shots - self.assertDictAlmostEqual(counts, target, threshold) - - def test_combine_circuit_different(self): - """Test combining two circuits with different registers. - - If all correct should return the data - """ - qr = QuantumRegister(2, "qr") - cr = ClassicalRegister(2, "cr") - qc1 = QuantumCircuit(qr) - qc1.x(qr) - qc2 = QuantumCircuit(qr, cr) - qc2.measure(qr, cr) - - qp = QuantumProgram() - name = 'test' - qp.add_circuit(name, qc1 + qc2) - backend = 'local_qasm_simulator' - shots = 1024 - result = qp.execute(name, backend=backend, shots=shots, seed=78) - counts = result.get_counts(name) - target = {'11': shots} - self.assertEqual(counts, target) - - def test_combine_circuit_fail(self): - """Test combining two circuits fails if registers incompatible. - - If two circuits have samed name register of different size or type - it should raise a QISKitError. - """ - q1 = QuantumRegister(1, "q") - q2 = QuantumRegister(2, "q") - c1 = QuantumRegister(1, "q") - qc1 = QuantumCircuit(q1) - qc2 = QuantumCircuit(q2) - qc3 = QuantumCircuit(c1) - - self.assertRaises(QISKitError, qc1.__add__, qc2) - self.assertRaises(QISKitError, qc1.__add__, qc3) - - def test_extend_circuit(self): - """Test extending a circuit with same registers. - - If all correct should return the data - """ - q_program = QuantumProgram() - qr = q_program.create_quantum_register("qr", 2) - cr = q_program.create_classical_register("cr", 2) - qc1 = q_program.create_circuit("qc1", [qr], [cr]) - qc2 = q_program.create_circuit("qc2", [qr], [cr]) - qc1.h(qr[0]) - qc1.measure(qr[0], cr[0]) - qc2.measure(qr[1], cr[1]) - qc1 += qc2 - name = 'test_circuit' - q_program.add_circuit(name, qc1) - backend = 'local_qasm_simulator' - shots = 1024 - result = q_program.execute(name, backend=backend, shots=shots, - seed=78) - counts = result.get_counts(name) - target = {'00': shots / 2, '01': shots / 2} - threshold = 0.04 * shots - self.assertDictAlmostEqual(counts, target, threshold) - - def test_extend_circuit_different_registers(self): - """Test extending a circuit with different registers. - - If all correct should return the data - """ - qr = QuantumRegister(2, "qr") - cr = ClassicalRegister(2, "cr") - qc1 = QuantumCircuit(qr) - qc1.x(qr) - qc2 = QuantumCircuit(qr, cr) - qc2.measure(qr, cr) - qc1 += qc2 - qp = QuantumProgram() - name = 'test_circuit' - qp.add_circuit(name, qc1) - backend = 'local_qasm_simulator' - shots = 1024 - result = qp.execute(name, backend=backend, shots=shots, seed=78) - counts = result.get_counts(name) - target = {'11': shots} - self.assertEqual(counts, target) - - def test_extend_circuit_fail(self): - """Test extending a circuits fails if registers incompatible. - - If two circuits have samed name register of different size or type - it should raise a QISKitError. - """ - q1 = QuantumRegister(1, "q") - q2 = QuantumRegister(2, "q") - c1 = QuantumRegister(1, "q") - qc1 = QuantumCircuit(q1) - qc2 = QuantumCircuit(q2) - qc3 = QuantumCircuit(c1) - - self.assertRaises(QISKitError, qc1.__iadd__, qc2) - self.assertRaises(QISKitError, qc1.__iadd__, qc3) - def test_example_multiple_compile(self): """Test a toy example compiling multiple circuits. From 00d37c780dba447551e1e544a12b3aef10a43d53 Mon Sep 17 00:00:00 2001 From: Ali Javadi-Abhari Date: Mon, 11 Jun 2018 06:31:00 -0400 Subject: [PATCH 5/5] Fix incorrect unrolling of t to tdg in CircuitBackend (#557) --- CHANGELOG.rst | 1 + qiskit/unroll/_circuitbackend.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ce093168cd24..951b012c09c5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -46,6 +46,7 @@ Fixed - Removed ``_modifiers call`` from ``reapply`` (#534) - Fix circuit drawer issue with filename location on windows (#543) - Change initial qubit layout only if the backend coupling map is not satisfied (#527) +- Fix incorrect unrolling of t to tdg in CircuitBackend (#557) - Fix issue with simulator extension commands not reapplying correctly (#556) diff --git a/qiskit/unroll/_circuitbackend.py b/qiskit/unroll/_circuitbackend.py index 42d5278a99a5..b80c32312373 100644 --- a/qiskit/unroll/_circuitbackend.py +++ b/qiskit/unroll/_circuitbackend.py @@ -235,7 +235,7 @@ def start_gate(self, name, args, qubits, nested_scope=None): "rz": [(1, 1), lambda x: self.circuit.rz(x[0][0], x[1][0])], "s": [(0, 1), lambda x: self.circuit.s(x[1][0])], "sdg": [(0, 1), lambda x: self.circuit.s(x[1][0]).inverse()], - "t": [(0, 1), lambda x: self.circuit.t(x[1][0]).inverse()], + "t": [(0, 1), lambda x: self.circuit.t(x[1][0])], "tdg": [(0, 1), lambda x: self.circuit.t(x[1][0]).inverse()], "u1": [(1, 1), lambda x: self.circuit.u1(x[0][0], x[1][0])], "u2": [(2, 1), lambda x: self.circuit.u2(x[0][0], x[0][1],