diff --git a/examples/partitionor_test_randomcircuits.py b/examples/partitionor_test_randomcircuits.py new file mode 100644 index 0000000..748bd9c --- /dev/null +++ b/examples/partitionor_test_randomcircuits.py @@ -0,0 +1,48 @@ +from qiskit import transpile +from qiskit.circuit.random import random_circuit +from qiskit_aer import Aer +from qiskit.qasm2 import dumps +from qdao import Engine +from quafu import QuantumCircuit +from qdao.circuit import ( + BasePartitioner, + CircuitHelperProvider, + QdaoCircuit, + StaticPartitioner, + UniQPartitioner, + BaselinePartitioner, +) +import pandas as pd + +data = {"qubit": [], "static-partitionor": [], "Uniq-partitioner": []} +for i in range(8, 26): + for j in range(10): + num_qubits = i + num_primary = i - 4 + num_local = 0 + circ = random_circuit(num_qubits, i, measure=False, max_operands=2) + backend = Aer.get_backend("aer_simulator") + circ = transpile(circ, backend=backend) + quafu_circ = QuantumCircuit(1) + quafu_circ.from_openqasm(dumps(circ)) + eng = Engine( + partitioner=StaticPartitioner( + np=num_primary, nl=num_local, backend="quafu" + ), + circuit=quafu_circ, + num_primary=num_primary, + num_local=num_local, + backend="quafu", + ) + data["qubit"].append(i) + data["static-partitionor"].append(eng.run()) + eng = Engine( + partitioner=UniQPartitioner(np=num_primary, nl=num_local, backend="quafu"), + circuit=quafu_circ, + num_primary=num_primary, + num_local=num_local, + backend="quafu", + ) + data["Uniq-partitioner"].append(eng.run()) +df = pd.DataFrame(data) +df.to_csv("test_randomcircuit.csv", index=False) diff --git a/qdao/circuit.py b/qdao/circuit.py index c98728a..3c02264 100644 --- a/qdao/circuit.py +++ b/qdao/circuit.py @@ -2,7 +2,9 @@ This module provides methods to partition original circuit into sub-circuits. """ + import logging +import copy from typing import Any, List from qdao.qiskit.circuit import QiskitCircuitWrapper @@ -72,7 +74,8 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: sub_circ = self._circ_helper.gen_sub_circ([instr], self._nl, self._np) sub_circs.append(sub_circ) logging.info("Find sub-circuit: {}, qubits: {}".format(sub_circ.circ, qset)) - + print("----------BaselinePartitioner-----------") + print("num of sub-circuits:" + str(len(sub_circs))) return sub_circs @@ -109,7 +112,99 @@ def run(self, circuit: Any) -> List[QdaoCircuit]: if instrs: sub_circ = self._circ_helper.gen_sub_circ(instrs, self._nl, self._np) sub_circs.append(sub_circ) + print("----------StaticPartitioner-----------") + print("num of sub-circuits:" + str(len(sub_circs))) + return sub_circs + + +class dptask: + """To assist the Uniq partitioning algorithm""" + + def __init__(self, qubitNum: int, gateNum: int): + self.gateNum = gateNum + self.qubitNum = qubitNum + + def addGate(self, gateIndex: int, gatePos): + if isinstance(gatePos, int): + gatePos = [gatePos] + for j in range(self.qubitNum): + self.result_bit[gateIndex + 1][j] += self.result_bit[gateIndex][j] + self.result_op[gateIndex + 1][j] += self.result_op[gateIndex][j] + if j in gatePos: + self.result_op[gateIndex + 1][j].append(gateIndex + 1) + if len(gatePos) == 2: + a = [x for x in gatePos if x != j][0] + self.result_bit[gateIndex + 1][j] += self.result_bit[gateIndex][a] + self.result_bit[gateIndex + 1][j] = list( + set(self.result_bit[gateIndex + 1][j]) + ) + self.result_op[gateIndex + 1][j] += self.result_op[gateIndex][a] + self.result_op[gateIndex + 1][j] = list( + set(self.result_op[gateIndex + 1][j]) + ) + + def createTask(self, ops): + self.result_bit = [ + [[] for _ in range(self.qubitNum)] for _ in range(self.gateNum + 1) + ] + self.result_op = [ + [[] for _ in range(self.qubitNum)] for _ in range(self.gateNum + 1) + ] + for i in range(self.qubitNum): + self.result_bit[0][i].append(i) + for i in range(self.gateNum): + self.addGate(i, ops[i].pos) + + def selectSubCircuit(self, numSubcircuit: int) -> (list[int], int): + """Find sub-lines that meet the requirements from the array in + preprocessing. The return value is the number of bits representing + the sub-circuit. When a sub-circuit that fails to obtain enough + qubits is selected at one time, the return value can be used for + the next selection.""" + numQubit = 0 + numOp = 0 + opList = [] + bitList = [] + for i in range(self.gateNum): + for j in range(self.qubitNum): + if ( + len(self.result_op[i + 1][j]) >= numOp + and len(self.result_bit[i + 1][j]) <= numSubcircuit + ): + opList = self.result_op[i + 1][j] + bitList = self.result_bit[i + 1][j] + numQubit = len(self.result_bit[i + 1][j]) + numOp = len(self.result_op[i + 1][j]) + return (opList, numQubit) + + +class UniQPartitioner(BasePartitioner): + """Partitioner in UniQ""" + def run(self, circuit: Any) -> List[QdaoCircuit]: + self._circ_helper.circ = circuit + ops = copy.deepcopy(self._circ_helper.instructions) + active = self._np + m = self._circ_helper.circ.num + sub_circs = [] + while len(ops) != 0: + needQubit = active + instrs = [] + while needQubit != 0 and len(ops) != 0: + task = dptask(m, len(ops)) + task.createTask(ops) + opList, numQubit = task.selectSubCircuit(needQubit) + if numQubit == 0: + break + needQubit -= numQubit + for i in range(len(ops), -1, -1): + if i + 1 in opList: + instrs.append(ops[i]) + ops.pop(i) + sub_circ = self._circ_helper.gen_sub_circ(instrs, self._nl, self._np) + sub_circs.append(sub_circ) + print("----------UniqPartitioner-----------") + print("num of sub-circuits:" + str(len(sub_circs))) return sub_circs diff --git a/qdao/quafu/circuit.py b/qdao/quafu/circuit.py index bbb2586..a62022d 100644 --- a/qdao/quafu/circuit.py +++ b/qdao/quafu/circuit.py @@ -72,19 +72,17 @@ def gen_sub_circ(self, instrs: List[QuantumGate], num_local: int, num_primary: i # 1. Get the set of qubits qset = set(range(num_local)) - # instrs: a series of gates # [XGate, XGate, CXGate, RYGate, RXGate, RZGate, CZGate] - for instr in instrs: for q in self.get_instr_qubits(instr): qset.add(q) - # Fix qbit sub_circ = QuantumCircuit(num_primary) # Sorting [] real_qubits = sorted(list(qset)) - + # print("len(real_qubits)=" + str(len(real_qubits))) + # print("num_primary=" + str(num_primary)) assert len(real_qubits) <= num_primary qubit_map = {q: i for i, q in enumerate(real_qubits)}