-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add UniQ partitioning algorithm #16
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. naming convention please follow google style guide |
||
"""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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, please refer to the style guide. In short
|
||
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function name will confuse users and developers. Basically, we already defined |
||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not necessary to explicitly use tuple. |
||
|
||
|
||
class UniQPartitioner(BasePartitioner): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should consider registering |
||
"""Partitioner in UniQ""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest to add a reference here. Refer to sphinx docstring sections to see commonly used keywords. Currently, we're not creating a document website. But trying to write standard docstrings will reduce lots of effort once we need to have a website holding documentation of this project.
|
||
|
||
def run(self, circuit: Any) -> List[QdaoCircuit]: | ||
self._circ_helper.circ = circuit | ||
ops = copy.deepcopy(self._circ_helper.instructions) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need to make a deep copy of instructions? If necessary, we need to measure the performance overhead when circuit becomes very large with a large number of instructions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because after each selection of a subcircuit, the selected gate must be removed and preprocessed again. In order not to affect the original data, a deep copy operation is selected. This really isn't necessary and if it would cause a performance hit, I would build an array to record this kind of property. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. based on my understanding, all operations you performed on |
||
active = self._np | ||
m = self._circ_helper.circ.num | ||
sub_circs = [] | ||
while len(ops) != 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
needQubit = active | ||
instrs = [] | ||
while needQubit != 0 and len(ops) != 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe better to use |
||
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))) | ||
Comment on lines
+206
to
+207
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be better to use |
||
return sub_circs | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we do not need to commit this file into this project. If necessary, it would be better to appear in our test directory