From 611e8ea2fbbe1c2ef94fd146fc2c4c727a95192f Mon Sep 17 00:00:00 2001 From: Biswayan Nandi Date: Mon, 19 Feb 2024 00:58:28 +0530 Subject: [PATCH 1/4] added circuit visualization --- .../standard_gates/toffoli_utility.py | 3 +- src/qudiet/core/quantum_circuit.py | 85 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py index 28ebcab..f894e36 100644 --- a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py +++ b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py @@ -81,4 +81,5 @@ def __init__(self, qreg: int, dims: Union[int, tuple], plus:int = 1, backend: Ba _unitary, lb, ub = toffoli_dm(qreg, plus, dims) self._acting_on = list(range(lb, ub+1)) super().__init__(qreg, [dims[a] for a in self._acting_on], backend) - self._unitary = _unitary \ No newline at end of file + self._unitary = _unitary + self._plus = plus \ No newline at end of file diff --git a/src/qudiet/core/quantum_circuit.py b/src/qudiet/core/quantum_circuit.py index 7df3ec8..518deff 100644 --- a/src/qudiet/core/quantum_circuit.py +++ b/src/qudiet/core/quantum_circuit.py @@ -386,3 +386,88 @@ def print_opflow_list(self): for i in self.op_flow.peek(): i: Moment = i print(i.peek_list(), "\n") + + def draw(self): + """ + Prints the circuit in the terminal. It does not look pretty but does the job of visualizing circuits. It relies on the operator flow list of the circuit for visualizing the circuit. In the current implementation of this function, use it with caution if dimensionality of qudits are more than 9 because it may not correctly display in such cases. + + E.g. + + (2)|0>--*----Z---------M-- + (2)|1>--|---X(1)-------M-- + (3)|0>-X(2)-------*----M-- + (2)|0>--|---------|----M-- + (2)|0>--|--------X(1)--M-- + (2)|0>--*--------------M-- + + depth: 3, width: 6 + + + 1. Those numbers inside parenthesis on the left of the initial states denotes the dimension of that particular qudit. + 2. Controls are represented by the '*' sign and the number inside parenthesis after 'X' denotes how much it increments. + 3. The symbol 'M' is used for measurement. + 4. '|' these symbols are used to denote that the control lines are passing through. + """ + print(self) + + def __str__(self): + n_qudits = len(self.qregs) + depth = self.get_circuit_config()['depth'] + draw_matrix = [[None for i in range(depth+2)] for i in range(n_qudits)] # this matrix is to represent the entire operator flow list in a "drawable" manner as the raw operator flow is not + circuit_drawing = "" + + for j, moment in enumerate(self.op_flow.peek()): + operators = moment.peek_list() + # print(operators) + for i, op in enumerate(operators): + # TODO: Instead of writing the symbols of standard gates directly, it would be more flexible if they are defined in the __str__ method of their own class. + if isinstance(op, InitState): + draw_matrix[op.qreg][j] = f'({op.dim})|{op.state}>' + if isinstance(op, XGate): + draw_matrix[op.qreg][j] = f'X({op.plus})' + if isinstance(op, HGate): + draw_matrix[op.qreg][j] = 'H' + if isinstance(op, ZGate): + draw_matrix[op.qreg][j] = 'Z' + if isinstance(op, Measurement): + draw_matrix[op._qreg][j] = 'M' + if isinstance(op, CXGate): + control, target = op._acting_on + draw_matrix[control][j] = '*' + draw_matrix[target][j] = f'X({op._plus})' + + end = control if control > target else target + start = control if control < target else target + for x in range(start+1, end): + draw_matrix[x][j] = '|' + if isinstance(op, Toffoli): + controls, target = op.qreg[0], op.qreg[1] + # print(op._acting_on) + for dit in op._acting_on: + if dit in controls: + draw_matrix[dit][j] = '*' + elif dit == target: + draw_matrix[dit][j] = f'X({op._plus})' + else: + draw_matrix[dit][j] = "|" + + for i in range(len(draw_matrix)): + for j in range(len(draw_matrix[i])): + ch = draw_matrix[i][j] + if ch != None: + # Adding a little padding on both left and right sides before adding to the circuit drawing looks nicer + side = 'left' + if len(ch) < 5: + while len(ch) != 5: + if side == 'right': + ch += '-' + side = 'left' + else: + ch = '-'+ch + side = 'right' + else: + ch = '-----' + circuit_drawing += ch + circuit_drawing += '\n' + circuit_drawing += f"\ndepth: {self.get_circuit_config()['depth']}, width: {self.get_circuit_config()['width']}" + return circuit_drawing \ No newline at end of file From 19739399f0c6e6f73515d70f2b7cf651169a279e Mon Sep 17 00:00:00 2001 From: Biswayan Nandi Date: Tue, 20 Feb 2024 22:54:43 +0530 Subject: [PATCH 2/4] currently having a problem when adding multiple toffolies in a loop --- .../standard_gates/toffoli_utility.py | 5 ++- src/qudiet/core/quantum_circuit.py | 8 ++--- src/test.py | 34 +++++++++++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/test.py diff --git a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py index f894e36..08efe36 100644 --- a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py +++ b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py @@ -82,4 +82,7 @@ def __init__(self, qreg: int, dims: Union[int, tuple], plus:int = 1, backend: Ba self._acting_on = list(range(lb, ub+1)) super().__init__(qreg, [dims[a] for a in self._acting_on], backend) self._unitary = _unitary - self._plus = plus \ No newline at end of file + self._plus = plus + self.controls = qreg[0] + self.target = qreg[1] + print(f"[debug] inside Toffoli constructor: controls={self.controls} target={self.target}") \ No newline at end of file diff --git a/src/qudiet/core/quantum_circuit.py b/src/qudiet/core/quantum_circuit.py index 518deff..849de9b 100644 --- a/src/qudiet/core/quantum_circuit.py +++ b/src/qudiet/core/quantum_circuit.py @@ -441,12 +441,12 @@ def __str__(self): for x in range(start+1, end): draw_matrix[x][j] = '|' if isinstance(op, Toffoli): - controls, target = op.qreg[0], op.qreg[1] - # print(op._acting_on) + # controls, target = op.qreg[0], op.qreg[1] + print(f'[debug] inside draw(Toffoli) function: controls={op.controls} target={op.target}') for dit in op._acting_on: - if dit in controls: + if dit in op.controls: draw_matrix[dit][j] = '*' - elif dit == target: + elif dit == op.target: draw_matrix[dit][j] = f'X({op._plus})' else: draw_matrix[dit][j] = "|" diff --git a/src/test.py b/src/test.py new file mode 100644 index 0000000..f9d7670 --- /dev/null +++ b/src/test.py @@ -0,0 +1,34 @@ +from qudiet.core.quantum_circuit import QuantumCircuit + +n = 4 +qc = QuantumCircuit(qregs=[4 for i in range(n)], init_states=[3, 0, 0, 0]) + +# qc.cx((1, 1), 1) +# qc.toffoli(([0, 1, 2], 2), 1) +# qc.z(4) + +ctrls, tgt = [[0], [0, 2], [0, 1, 2]], n-1 + +for i in range(n-1): + # ctrls.append(i) + print(ctrls[i]) + qc.toffoli((ctrls[i], tgt), 1) + qc.measure_all() + # print(qc) + # qc = QuantumCircuit(qregs=[4 for i in range(n)], init_states=[0, 3, 0]) + +qc.measure_all() +print(qc) + +# try: +# print(qc.run()) +# except Exception as e: +# print(f'failure\n{e}') + +# for moment in qc.op_flow.peek(): +# print(moment.peek_list()[1]) +# qc.toffoli(([0], 3), 1) +# qc.toffoli(([0, 1], 3), 1) +# qc.toffoli(([0, 1, 2], 3), 1) +# print(qc) +# print(qc.op_flow.peek()[1].peek_list()[0].qreg) \ No newline at end of file From 141ece4626dd3644a77d5d283a6a6daa54e76e0d Mon Sep 17 00:00:00 2001 From: Biswayan Nandi Date: Wed, 21 Feb 2024 00:18:17 +0530 Subject: [PATCH 3/4] Solved the issue by using .copy function for lists for toffoli only. Not sure in how many places it is still a problem --- .../standard_gates/toffoli_utility.py | 6 +-- src/qudiet/core/quantum_circuit.py | 7 +-- src/test.py | 54 +++++++++++-------- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py index 08efe36..875b7aa 100644 --- a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py +++ b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py @@ -83,6 +83,6 @@ def __init__(self, qreg: int, dims: Union[int, tuple], plus:int = 1, backend: Ba super().__init__(qreg, [dims[a] for a in self._acting_on], backend) self._unitary = _unitary self._plus = plus - self.controls = qreg[0] - self.target = qreg[1] - print(f"[debug] inside Toffoli constructor: controls={self.controls} target={self.target}") \ No newline at end of file + self._controls = qreg[0] + self._target = qreg[1] + print(f"[debug] inside Toffoli constructor: {self.qreg}") \ No newline at end of file diff --git a/src/qudiet/core/quantum_circuit.py b/src/qudiet/core/quantum_circuit.py index 849de9b..4f6c224 100644 --- a/src/qudiet/core/quantum_circuit.py +++ b/src/qudiet/core/quantum_circuit.py @@ -271,6 +271,7 @@ def toffoli(self, qreg: "tuple[list[int], int]", plus: int = 1) -> bool: # # self.op_flow.populate_opflow(DecrMoment("DecrMoment_for_Toffoli", [])) # Density Matrix Based + qreg = (qreg[0].copy(), qreg[1]) _toffoligate = Toffoli( dims=self.qregs, qreg=qreg, plus=plus, backend=self.backend ) @@ -442,11 +443,11 @@ def __str__(self): draw_matrix[x][j] = '|' if isinstance(op, Toffoli): # controls, target = op.qreg[0], op.qreg[1] - print(f'[debug] inside draw(Toffoli) function: controls={op.controls} target={op.target}') + print(f'[debug] inside draw(Toffoli) function: controls={op._controls} target={op._target}') for dit in op._acting_on: - if dit in op.controls: + if dit in op._controls: draw_matrix[dit][j] = '*' - elif dit == op.target: + elif dit == op._target: draw_matrix[dit][j] = f'X({op._plus})' else: draw_matrix[dit][j] = "|" diff --git a/src/test.py b/src/test.py index f9d7670..684cd53 100644 --- a/src/test.py +++ b/src/test.py @@ -1,34 +1,42 @@ from qudiet.core.quantum_circuit import QuantumCircuit +from qudiet.circuit_library.standard_gates.toffoli_utility import Toffoli n = 4 -qc = QuantumCircuit(qregs=[4 for i in range(n)], init_states=[3, 0, 0, 0]) +qc = QuantumCircuit(qregs=[4 for i in range(n)]) +ctrls, target = [], n-1 -# qc.cx((1, 1), 1) -# qc.toffoli(([0, 1, 2], 2), 1) -# qc.z(4) +# ctrls.append(0) +# qc.toffoli((ctrls, target), 1) +# ctrls = [] +# ctrls.append(0) +# ctrls.append(1) +# qc.toffoli((ctrls, target), 2) -ctrls, tgt = [[0], [0, 2], [0, 1, 2]], n-1 for i in range(n-1): - # ctrls.append(i) - print(ctrls[i]) - qc.toffoli((ctrls[i], tgt), 1) - qc.measure_all() - # print(qc) - # qc = QuantumCircuit(qregs=[4 for i in range(n)], init_states=[0, 3, 0]) + ctrls.append(i) + print(f"({tuple(ctrls)}, {target})") + qc.toffoli([ctrls, target], i) qc.measure_all() print(qc) -# try: -# print(qc.run()) -# except Exception as e: -# print(f'failure\n{e}') - -# for moment in qc.op_flow.peek(): -# print(moment.peek_list()[1]) -# qc.toffoli(([0], 3), 1) -# qc.toffoli(([0, 1], 3), 1) -# qc.toffoli(([0, 1, 2], 3), 1) -# print(qc) -# print(qc.op_flow.peek()[1].peek_list()[0].qreg) \ No newline at end of file + +for index, moment in enumerate(qc.op_flow.peek()): + print(f"{index+1}th moment") + for op in moment.peek_list(): + print('\t', op) + if isinstance(op, Toffoli): + print(f'\t\t[got a Toffoli] {op.qreg}') + print() + +# class A: +# def __init__(self, x: list): +# self.x = x.copy() +# def __str__(self) -> str: +# return f"{self.x}" + +# l1 = [[0], 1] +# a = A(l1) +# l1.append('bummer') +# print(a) \ No newline at end of file From 489be12c5f79b91b6e2cc616eb9e8c553985e4aa Mon Sep 17 00:00:00 2001 From: Biswayan Nandi Date: Wed, 21 Feb 2024 00:56:21 +0530 Subject: [PATCH 4/4] Ready to be merged with main after solving the issue with .copy --- .../standard_gates/toffoli_utility.py | 5 +-- src/qudiet/core/quantum_circuit.py | 8 ++-- src/test.py | 42 ------------------- 3 files changed, 5 insertions(+), 50 deletions(-) delete mode 100644 src/test.py diff --git a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py index 875b7aa..f894e36 100644 --- a/src/qudiet/circuit_library/standard_gates/toffoli_utility.py +++ b/src/qudiet/circuit_library/standard_gates/toffoli_utility.py @@ -82,7 +82,4 @@ def __init__(self, qreg: int, dims: Union[int, tuple], plus:int = 1, backend: Ba self._acting_on = list(range(lb, ub+1)) super().__init__(qreg, [dims[a] for a in self._acting_on], backend) self._unitary = _unitary - self._plus = plus - self._controls = qreg[0] - self._target = qreg[1] - print(f"[debug] inside Toffoli constructor: {self.qreg}") \ No newline at end of file + self._plus = plus \ No newline at end of file diff --git a/src/qudiet/core/quantum_circuit.py b/src/qudiet/core/quantum_circuit.py index 4f6c224..833a355 100644 --- a/src/qudiet/core/quantum_circuit.py +++ b/src/qudiet/core/quantum_circuit.py @@ -442,12 +442,12 @@ def __str__(self): for x in range(start+1, end): draw_matrix[x][j] = '|' if isinstance(op, Toffoli): - # controls, target = op.qreg[0], op.qreg[1] - print(f'[debug] inside draw(Toffoli) function: controls={op._controls} target={op._target}') + controls, target = op.qreg[0], op.qreg[1] + # print(op._acting_on) for dit in op._acting_on: - if dit in op._controls: + if dit in controls: draw_matrix[dit][j] = '*' - elif dit == op._target: + elif dit == target: draw_matrix[dit][j] = f'X({op._plus})' else: draw_matrix[dit][j] = "|" diff --git a/src/test.py b/src/test.py deleted file mode 100644 index 684cd53..0000000 --- a/src/test.py +++ /dev/null @@ -1,42 +0,0 @@ -from qudiet.core.quantum_circuit import QuantumCircuit -from qudiet.circuit_library.standard_gates.toffoli_utility import Toffoli - -n = 4 -qc = QuantumCircuit(qregs=[4 for i in range(n)]) -ctrls, target = [], n-1 - -# ctrls.append(0) -# qc.toffoli((ctrls, target), 1) -# ctrls = [] -# ctrls.append(0) -# ctrls.append(1) -# qc.toffoli((ctrls, target), 2) - - -for i in range(n-1): - ctrls.append(i) - print(f"({tuple(ctrls)}, {target})") - qc.toffoli([ctrls, target], i) - -qc.measure_all() -print(qc) - - -for index, moment in enumerate(qc.op_flow.peek()): - print(f"{index+1}th moment") - for op in moment.peek_list(): - print('\t', op) - if isinstance(op, Toffoli): - print(f'\t\t[got a Toffoli] {op.qreg}') - print() - -# class A: -# def __init__(self, x: list): -# self.x = x.copy() -# def __str__(self) -> str: -# return f"{self.x}" - -# l1 = [[0], 1] -# a = A(l1) -# l1.append('bummer') -# print(a) \ No newline at end of file