Skip to content

Commit

Permalink
Merge branch 'main' into brennanfreeze/issue82
Browse files Browse the repository at this point in the history
  • Loading branch information
brennanfreeze authored Oct 29, 2024
2 parents 76e6be2 + b6ea993 commit b4a0cd0
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 9 deletions.
89 changes: 84 additions & 5 deletions src/circuit_drawing/circuit_drawing.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
from .drawings import *
from .wire import Wire
from typing import List


class CircuitDrawing:
"""Private handler of generating the circuit drawing.
Note:
This is a work in progress and may see some small bugs/invalid formations.
In other iterations, this will change functionality!
Args:
qubits (int): number of qubits.
Attributes:
qubits (int): Number of qubits from quantum circuit.
circuit_queue (arr): 2D-Queue of strings that format/generate the circuit drawing.
max_length (int): Value to compare when needing to extend rows to match lengths.
"""

def __init__(self, qubits: int):
self.qubits = qubits
self.circuit_queue = []
Expand All @@ -11,15 +27,37 @@ def __init__(self, qubits: int):
self.circuit_queue.append(Wire())

def equal_length(self) -> None:
"""Determines and sets all rows of strings to be equal after a gate insertion"""
for i in range(self.qubits):
while self.circuit_queue[i].length < self.max_length:
self.add_drawing(horizontal_line(), i)

def add_drawing(self, drawing: str, qubit: int) -> None:
"""Inserts drawing at specific qubit drawing row.
Args:
drawing (str): number of qubits.
qubit (int): Which qubit the drawing is inserted at.
"""
self.circuit_queue[qubit].add(drawing)
self.max_length = max(self.max_length, self.circuit_queue[qubit].length)

def insert_single(self, gate: str, qubit: int) -> None:
"""Inserts a single gate drawing into a specific qubit row.
Note:
This is a work in progress and may see some small bugs/invalid formations.
In other iterations, this will change functionality!
Args:
qubits (int): number of qubits.
Attributes:
qubits (int): Number of qubits from quantum circuit.
circuit_queue (arr): 2D-Queue of strings that format/generate the circuit drawing.
max_length (int): Value to compare when needing to extend rows to match lengths.
"""
to_insert = self.max_length - 1
if self.max_length:
while (
Expand All @@ -35,7 +73,13 @@ def insert_single(self, gate: str, qubit: int) -> None:
self.add_drawing(single_gate(gate), qubit)
self.equal_length()

def two_qubit(self, qubit_1: int, qubit_2: int, gate=None) -> None:
def two_qubit(self, qubit_1: int, qubit_2: int, gate: str = "") -> None:
"""Adds a two qubit gate into the circuit drawing.
Args:
qubit_1 (int): start of range of two qubits.
qubit_2 (int): end of range of two qubits.
gate (str): The gate's symbol to be drawn.
"""
self.equal_length()
start = min(qubit_1, qubit_2)
end = max(qubit_1, qubit_2)
Expand All @@ -52,7 +96,13 @@ def two_qubit(self, qubit_1: int, qubit_2: int, gate=None) -> None:
self.add_drawing(swap_point(), qubit_2)
self.equal_length()

def add_multi(self, gate: str, controls, target: int) -> None:
def add_multi(self, gate: str, controls: List[int], target: int) -> None:
"""Adds a multi gate drawing (toffoli for example)
Args:
gate (str): Character symbol of the gate that is being inserted.
controls (arr): array of controls on the gate.
target (int): Where the gate drawing will be inserted.
"""
controls.append(target)
self.equal_length()
for i in range(self.qubits):
Expand All @@ -74,12 +124,28 @@ def add_multi(self, gate: str, controls, target: int) -> None:
self.equal_length()

def add_swap(self, qubit_1, qubit_2) -> None:
"""Draws a swap gate on a circuit drawing.
Args:
qubit_2 (int): first qubit to add 'x' drawing.
qubit_1 (int): second qubit to add 'x' drawing.
"""
self.two_qubit(qubit_1=qubit_1, qubit_2=qubit_2)

def add_control(self, gate, control, target) -> None:
def add_control(self, gate: str, control: int, target: int) -> None:
"""Adds a gate that has a singular controlled qubit to the drawing.
Args:
gate (str): Character symbol for the target drawing.
control (int): Control qubit.
target (int): Target qubit.
"""
self.two_qubit(qubit_1=control, qubit_2=target, gate=gate)

def add_block(self, gate: str, qubits) -> None:
def add_block(self, gate: str, qubits: List[int]) -> None:
"""Adds a block drawing to the circuit drawing (example: RC3X).
Args:
gate (str): String that represents the gate.
qubits (int): Which qubits to know the range of the gate.
"""
center = (max(qubits) + min(qubits)) // 2
for i in range(self.qubits):
if i == center:
Expand All @@ -92,7 +158,16 @@ def add_block(self, gate: str, qubits) -> None:
self.add_drawing(block_connect(), i)
self.equal_length()

def make_wire(self, wire, i) -> str:
def make_wire(self, wire: List[str], i: int) -> str:
"""Creates an entire row drawing to print for a singular qubit.
Args:
wire (arr): Array of strings to concatenate together.
i (int): Which qubit is being drawn.
Returns:
str: Returns a string of the generated qubit row.
"""
top = [" "]
middle = ["q" + str(i) + "─"]
bottom = [" "]
Expand All @@ -109,6 +184,10 @@ def make_wire(self, wire, i) -> str:
return "".join(top) + "\n" + "".join(middle) + "\n" + "".join(bottom) + "\n"

def make(self) -> str:
"""Generates the entirety of the string to print.
Returns:
str: Combination of all qubit strings in a single string.
"""
output = ""
for i in range(len(self.circuit_queue)):
output += self.make_wire(self.circuit_queue[i].content, i)
Expand Down
57 changes: 53 additions & 4 deletions src/circuit_drawing/drawings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
def multi_control(is_connector: bool = False, is_end: bool = False) -> str:
"""Formats a controlled section of the drawing
Args:
is_connector (bool): Determines if this is not the bottom or top of a drawing.
is_end (int): Determines if the connector is at the end of a multi drawing.
Returns:
str: Formatted version of a multi controlled drawing.
"""
res = " ──■── │ "
if is_connector:
res = " │ ──■── │ "
Expand All @@ -8,30 +15,64 @@ def multi_control(is_connector: bool = False, is_end: bool = False) -> str:


def multi_connect() -> str:
"""Shows the drawing of when a qubit is simply passed through in the logic
Returns:
str: A connect drawing.
"""
return " │ ──┼── │ "


def horizontal_line() -> str:
"""A simple horizontal line in the circuit drawing
Returns:
str: A horizontal line block.
"""
return " ───── "


def block_bottom() -> str:
"""When a block has ended this is called in circuit drawing.
Returns:
str: End of the block drawing.
"""
return "│ │┤ ├└───┘"


def block_connect():
def block_connect() -> str:
"""When a qubit is in range of a block
Returns:
str: A connector for a block drawing.
"""
return "│ │┤ ├│ │"


def block_gate(gate: str) -> str:
"""The "center" of a block drawing
Args:
gate (str): Not currently used (Needs to change that!)
Returns:
str: Center block drawing.
"""
return "│ │┤MUL├│ │"


def block_top() -> str:
"""The start of the range for a block drawing
Returns:
str: Top of a block drawing.
"""
return "┌───┐┤ ├│ │"


def single_gate(gate, is_controlled: bool = False, is_start: bool = False):
def single_gate(gate: str, is_controlled: bool = False, is_start: bool = False) -> str:
"""Draws a gate with it's symbol inside.
Args:
gate (str): String char representation of a quantum gate.
is_controlled (bool): Determines if the gate is controlled by another qubit.
is_start (bool): Determines if this gate is upside down with a target qubit.
Returns:
str: A quantum gate to then be inserted into a circuit drawing.
"""
top = "┌─┴─┐" if (is_controlled and not is_start) else "┌───┐"
middle = "┤"
if len(gate) == 1:
Expand All @@ -43,9 +84,17 @@ def single_gate(gate, is_controlled: bool = False, is_start: bool = False):
return top + middle + ("└───┘" if not is_start else "└─┬─┘")


def swap_point():
def swap_point() -> str:
"""Block drawing of a swap gate.
Returns:
str: Swap gate drawing.
"""
return " ──╳── "


def vertical_line():
def vertical_line() -> str:
"""A simple vertical line block.
Returns:
str: A string of a vertical line block.
"""
return " │ "
27 changes: 27 additions & 0 deletions src/circuit_drawing/wire.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,44 @@
class Wire:
"""Private handler of array of strings
Note:
This is a work in progress and may see some small bugs/invalid formations.
In other iterations, this will change functionality!
Attributes:
length (int): Length of the array of content.
content (arr): Array of strings that were inserted into by circuit_drawing.
"""

def __init__(self):
self.length = 0
self.content = []

def add(self, to_add: str) -> None:
"""Appends a string into the content
Args:
to_add (str): String to add.
"""

self.length += 1
self.content.append(to_add)

def insert(self, to_insert: int, to_add: str) -> None:
"""Inserts a string into the content array
Args:
to_insert (int): Where to insert drawing.
to_add (str): The string to insert.
"""

if to_insert >= self.length:
self.add(to_add)
else:
self.content[to_insert] = to_add

def at(self, index: int) -> str:
"""Returns the string at a certain index
Args:
index (int): To try and find the string at a specific location.
Returns:
str: empty if nothing was found, else the value stored at the index.
"""

return "" if index >= self.length else self.content[index]

0 comments on commit b4a0cd0

Please sign in to comment.