def setUp(self): self.seed = 88 qasm_filename = self._get_resource_path('qasm/example.qasm') unroller = unroll.Unroller( qasm.Qasm(filename=qasm_filename).parse(), unroll.JsonBackend([])) circuit = QobjExperiment.from_dict(unroller.execute()) circuit.config = QobjItem(coupling_map=None, basis_gates='u1,u2,u3,cx,id', layout=None, seed=self.seed) circuit.header.name = 'test' self.qobj = Qobj(qobj_id='test_sim_single_shot', config=QobjConfig(shots=1024, memory_slots=6, max_credits=3), experiments=[circuit], header=QobjHeader(backend_name='qasm_simulator_py'))
def setUp(self): self.seed = 88 self.qasmFileName = os.path.join(qiskit.__path__[0], '../test/python/qasm/example.qasm') self.qp = QuantumProgram() shots = 1 self.qp.load_qasm_file(self.qasmFileName, name='example') basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=self.qp.get_qasm("example")).parse(), unroll.JsonBackend(basis_gates)) circuit = unroller.execute() self.job = { 'compiled_circuit': circuit, 'config': { 'shots': shots, 'seed': random.randint(0, 10) } }
def _unroller_code(qasm_circuit, basis_gates=None): """ Unroll the code. Circuit is the circuit to unroll using the DAG representation. This is an internal function. Args: qasm_circuit (str): a circuit representation as qasm text. basis_gates (str): a comma seperated string and are the base gates, which by default are: u1,u2,u3,cx,id Return: 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 program_node_circuit = qasm.Qasm(data=qasm_circuit).parse() unroller_circuit = unroll.Unroller( program_node_circuit, unroll.DAGBackend(basis_gates.split(","))) dag_circuit_unrolled = unroller_circuit.execute() return dag_circuit_unrolled
def setUp(self): self.seed = 88 self.qasm_filename = self._get_resource_path('qasm/example.qasm') with open(self.qasm_filename, 'r') as qasm_file: self.qasm_text = qasm_file.read() self.qasm_ast = qasm.Qasm(data=self.qasm_text).parse() self.qasm_be = unroll.CircuitBackend( ['u1', 'u2', 'u3', 'id', 'cx']) self.qasm_circ = unroll.Unroller(self.qasm_ast, self.qasm_be).execute() qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.measure(qr[0], cr[0]) self.qc = qc # create qobj compiled_circuit1 = QobjExperiment.from_dict( transpile_dag(DAGCircuit.fromQuantumCircuit(self.qc), format='json')) compiled_circuit2 = QobjExperiment.from_dict( transpile_dag(DAGCircuit.fromQuantumCircuit(self.qasm_circ), format='json')) self.qobj = Qobj(qobj_id='test_qobj', config=QobjConfig(shots=2000, memory_slots=1, max_credits=3, seed=1111), experiments=[compiled_circuit1, compiled_circuit2], header=QobjHeader(backend_name='qasm_simulator')) self.qobj.experiments[0].header.name = 'test_circuit1' self.qobj.experiments[0].config = QobjItem( basis_gates='u1,u2,u3,cx,id') self.qobj.experiments[1].header.name = 'test_circuit2' self.qobj.experiments[1].config = QobjItem( basis_gates='u1,u2,u3,cx,id') self.backend = QasmSimulator()
def test_if_statement(self): logging.info('test_if_statement_x') shots = 100 max_qubits = 3 qp = QuantumProgram() qr = qp.create_quantum_register('qr', max_qubits) cr = qp.create_classical_register('cr', max_qubits) circuit = qp.create_circuit('test_if', [qr], [cr]) circuit.x(qr[0]) circuit.x(qr[1]) circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) circuit.x(qr[2]).c_if(cr, 0x3) circuit.measure(qr[0], cr[0]) circuit.measure(qr[1], cr[1]) circuit.measure(qr[2], cr[2]) basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=qp.get_qasm('test_if')).parse(), unroll.JsonBackend(basis_gates)) ucircuit = json.loads(unroller.execute()) config = {'shots': shots, 'seed': self.seed} job = {'compiled_circuit': json.dumps(ucircuit), 'config': config} result_if_true = QasmSimulator(job).run() del ucircuit['operations'][1] # remove x(qr[1]) operation job = {'compiled_circuit': json.dumps(ucircuit), 'config': config} result_if_false = QasmSimulator(job).run() logging.info('result_if_true circuit:') logging.info(circuit.qasm()) logging.info('result_if_true={0}'.format(result_if_true)) del circuit.data[1] logging.info('result_if_false circuit:') logging.info(circuit.qasm()) logging.info('result_if_false={0}'.format(result_if_false)) self.assertTrue(result_if_true['data']['counts']['111'] == 100) self.assertTrue(result_if_false['data']['counts']['001'] == 100)
def setUp(self): self.seed = 88 self.qasm_filename = self._get_resource_path('qasm/example.qasm') self.qp = QuantumProgram() self.qp.load_qasm_file(self.qasm_filename, name='example') basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=self.qp.get_qasm('example')).parse(), unroll.JsonBackend(basis_gates)) circuit = unroller.execute() circuit_config = { 'coupling_map': None, 'basis_gates': 'u1,u2,u3,cx,id', 'layout': None, 'seed': self.seed } resources = {'max_credits': 3} self.qobj = { 'id': 'test_sim_single_shot', 'config': { 'max_credits': resources['max_credits'], 'shots': 1024, 'backend_name': 'local_qasm_simulator_py', }, 'circuits': [{ 'name': 'test', 'compiled_circuit': circuit, 'compiled_circuit_qasm': None, 'config': circuit_config }] } self.q_job = QuantumJob(self.qobj, backend=QasmSimulatorPy(), circuit_config=circuit_config, seed=self.seed, resources=resources, preformatted=True)
def dag2json(dag_circuit, basis_gates='u1,u2,u3,cx,id'): """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. basis_gates (str): a comma seperated string and are the base gates, which by default are: u1,u2,u3,cx,id Returns: the json version of the dag """ # TODO: Jay: I think this needs to become a method like .qasm() for the DAG. try: circuit_string = dag_circuit.qasm(qeflag=True) except TypeError: circuit_string = dag_circuit.qasm() unroller = unroll.Unroller(qasm.Qasm(data=circuit_string).parse(), unroll.JsonBackend(basis_gates.split(","))) json_circuit = unroller.execute() return json_circuit
def test_qasm_simulator(self): """Test data counts output for single circuit run against reference.""" shots = 1024 self.qp.load_qasm_file(self.qasmFileName, name='example') basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=self.qp.get_qasm("example")).parse(), unroll.JsonBackend(basis_gates)) circuit = unroller.execute() config = {'shots': shots, 'seed': self.seed} job = {'compiled_circuit': circuit, 'config': config} result = QasmSimulator(job).run() expected = { '100 100': 137, '011 011': 131, '101 101': 117, '111 111': 127, '000 000': 131, '010 010': 141, '110 110': 116, '001 001': 124 } self.assertEqual(result['data']['counts'], expected)
def test_if_statement(self): self.log.info('test_if_statement_x') shots = 100 max_qubits = 3 qr = QuantumRegister(max_qubits, 'qr') cr = ClassicalRegister(max_qubits, 'cr') circuit_if_true = QuantumCircuit(qr, cr, name='test_if_true') circuit_if_true.x(qr[0]) circuit_if_true.x(qr[1]) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.x(qr[2]).c_if(cr, 0x3) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.measure(qr[2], cr[2]) circuit_if_false = QuantumCircuit(qr, cr, name='test_if_false') circuit_if_false.x(qr[0]) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.x(qr[2]).c_if(cr, 0x3) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.measure(qr[2], cr[2]) basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=circuit_if_true.qasm()).parse(), unroll.JsonBackend(basis_gates)) ucircuit_true = QobjExperiment.from_dict(unroller.execute()) unroller = unroll.Unroller( qasm.Qasm(data=circuit_if_false.qasm()).parse(), unroll.JsonBackend(basis_gates)) ucircuit_false = QobjExperiment.from_dict(unroller.execute()) # Customize the experiments and create the qobj. ucircuit_true.config = QobjItem(coupling_map=None, basis_gates='u1,u2,u3,cx,id', layout=None, seed=None) ucircuit_true.header.name = 'test_if_true' ucircuit_false.config = QobjItem(coupling_map=None, basis_gates='u1,u2,u3,cx,id', layout=None, seed=None) ucircuit_false.header.name = 'test_if_false' qobj = Qobj(id='test_if_qobj', config=QobjConfig(max_credits=3, shots=shots, memory_slots=max_qubits), experiments=[ucircuit_true, ucircuit_false], header=QobjHeader(backend_name='local_qasm_simulator_py')) result = QasmSimulatorPy().run(qobj).result() result_if_true = result.get_data('test_if_true') self.log.info('result_if_true circuit:') self.log.info(circuit_if_true.qasm()) self.log.info('result_if_true=%s', result_if_true) result_if_false = result.get_data('test_if_false') self.log.info('result_if_false circuit:') self.log.info(circuit_if_false.qasm()) self.log.info('result_if_false=%s', result_if_false) self.assertTrue(result_if_true['counts']['111'] == 100) self.assertTrue(result_if_false['counts']['001'] == 100)
def optimize_1q_gates(circuit): """Simplify runs of single qubit gates in the QX basis. Return a new circuit that has been optimized. """ qx_basis = ["u1", "u2", "u3", "cx", "id"] urlr = unroll.Unroller( Qasm(data=circuit.qasm(qeflag=True)).parse(), unroll.DAGBackend(qx_basis)) unrolled = urlr.execute() runs = unrolled.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: qname = unrolled.multi_graph.node[run[0]]["qargs"][0] right_name = "u1" right_parameters = (0.0, 0.0, 0.0) # (theta, phi, lambda) for node in run: nd = unrolled.multi_graph.node[node] assert nd["condition"] is None, "internal error" assert len(nd["qargs"]) == 1, "internal error" assert nd["qargs"][0] == qname, "internal error" left_name = nd["name"] assert left_name in ["u1", "u2", "u3", "id"], "internal error" if left_name == "u1": left_parameters = (0.0, 0.0, float(nd["params"][0])) elif left_name == "u2": left_parameters = (math.pi / 2, float(nd["params"][0]), float(nd["params"][1])) elif left_name == "u3": left_parameters = tuple(map(float, nd["params"])) else: left_name = "u1" # replace id with u1 left_parameters = (0.0, 0.0, 0.0) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (0.0, 0.0, right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (math.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u2", "u1"): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (math.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u3"): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u3", "u1"): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) right_name = "u3" right_parameters = (math.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + math.pi / 2, right_parameters[2] + math.pi / 2) else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. right_name = "u3" right_parameters = compose_u3(left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Here down, when we simplify, we add f(theta) to lambda to correct # the global phase when f(theta) is 2*pi. This isn't necessary but # the other steps preserve the global phase, so we continue. epsilon = 1e-9 # for comparison with zero # Y rotation is 0 mod 2*pi, so the gate is a u1 if abs(right_parameters[0] % 2.0 * math.pi) < epsilon \ and right_name != "u1": right_name = "u1" right_parameters = (0.0, 0.0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3": # theta = pi/2 + 2*k*pi if abs((right_parameters[0] - math.pi / 2) % 2.0 * math.pi) \ < epsilon: right_name = "u2" right_parameters = (math.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - math.pi / 2)) # theta = -pi/2 + 2*k*pi if abs((right_parameters[0] + math.pi / 2) % 2.0 * math.pi) \ < epsilon: right_name = "u2" right_parameters = (math.pi / 2, right_parameters[1] + math.pi, right_parameters[2] - math.pi + (right_parameters[0] + math.pi / 2)) # u1 and lambda is 0 mod 4*pi so gate is nop if right_name == "u1" and \ abs(right_parameters[2] % 4.0 * math.pi) < epsilon: right_name = "nop" # Replace the data of the first node in the run new_params = [] if right_name == "u1": new_params.append(right_parameters[2]) if right_name == "u2": new_params = [right_parameters[1], right_parameters[2]] if right_name == "u3": new_params = list(right_parameters) nx.set_node_attributes(unrolled.multi_graph, 'name', {run[0]: right_name}) nx.set_node_attributes(unrolled.multi_graph, 'params', {run[0]: tuple(map(str, new_params))}) # Delete the other nodes in the run for node in run[1:]: unrolled._remove_op_node(node) if right_name == "nop": unrolled._remove_op_node(run[0]) return unrolled
def compiler_function(dag_circuit, coupling_map=None, gate_costs=None): """ Modify a DAGCircuit based on a gate cost function. Instructions: Your submission involves filling in the implementation of this function. The function takes as input a DAGCircuit object, which can be generated from a QASM file by using the function 'qasm_to_dag_circuit' from the included 'submission_evaluation.py' module. For more information on the DAGCircuit object see the or QISKit documentation (eg. 'help(DAGCircuit)'). Args: dag_circuit (DAGCircuit): DAGCircuit object to be compiled. coupling_circuit (list): Coupling map for device topology. A coupling map of None corresponds an all-to-all connected topology. gate_costs (dict) : dictionary of gate names and costs. Returns: A modified DAGCircuit object that satisfies an input coupling_map and has as low a gate_cost as possible. """ qasm_in = dag_circuit.qasm() Q_program = QuantumProgram() qcircuit_name = Q_program.load_qasm_text(qasm_in) qcircuit = Q_program.get_circuit(qcircuit_name) tuple_map = {} if coupling_map is None: # todo: make all-to-all map raise ValueError else: for key in coupling_map.keys(): tuple_map[key] = tuple(coupling_map[key]) result, cost = Embed(qcircuit, tuple_map) compiled_dag = DAGCircuit.fromQuantumCircuit(result) return compiled_dag if False: # Example using mapper passes in Qiskit import copy from qiskit.mapper import swap_mapper, direction_mapper, cx_cancellation, optimize_1q_gates, Coupling from qiskit import qasm, unroll initial_layout = None coupling = Coupling(coupling_map) compiled_dag, final_layout = swap_mapper(copy.deepcopy(dag_circuit), coupling, initial_layout, trials=40, seed=19) # Expand swaps basis_gates = "u1,u2,u3,cx,id" # QE target basis program_node_circuit = qasm.Qasm(data=compiled_dag.qasm()).parse() unroller_circuit = unroll.Unroller( program_node_circuit, unroll.DAGBackend(basis_gates.split(","))) compiled_dag = unroller_circuit.execute() # Change cx directions compiled_dag = direction_mapper(compiled_dag, coupling) # Simplify cx gates cx_cancellation(compiled_dag) # Simplify single qubit gates compiled_dag = optimize_1q_gates(compiled_dag) # Return the compiled dag circuit return compiled_dag
def swap_mapper(circuit_graph, coupling_graph, initial_layout=None, basis="cx,u1,u2,u3,id", verbose=False): """Map a Circuit onto a CouplingGraph using swap gates. circuit_graph = input Circuit coupling_graph = CouplingGraph to map onto initial_layout = dict from qubits of circuit_graph to qubits of coupling_graph (optional) basis = basis string specifying basis of output Circuit verbose = optional flag to print more information Returns a Circuit object containing a circuit equivalent to circuit_graph that respects couplings in coupling_graph, and a layout dict mapping qubits of circuit_graph into qubits of coupling_graph. The layout may differ from the initial_layout if the first layer of gates cannot be executed on the initial_layout. """ if circuit_graph.width() > coupling_graph.size(): raise QISKitException("Not enough qubits in CouplingGraph") # Schedule the input circuit layerlist = circuit_graph.layers() if verbose: print("schedule:") for i in range(len(layerlist)): print(" %d: %s" % (i, layerlist[i]["partition"])) # Check input layout and create default layout if necessary if initial_layout is not None: circ_qubits = circuit_graph.get_qubits() coup_qubits = coupling_graph.get_qubits() qubit_subset = [] for k, v in initial_layout.values(): qubit_subset.append(v) if k not in circ_qubits: raise QISKitException("initial_layout qubit %s[%d] not " + "in input Circuit" % (k[0], k[1])) if v not in coup_qubits: raise QISKitException("initial_layout qubit %s[%d] not " + " in input CouplingGraph" % (v[0], v[1])) else: # Supply a default layout qubit_subset = coupling_graph.get_qubits() qubit_subset = qubit_subset[0:circuit_graph.width()] initial_layout = {a: b for a, b in zip(circuit_graph.get_qubits(), qubit_subset)} # Find swap circuit to preceed to each layer of input circuit layout = copy.deepcopy(initial_layout) openqasm_output = "" first_layer = True # True until first layer is output first_swapping_layer = True # True until first swap layer is output # Iterate over layers for i in range(len(layerlist)): # Attempt to find a permutation for this layer success_flag, best_circ, best_d, best_layout, trivial_flag \ = layer_permutation(layerlist[i]["partition"], layout, qubit_subset, coupling_graph, 20) # If this fails, try one gate at a time in this layer if not success_flag: if verbose: print("swap_mapper: failed, layer %d, " % i, " retrying sequentially") serial_layerlist = layerlist[i]["graph"].serial_layers() # Go through each gate in the layer for j in range(len(serial_layerlist)): success_flag, best_circ, best_d, best_layout, trivial_flag \ = layer_permutation(serial_layerlist[j]["partition"], layout, qubit_subset, coupling_graph, 20) # Give up if we fail again if not success_flag: raise QISKitException("swap_mapper failed: " + "layer %d, sublayer %d" % (i, j) + ", \"%s\"" % serial_layerlist[j]["graph"].qasm( no_decls=True, aliases=layout)) else: # Update the qubit positions each iteration layout = best_layout if best_d == 0: # Output qasm without swaps if first_layer: openqasm_output += circuit_graph.qasm( add_swap=True, decls_only=True, aliases=layout) first_layer = False if not trivial_flag and first_swapping_layer: initial_layout = layout first_swapping_layer = False else: # Output qasm with swaps if first_layer: openqasm_output += circuit_graph.qasm( add_swap=True, decls_only=True, aliases=layout) first_layer = False initial_layout = layout first_swapping_layer = False else: if not first_swapping_layer: if verbose: print("swap_mapper: layer %d (%d), depth %d" % (i, j, best_d)) openqasm_output += best_circ else: initial_layout = layout first_swapping_layer = False openqasm_output += serial_layerlist[j]["graph"].qasm( no_decls=True, aliases=layout) else: # Update the qubit positions each iteration layout = best_layout if best_d == 0: # Output qasm without swaps if first_layer: openqasm_output += circuit_graph.qasm( add_swap=True, decls_only=True, aliases=layout) first_layer = False if not trivial_flag and first_swapping_layer: initial_layout = layout first_swapping_layer = False else: # Output qasm with swaps if first_layer: openqasm_output += circuit_graph.qasm( add_swap=True, decls_only=True, aliases=layout) first_layer = False initial_layout = layout first_swapping_layer = False else: if not first_swapping_layer: if verbose: print("swap_mapper: layer %d, depth %d" % (i, best_d)) openqasm_output += best_circ else: initial_layout = layout first_swapping_layer = False openqasm_output += layerlist[i]["graph"].qasm( no_decls=True, aliases=layout) # Parse openqasm_output into Circuit object basis += ",swap" ast = Qasm(data=openqasm_output).parse() u = unroll.Unroller(ast, unroll.CircuitBackend(basis.split(","))) u.execute() return u.backend.circuit, initial_layout
def swap_mapper(circuit_graph, coupling_graph, initial_layout=None, basis="cx,u1,u2,u3,id", trials=20): """Map a DAGCircuit onto a CouplingGraph using swap gates. Args: circuit_graph (DAGCircuit): input DAG circuit coupling_graph (CouplingGraph): coupling graph to map onto initial_layout (dict): dict from qubits of circuit_graph to qubits of coupling_graph (optional) basis (str, optional): basis string specifying basis of output DAGCircuit Returns: Returns a DAGCircuit object containing a circuit equivalent to circuit_graph that respects couplings in coupling_graph, and a layout dict mapping qubits of circuit_graph into qubits of coupling_graph. The layout may differ from the initial_layout if the first layer of gates cannot be executed on the initial_layout. """ if circuit_graph.width() > coupling_graph.size(): raise MapperError("Not enough qubits in CouplingGraph") # Schedule the input circuit layerlist = circuit_graph.layers() logger.debug("schedule:") for i in range(len(layerlist)): logger.debug(" %d: %s", i, layerlist[i]["partition"]) if initial_layout is not None: # Check the input layout circ_qubits = circuit_graph.get_qubits() coup_qubits = coupling_graph.get_qubits() qubit_subset = [] for k, v in initial_layout.items(): qubit_subset.append(v) if k not in circ_qubits: raise MapperError("initial_layout qubit %s[%d] not in input " "DAGCircuit" % (k[0], k[1])) if v not in coup_qubits: raise MapperError("initial_layout qubit %s[%d] not in input " "CouplingGraph" % (v[0], v[1])) else: # Supply a default layout qubit_subset = coupling_graph.get_qubits() qubit_subset = qubit_subset[0:circuit_graph.width()] initial_layout = { a: b for a, b in zip(circuit_graph.get_qubits(), qubit_subset) } # Find swap circuit to preceed to each layer of input circuit layout = copy.deepcopy(initial_layout) openqasm_output = "" first_layer = True # True until first layer is output logger.debug("initial_layout = %s", layout) # Iterate over layers for i, layer in enumerate(layerlist): # Attempt to find a permutation for this layer success_flag, best_circ, best_d, best_layout, trivial_flag \ = layer_permutation(layer["partition"], layout, qubit_subset, coupling_graph, trials) logger.debug("swap_mapper: layer %d", i) logger.debug("swap_mapper: success_flag=%s,best_d=%s,trivial_flag=%s", success_flag, str(best_d), trivial_flag) # If this layer is only single-qubit gates, # and we have yet to see multi-qubit gates, # continue to the next iteration if trivial_flag and first_layer: logger.debug("swap_mapper: skip to next layer") continue # If this fails, try one gate at a time in this layer if not success_flag: logger.debug( "swap_mapper: failed, layer %d, " "retrying sequentially", i) serial_layerlist = layer["graph"].serial_layers() # Go through each gate in the layer for j, serial_layer in enumerate(serial_layerlist): success_flag, best_circ, best_d, best_layout, trivial_flag \ = layer_permutation(serial_layer["partition"], layout, qubit_subset, coupling_graph, trials) logger.debug("swap_mapper: layer %d, sublayer %d", i, j) logger.debug( "swap_mapper: success_flag=%s,best_d=%s," "trivial_flag=%s", success_flag, str(best_d), trivial_flag) # Give up if we fail again if not success_flag: raise MapperError("swap_mapper failed: " + "layer %d, sublayer %d" % (i, j) + ", \"%s\"" % serial_layer["graph"].qasm( no_decls=True, aliases=layout)) # If this layer is only single-qubit gates, # and we have yet to see multi-qubit gates, # continue to the next inner iteration if trivial_flag and first_layer: logger.debug("swap_mapper: skip to next sublayer") continue # Update the record of qubit positions for each inner iteration layout = best_layout # Update the QASM openqasm_output += update_qasm(j, first_layer, best_layout, best_d, best_circ, circuit_graph, serial_layerlist) # Update initial layout if first_layer: initial_layout = layout first_layer = False else: # Update the record of qubit positions for each iteration layout = best_layout # Update the QASM openqasm_output += update_qasm(i, first_layer, best_layout, best_d, best_circ, circuit_graph, layerlist) # Update initial layout if first_layer: initial_layout = layout first_layer = False # If first_layer is still set, the circuit only has single-qubit gates # so we can use the initial layout to output the entire circuit if first_layer: layout = initial_layout openqasm_output += circuit_graph.qasm(add_swap=True, decls_only=True, aliases=layout) for i, layer in enumerate(layerlist): openqasm_output += layer["graph"].qasm(no_decls=True, aliases=layout) # Parse openqasm_output into DAGCircuit object basis += ",swap" ast = Qasm(data=openqasm_output).parse() u = unroll.Unroller(ast, unroll.DAGBackend(basis.split(","))) return u.execute(), initial_layout
def test_if_statement(self): self.log.info('test_if_statement_x') shots = 100 max_qubits = 3 qp = QuantumProgram() qr = qp.create_quantum_register('qr', max_qubits) cr = qp.create_classical_register('cr', max_qubits) circuit_if_true = qp.create_circuit('test_if_true', [qr], [cr]) circuit_if_true.x(qr[0]) circuit_if_true.x(qr[1]) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.x(qr[2]).c_if(cr, 0x3) circuit_if_true.measure(qr[0], cr[0]) circuit_if_true.measure(qr[1], cr[1]) circuit_if_true.measure(qr[2], cr[2]) circuit_if_false = qp.create_circuit('test_if_false', [qr], [cr]) circuit_if_false.x(qr[0]) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.x(qr[2]).c_if(cr, 0x3) circuit_if_false.measure(qr[0], cr[0]) circuit_if_false.measure(qr[1], cr[1]) circuit_if_false.measure(qr[2], cr[2]) basis_gates = [] # unroll to base gates unroller = unroll.Unroller( qasm.Qasm(data=qp.get_qasm('test_if_true')).parse(), unroll.JsonBackend(basis_gates)) ucircuit_true = unroller.execute() unroller = unroll.Unroller( qasm.Qasm(data=qp.get_qasm('test_if_false')).parse(), unroll.JsonBackend(basis_gates)) ucircuit_false = unroller.execute() qobj = { 'id': 'test_if_qobj', 'config': { 'max_credits': 3, 'shots': shots, 'backend_name': 'local_qasm_simulator_py', }, 'circuits': [{ 'name': 'test_if_true', 'compiled_circuit': ucircuit_true, 'compiled_circuit_qasm': None, 'config': { 'coupling_map': None, 'basis_gates': 'u1,u2,u3,cx,id', 'layout': None, 'seed': None } }, { 'name': 'test_if_false', 'compiled_circuit': ucircuit_false, 'compiled_circuit_qasm': None, 'config': { 'coupling_map': None, 'basis_gates': 'u1,u2,u3,cx,id', 'layout': None, 'seed': None } }] } q_job = QuantumJob(qobj, backend=QasmSimulatorPy(), preformatted=True) result = QasmSimulatorPy().run(q_job).result() result_if_true = result.get_data('test_if_true') self.log.info('result_if_true circuit:') self.log.info(circuit_if_true.qasm()) self.log.info('result_if_true=%s', result_if_true) result_if_false = result.get_data('test_if_false') self.log.info('result_if_false circuit:') self.log.info(circuit_if_false.qasm()) self.log.info('result_if_false=%s', result_if_false) self.assertTrue(result_if_true['counts']['111'] == 100) self.assertTrue(result_if_false['counts']['001'] == 100)
def optimize_1q_gates(circuit): """Simplify runs of single qubit gates in the QX basis. Return a new circuit that has been optimized. """ qx_basis = ["u1", "u2", "u3", "cx", "id"] urlr = unroll.Unroller( Qasm(data=circuit.qasm()).parse(), unroll.DAGBackend(qx_basis)) unrolled = urlr.execute() runs = unrolled.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: qname = unrolled.multi_graph.node[run[0]]["qargs"][0] right_name = "u1" right_parameters = (N(0), N(0), N(0)) # (theta, phi, lambda) for node in run: nd = unrolled.multi_graph.node[node] assert nd["condition"] is None, "internal error" assert len(nd["qargs"]) == 1, "internal error" assert nd["qargs"][0] == qname, "internal error" left_name = nd["name"] assert left_name in ["u1", "u2", "u3", "id"], "internal error" if left_name == "u1": left_parameters = (N(0), N(0), nd["params"][0]) elif left_name == "u2": left_parameters = (sympy.pi / 2, nd["params"][0], nd["params"][1]) elif left_name == "u3": left_parameters = tuple(nd["params"]) else: left_name = "u1" # replace id with u1 left_parameters = (N(0), N(0), N(0)) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (N(0), N(0), right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (sympy.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u2", "u1"): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (sympy.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u3"): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u3", "u1"): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) right_name = "u3" right_parameters = (sympy.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + sympy.pi / 2, right_parameters[2] + sympy.pi / 2) elif name_tuple[1] == "nop": right_name = left_name right_parameters = left_parameters else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. right_name = "u3" # Evaluate the symbolic expressions for efficiency left_parameters = tuple( map(lambda x: x.evalf(), list(left_parameters))) right_parameters = tuple( map(lambda x: x.evalf(), list(right_parameters))) right_parameters = compose_u3(left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Why evalf()? This program: # OPENQASM 2.0; # include "qelib1.inc"; # qreg q[2]; # creg c[2]; # u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0]; # u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0]; # u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0]; # measure q -> c; # took >630 seconds (did not complete) to optimize without # calling evalf() at all, 19 seconds to optimize calling # evalf() AFTER compose_u3, and 1 second to optimize # calling evalf() BEFORE compose_u3. # 1. Here down, when we simplify, we add f(theta) to lambda to # correct the global phase when f(theta) is 2*pi. This isn't # necessary but the other steps preserve the global phase, so # we continue in that manner. # 2. The final step will remove Z rotations by 2*pi. # 3. Note that is_zero is true only if the expression is exactly # zero. If the input expressions have already been evaluated # then these final simplifications will not occur. # TODO After we refactor, we should have separate passes for # exact and approximate rewriting. # Y rotation is 0 mod 2*pi, so the gate is a u1 if (right_parameters[0] % (2 * sympy.pi)).is_zero \ and right_name != "u1": right_name = "u1" right_parameters = (0, 0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3": # theta = pi/2 + 2*k*pi if ((right_parameters[0] - sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - sympy.pi / 2)) # theta = -pi/2 + 2*k*pi if ((right_parameters[0] + sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1] + sympy.pi, right_parameters[2] - sympy.pi + (right_parameters[0] + sympy.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name == "u1" and (right_parameters[2] % (2 * sympy.pi)).is_zero: right_name = "nop" # Simplify the symbolic parameters right_parameters = tuple( map(sympy.simplify, list(right_parameters))) # Replace the data of the first node in the run new_params = [] if right_name == "u1": new_params = [right_parameters[2]] if right_name == "u2": new_params = [right_parameters[1], right_parameters[2]] if right_name == "u3": new_params = list(right_parameters) nx.set_node_attributes(unrolled.multi_graph, name='name', values={run[0]: right_name}) # params is a list of sympy symbols and the str() method # will return Python expressions. To get the correct # OpenQASM expression, we need to replace "**" with "^". nx.set_node_attributes(unrolled.multi_graph, name='params', values={ run[0]: tuple( map(lambda x: str(x).replace("**", "^"), new_params)) }) # Delete the other nodes in the run for node in run[1:]: unrolled._remove_op_node(node) if right_name == "nop": unrolled._remove_op_node(run[0]) return unrolled
def optimize_1q_gates(circuit): """Simplify runs of single qubit gates in the QX basis. Return a new circuit that has been optimized. """ qx_basis = ["u1", "u2", "u3", "cx", "id"] urlr = unroll.Unroller( Qasm(data=circuit.qasm(qeflag=True)).parse(), unroll.DAGBackend(qx_basis)) unrolled = urlr.execute() runs = unrolled.collect_runs(["u1", "u2", "u3", "id"]) for run in runs: qname = unrolled.multi_graph.node[run[0]]["qargs"][0] right_name = "u1" right_parameters = (0, 0, 0) # (theta, phi, lambda) for node in run: nd = unrolled.multi_graph.node[node] assert nd["condition"] is None, "internal error" assert len(nd["qargs"]) == 1, "internal error" assert nd["qargs"][0] == qname, "internal error" left_name = nd["name"] assert left_name in ["u1", "u2", "u3", "id"], "internal error" if left_name == "u1": left_parameters = (0, 0, sympy.sympify(nd["params"][0])) elif left_name == "u2": left_parameters = (sympy.pi / 2, sympy.sympify(nd["params"][0]), sympy.sympify(nd["params"][1])) elif left_name == "u3": left_parameters = tuple(sympy.sympify(nd["params"])) else: left_name = "u1" # replace id with u1 left_parameters = (0, 0, 0) # Compose gates name_tuple = (left_name, right_name) if name_tuple == ("u1", "u1"): # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2) right_parameters = (0, 0, right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u2"): # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2) right_parameters = (sympy.pi / 2, right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u2", "u1"): # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2) right_name = "u2" right_parameters = (sympy.pi / 2, left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u1", "u3"): # u1(lambda1) * u3(theta2, phi2, lambda2) = # u3(theta2, phi2 + lambda1, lambda2) right_parameters = (right_parameters[0], right_parameters[1] + left_parameters[2], right_parameters[2]) elif name_tuple == ("u3", "u1"): # u3(theta1, phi1, lambda1) * u1(lambda2) = # u3(theta1, phi1, lambda1 + lambda2) right_name = "u3" right_parameters = (left_parameters[0], left_parameters[1], right_parameters[2] + left_parameters[2]) elif name_tuple == ("u2", "u2"): # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) = # Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2), # u2(phi1, lambda1) * u2(phi2, lambda2) = # u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2) right_name = "u3" right_parameters = (sympy.pi - left_parameters[2] - right_parameters[1], left_parameters[1] + sympy.pi / 2, right_parameters[2] + sympy.pi / 2) else: # For composing u3's or u2's with u3's, use # u2(phi, lambda) = u3(pi/2, phi, lambda) # together with the qiskit.mapper.compose_u3 method. right_name = "u3" right_parameters = compose_u3(left_parameters[0], left_parameters[1], left_parameters[2], right_parameters[0], right_parameters[1], right_parameters[2]) # Evaluate the symbolic expressions for efficiency right_parameters = tuple(map(sympy.N, list(right_parameters))) # 1. Here down, when we simplify, we add f(theta) to lambda to # correct the global phase when f(theta) is 2*pi. This isn't # necessary but the other steps preserve the global phase, so # we continue in that manner. # 2. The final step will remove Z rotations by 2*pi. # 3. Note that is_zero is true only if the expression is exactly # zero. If the input expressions have already been evaluated # then these final simplifications will not occur. # TODO After we refactor, we should have separate passes for # exact and approximate rewriting. # Y rotation is 0 mod 2*pi, so the gate is a u1 if (right_parameters[0] % (2 * sympy.pi)).is_zero \ and right_name != "u1": right_name = "u1" right_parameters = (0, 0, right_parameters[1] + right_parameters[2] + right_parameters[0]) # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2 if right_name == "u3": # theta = pi/2 + 2*k*pi if ((right_parameters[0] - sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1], right_parameters[2] + (right_parameters[0] - sympy.pi / 2)) # theta = -pi/2 + 2*k*pi if ((right_parameters[0] + sympy.pi / 2) % (2 * sympy.pi)).is_zero: right_name = "u2" right_parameters = (sympy.pi / 2, right_parameters[1] + sympy.pi, right_parameters[2] - sympy.pi + (right_parameters[0] + sympy.pi / 2)) # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase) if right_name == "u1" and (right_parameters[2] % (2 * sympy.pi)).is_zero: right_name = "nop" # Simplify the symbolic parameters right_parameters = tuple( map(sympy.simplify, list(right_parameters))) # Replace the data of the first node in the run new_params = [] if right_name == "u1": new_params = [right_parameters[2]] if right_name == "u2": new_params = [right_parameters[1], right_parameters[2]] if right_name == "u3": new_params = list(right_parameters) nx.set_node_attributes(unrolled.multi_graph, 'name', {run[0]: right_name}) # params is a list of sympy symbols and the str() method # will return Python expressions. To get the correct # OpenQASM expression, we need to replace "**" with "^". nx.set_node_attributes(unrolled.multi_graph, 'params', { run[0]: tuple(map(lambda x: str(x).replace("**", "^"), new_params)) }) # Delete the other nodes in the run for node in run[1:]: unrolled._remove_op_node(node) if right_name == "nop": unrolled._remove_op_node(run[0]) return unrolled
def my_swap_mapper(circuit_graph, coupling, speedup=False, initial_layout=None): """ TODO: add description""" gates = read_gates(circuit_graph) qubits = coupling.get_qubits() if initial_layout == None: # We start with a trivial layout, no significat improvement, especially # for large circuits, could be achived by optimizing the initial layout initial_layout = {qubit: qubit for qubit in qubits} qasm_string = "" # Set the depth we are actually going to use. If speedup is true, use # a depth one smaller than usually used_depth = DEPTH if speedup: used_depth -= 1 # This value gives a good compromise between speed and final score max_gates = 50 + 10 * len(coupling.get_qubits()) # Build the initial tree node = build_tree(None, gates, coupling, initial_layout, used_depth, width=WIDTH, max_gates=max_gates) # Now actually start compiling run = True while run: # if no gates are left, stop ater this iteration run = node["remaining_gates"] != [] # add the swap of the top node to the qasm string if node["swap"] != None: edge = node["swap"] qasm_string += "swap %s[%d],%s[%d]; " % (edge[0][0], edge[0][1], edge[1][0], edge[1][1]) # add all executed gates to the qasm string for gate in node["executed_gates"]: qasm_string += gate_to_qasm(gate, node["layout"]) last_layout = node["layout"] # Go one step deeper into the tree. For this, choose the child with the # best score. This is the child whose score matches the score of the node for n in node["children"]: if n["score"] == node["score"]: node = n break # append one layer to the tree update_tree(node, coupling, width=WIDTH, max_gates=max_gates) # complete the qasm string swap_decl = "gate swap a,b { cx a,b; cx b,a; cx a,b;}" end_str = "barrier " # end of the qasm code for q in coupling.get_qubits(): end_str += "%s[%d]," % q end_str = end_str[:-1] + ";\n" # Assume that each qubit q[i] gets measured to c[i] for q in circuit_graph.get_qubits(): end_str += qubit_to_measure_string(q, last_layout, q[1]) qasm_string = circuit_graph.qasm( decls_only=True) + swap_decl + qasm_string + end_str # convert qasm to a dag circuit basis = "u1,u2,u3,cx,id,swap" ast = Qasm(data=qasm_string).parse() u = unroll.Unroller(ast, unroll.DAGBackend(basis.split(","))) return u.execute()
def my_swap_mapper_recursive(circuit_graph, coupling): print_mem() qasm_str2 = circuit_graph.qasm() print_mem() gates = circuit_graph.serial_layers() print_mem() qubits = coupling.get_qubits() layout = {qubit: qubit for qubit in qubits} #layout_copy = deepcopy(layout) end_nodes = [] count = 0 while gates[count]["partition"] != []: count += 1 end_nodes = gates[count:] gates = gates[:count] #gates_copy = deepcopy(gates) #qasm_string = "" """executed_gates, gates, cnots = execute_free_gates(gates, coupling, layout) for gate in executed_gates: qasm_string += gate["graph"].qasm(no_decls = True, aliases = layout) while len(gates) > 0: #print(len(gates)) score, executions, remaining = get_best_action(gates, coupling, layout, DEPTH, width = WIDTH) #print("stop") for i in range(1): edge = executions[i][0] qasm_string += "swap %s[%d],%s[%d]; " % (edge[0][0], edge[0][1], edge[1][0], edge[1][1]) swaped_layout = deepcopy(layout) swaped_layout[reverse_layout_lookup(layout, edge[0])] = edge[1] swaped_layout[reverse_layout_lookup(layout, edge[1])] = edge[0] layout = swaped_layout for gate in executions[i][1]: qasm_string += gate["graph"].qasm(no_decls = True, aliases = layout) gates.remove(gate) swap_decl = "gate swap a,b { cx a,b; cx b,a; cx a,b;}" end_nodes_qasm = "" for n in end_nodes: end_nodes_qasm += n["graph"].qasm(no_decls=True, aliases =layout) qasm_string = circuit_graph.qasm(decls_only=True)+swap_decl+qasm_string+end_nodes_qasm print(qasm_string) print("")""" qasm_string = "" node = build_tree(None, gates, coupling, layout, DEPTH + 1, width=WIDTH) #print("stop") run = True while run: run = node["remaining_gates"] != [] if node["swap"] != None: edge = node["swap"] qasm_string += "swap %s[%d],%s[%d]; " % (edge[0][0], edge[0][1], edge[1][0], edge[1][1]) for gate in node["executed_gates"]: qasm_string += gate["graph"].qasm(no_decls=True, aliases=node["layout"]) last_layout = node["layout"] #scores = [] #for n in node["next_nodes"]: # scores.append(n["score"]) #print(scores) for n in node["next_nodes"]: if n["score"] == node["score"]: node = n break #print(node["score"]) update_tree(node, coupling, width=WIDTH) #print("stop") swap_decl = "gate swap a,b { cx a,b; cx b,a; cx a,b;}" end_nodes_qasm = "" for n in end_nodes: end_nodes_qasm += n["graph"].qasm(no_decls=True, aliases=last_layout) qasm_string = circuit_graph.qasm( decls_only=True) + swap_decl + qasm_string + end_nodes_qasm #print(qasm_string) basis = "u1,u2,u3,cx,id,swap" ast = Qasm(data=qasm_string).parse() u = unroll.Unroller(ast, unroll.DAGBackend(basis.split(","))) #print("Done.") return u.execute(), layout