def parse(file_path, prec=15): """ Simple helper - file_path: Path to the OpenQASM file - prec: Precision for the returned string """ qasm = Qasm(file_path) return qasm.parse().qasm(prec)
def _create_qobj(self, circuits, circuit_config, backend, seed, resources, shots, do_compile): # local and remote backends currently need different # compilied circuit formats formatted_circuits = [] if do_compile: for circuit in circuits: formatted_circuits.append(None) else: if backend in backends.local_backends(): for circuit in self.circuits: basis = ['u1', 'u2', 'u3', 'cx', 'id'] unroller = Unroller # TODO: No instanceof here! Refactor this class if isinstance(circuit, DAGCircuit): unroller = DagUnroller elif isinstance(circuit, QuantumCircuit): # TODO: We should remove this code path (it's redundant and slow) circuit = Qasm(data=circuit.qasm()).parse() unroller_instance = unroller(circuit, JsonBackend(basis)) compiled_circuit = unroller_instance.execute() formatted_circuits.append(compiled_circuit) else: for circuit in self.circuits: formatted_circuits.append(circuit.qasm(qeflag=True)) # create circuit component of qobj circuit_records = [] if circuit_config is None: config = {'coupling_map': None, 'basis_gates': 'u1,u2,u3,cx,id', 'layout': None, 'seed': seed} circuit_config = [config] * len(self.circuits) for circuit, fcircuit, name, config in zip(self.circuits, formatted_circuits, self.names, circuit_config): record = { 'name': name, 'compiled_circuit': None if do_compile else fcircuit, 'compiled_circuit_qasm': None if do_compile else fcircuit, 'circuit': circuit, 'config': config } circuit_records.append(record) return {'id': self._generate_job_id(length=10), 'config': { 'max_credits': resources['max_credits'], 'shots': shots, 'backend': backend }, 'circuits': circuit_records}
def test_all_valid_nodes(self): """Test that the tree contains only Node subclasses.""" def inspect(node): for child in node.children: self.assertTrue(isinstance(child, Node)) inspect(child) # Test the canonical example file. qasm = Qasm(self.QASM_FILE_PATH) res = qasm.parse() inspect(res) # Test a file containing if instructions. qasm_if = Qasm(self.QASM_FILE_PATH_IF) res_if = qasm_if.parse() inspect(res_if)
def setUp(self): qasm_filename = self._get_resource_path('qasm/example.qasm') qasm_ast = Qasm(filename=qasm_filename).parse() qasm_dag = Unroller(qasm_ast, DAGBackend()).execute() qasm_json = DagUnroller(qasm_dag, JsonBackend(qasm_dag.basis)).execute() qr = QuantumRegister(2, 'q') cr = ClassicalRegister(2, 'c') qc = QuantumCircuit(qr, cr) qc.h(qr[0]) qc.measure(qr[0], cr[0]) qc_dag = DAGCircuit.fromQuantumCircuit(qc) qc_json = DagUnroller(qc_dag, JsonBackend(qc_dag.basis)).execute() # create qobj compiled_circuit1 = QobjExperiment.from_dict(qc_json) compiled_circuit2 = QobjExperiment.from_dict(qasm_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[1].header.name = 'test_circuit2' self.backend = QasmSimulator()
def QASMToMLIR(code: str, strict=False) -> MLIRModule: try: src = Qasm(data=code).parse() except: raise ConversionError("Could not parse QASM") module: MLIRModule = MLIRModule(strict=strict) mainFunc: MLIRFunction = MLIRFunction('qasm_main') mainFunc.addAttribute('qasm.main') for node in src.children: logger.debug(f'>> PARSING:\n {node.qasm()}\n<<<<<<<<') if isinstance(node, Node.Format): module.parseVersion(node) elif isinstance(node, Node.Gate): module.parseGate(node) elif isinstance(node, Node.Opaque): module.parseGate(node) elif isaOperation(node): mainFunc.body.parseOperation(node) else: raise ConversionError(f"Unknown node object of type {type(node)} found: {node.qasm()}") mainFunc.body.buildOp(ReturnOp) module.addDecl(mainFunc) return module
def showtree(node, indent=0): if type(node) is str: node = Qasm(data=node).parse() pref = ' ' * (indent*4) logger.debug(f'{pref}{type(node)}') for child in node.children: showtree(child, indent + 1)
def circuit_from_qasm_string(qasm_string, name=None, basis_gates="id,u0,u1,u2,u3,x,y,z,h,s,sdg,t,tdg," "rx,ry,rz,cx,cy,cz,ch,crz,cu1,cu3,swap,ccx," "cswap"): """Construct a quantum circuit from a qasm representation (string). Args: qasm_string (str): a string of qasm, or a filename containing qasm. basis_gates (str): basis gates for the quantum circuit. name (str or None): the name of the quantum circuit after loading qasm text into it. If no name given, assign automatically. Returns: QuantumCircuit: circuit constructed from qasm. Raises: QISKitError: if the string is not valid QASM """ node_circuit = Qasm(data=qasm_string).parse() unrolled_circuit = Unroller(node_circuit, CircuitBackend(basis_gates.split(","))) circuit_unrolled = unrolled_circuit.execute() if name: circuit_unrolled.name = name return circuit_unrolled
def my_swap_mapper_tree(circuit_graph, coupling): random.seed(123) gates = read_gates(circuit_graph) #gates = circuit_graph.serial_layers() qubits = coupling.get_qubits() layout = {qubit : qubit for qubit in qubits} """end_nodes = [] count = 0 while gates[count]["partition"]!=[]: count += 1 end_nodes = gates[count:] gates = gates[:count]""" qasm_string = "" node = build_tree(None, gates, coupling, layout, DEPTH + 1, width = WIDTH) 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"]) qasm_string += gate_to_qasm(gate, node["layout"]) last_layout = node["layout"] for n in node["next_nodes"]: if n["score"] == node["score"]: node = n break update_tree(node, coupling, width=WIDTH) 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)""" end_str = "barrier " for q in coupling.get_qubits(): end_str += "%s[%d]," % q end_str = end_str[:-1]+";\n" for q in circuit_graph.get_qubits(): end_str += qubit_to_measure_string(last_layout[q], q[1]) qasm_string = circuit_graph.qasm(decls_only=True)+swap_decl+qasm_string+end_str print(qasm_string+"\n") basis = "u1,u2,u3,cx,id,swap" ast = Qasm(data=qasm_string).parse() u = unroll.Unroller(ast, unroll.DAGBackend(basis.split(","))) return u.execute(), layout
def direction_mapper(circuit_graph, coupling_graph, verbose=False): """Change the direction of CNOT gates to conform to CouplingGraph. circuit_graph = input Circuit coupling_graph = corresponding CouplingGraph verbose = optional flag to print more information Adds "h" to the circuit basis. Returns a Circuit object containing a circuit equivalent to circuit_graph but with CNOT gate directions matching the edges of coupling_graph. Raises an exception if the circuit_graph does not conform to the coupling_graph. """ if "cx" not in circuit_graph.basis: return circuit_graph if circuit_graph.basis["cx"] != (2, 0, 0): raise QISKitException("cx gate has unexpected signature %s" % circuit_graph.basis["cx"]) flipped_qasm = "OPENQASM 2.0;\n" + \ "gate cx c,t { CX c,t; }\n" + \ "gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }\n" + \ "gate h a { u2(0,pi) a; }\n" + \ "gate cx_flipped a,b { h a; h b; cx b, a; h a; h b; }\n" + \ "qreg q[2];\n" + \ "cx_flipped q[0],q[1];\n" u = unroll.Unroller(Qasm(data=flipped_qasm).parse(), unroll.CircuitBackend(["cx", "h"])) u.execute() flipped_cx_circuit = u.backend.circuit cx_node_list = circuit_graph.get_named_nodes("cx") cg_edges = coupling_graph.get_edges() for cx_node in cx_node_list: nd = circuit_graph.multi_graph.node[cx_node] cxedge = tuple(nd["qargs"]) if cxedge in cg_edges: if verbose: print("cx %s[%d], %s[%d] -- OK" % (cxedge[0][0], cxedge[0][1], cxedge[1][0], cxedge[1][1])) continue elif (cxedge[1], cxedge[0]) in cg_edges: circuit_graph.substitute_circuit_one(cx_node, flipped_cx_circuit, wires=[("q", 0), ("q", 1)]) if verbose: print("cx %s[%d], %s[%d] -FLIP" % (cxedge[0][0], cxedge[0][1], cxedge[1][0], cxedge[1][1])) else: raise QISKitException("circuit incompatible with CouplingGraph: " + "cx on %s" % cxedge) return circuit_graph
def setUp(self): self.seed = 88 self.backend = QasmSimulatorPy() backend_basis = self.backend.configuration().basis_gates qasm_filename = self._get_resource_path('qasm/example.qasm') qasm_ast = Qasm(filename=qasm_filename).parse() qasm_dag = Unroller(qasm_ast, DAGBackend()).execute() qasm_dag = DagUnroller(qasm_dag, DAGBackend(backend_basis)).expand_gates() qasm_json = DagUnroller(qasm_dag, JsonBackend(qasm_dag.basis)).execute() compiled_circuit = QobjExperiment.from_dict(qasm_json) compiled_circuit.header.name = 'test' self.qobj = Qobj( qobj_id='test_sim_single_shot', config=QobjConfig( shots=1024, memory_slots=6, max_credits=3, seed=self.seed ), experiments=[compiled_circuit], header=QobjHeader(backend_name='qasm_simulator_py') )
def test_get_tokens(self): """Test whether we get only valid tokens.""" qasm = Qasm(self.qasm_file_path) for token in qasm.get_tokens(): self.assertTrue(isinstance(token, ply.lex.LexToken))
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 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 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 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 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 test_get_tokens(self): """Test whether we get only valid tokens.""" qasm = Qasm(self.QASM_FILE_PATH) for token in qasm.get_tokens(): self.assertTrue(isinstance(token, ply.lex.LexToken))
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 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