Skip to content
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

feat: add U and GPhase gates #799

Merged
merged 55 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d784d88
Add U gate
jcjaskula-aws Nov 14, 2023
9633a2a
modification according to feedback
jcjaskula-aws Nov 14, 2023
e68e78c
fix linters
jcjaskula-aws Nov 14, 2023
0c2c9e8
clean commented code
jcjaskula-aws Nov 14, 2023
c3044cb
first version of gphase
jcjaskula-aws Nov 15, 2023
d6d6886
handle drawing and control global phase
jcjaskula-aws Nov 15, 2023
388367c
Adding a global phase attribute
jcjaskula-aws Nov 17, 2023
7a52eb8
add global phase to circuit unitary
jcjaskula-aws Nov 18, 2023
d8917f3
Merge branch 'main' into jcjaskula-aws/add_u_gate
jcjaskula-aws Nov 19, 2023
f35cc38
first draft tests to check coverage
jcjaskula-aws Nov 20, 2023
fa4cb3a
add more tests
jcjaskula-aws Nov 20, 2023
5ddf96e
add test with parametric global phase
jcjaskula-aws Nov 20, 2023
3c0d766
add test for neg control qubit printing
jcjaskula-aws Nov 20, 2023
9f8a547
clean up
jcjaskula-aws Nov 20, 2023
76989f7
simplify ctrl-gphase transform
jcjaskula-aws Nov 20, 2023
f1204d0
feat: add str, repr and getitem to BasisState
ajberdy Nov 21, 2023
f5d0511
add repr coverage
ajberdy Nov 21, 2023
f68b084
add index
jcjaskula-aws Nov 21, 2023
5c56724
add pop
jcjaskula-aws Nov 21, 2023
70844ee
Merge branch 'basis-state-slice' into jcjaskula-aws/add_u_gate
jcjaskula-aws Nov 21, 2023
dabb790
fix phase target qubit
jcjaskula-aws Nov 21, 2023
31a60f8
fix typing
jcjaskula-aws Nov 21, 2023
0b02f14
add index and pop tests
jcjaskula-aws Nov 21, 2023
0a7985e
fix code coverage
jcjaskula-aws Nov 21, 2023
c75f331
move unitary matrices
jcjaskula-aws Nov 21, 2023
df1b1a9
use a subindex in MomentKey
jcjaskula-aws Nov 23, 2023
daa9cd9
print global phase integration
jcjaskula-aws Nov 24, 2023
9e5f7dc
fix docstrings
jcjaskula-aws Nov 24, 2023
aa46064
fix circuits zero total global phase
jcjaskula-aws Nov 24, 2023
590d9c8
fix edge cases
jcjaskula-aws Nov 24, 2023
dec59aa
fix to_unitary
jcjaskula-aws Nov 24, 2023
ef91d3d
temporary fix that checks classname
jcjaskula-aws Nov 25, 2023
dfdecfe
clean up test conditions
jcjaskula-aws Nov 26, 2023
e5eaa21
change logic according to feedback
jcjaskula-aws Nov 28, 2023
b8f0f33
update docstring
jcjaskula-aws Nov 28, 2023
244d708
clean tests
jcjaskula-aws Nov 28, 2023
c185e41
update tests
jcjaskula-aws Nov 28, 2023
4efb8bc
replace control symbols
jcjaskula-aws Nov 30, 2023
ccb81fa
use box drawing characters
jcjaskula-aws Nov 30, 2023
4aa7545
Revert "use box drawing characters"
jcjaskula-aws Dec 1, 2023
c2291f8
Revert "replace control symbols"
jcjaskula-aws Dec 1, 2023
9386bca
simplify all gphase case
jcjaskula-aws Dec 1, 2023
ecbdff1
change preprare_y_axis function name
jcjaskula-aws Dec 3, 2023
696974d
Merge branch 'main' into jcjaskula-aws/add_u_gate
jcjaskula-aws Dec 11, 2023
ad1053f
create an helper function to compute the global phase
jcjaskula-aws Dec 11, 2023
cb6baac
make control_basis_state more explicit
jcjaskula-aws Dec 11, 2023
52ce20d
add comment and clean grouping
jcjaskula-aws Dec 11, 2023
d61a0d1
add test_only_neg_control_qubits
jcjaskula-aws Dec 11, 2023
b14c03b
parametrize test_one_gate_with_global_phase
jcjaskula-aws Dec 12, 2023
095fb68
reformat
jcjaskula-aws Dec 12, 2023
8ebb0ce
change to printing with fixed precision
jcjaskula-aws Dec 12, 2023
35a92cd
Merge branch 'main' into jcjaskula-aws/add_u_gate
jcjaskula-aws Dec 18, 2023
0626370
Merge branch 'main' into jcjaskula-aws/add_u_gate
jcjaskula-aws Dec 18, 2023
304a01d
Merge branch 'main' into jcjaskula-aws/add_u_gate
jcjaskula-aws Dec 20, 2023
b6e9563
fix docstring
jcjaskula-aws Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 131 additions & 17 deletions src/braket/circuits/ascii_circuit_diagram.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
from braket.circuits.compiler_directive import CompilerDirective
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.moments import MomentType
from braket.circuits.noise import Noise
from braket.circuits.result_type import ResultType
from braket.registers.qubit import Qubit
from braket.registers.qubit_set import QubitSet


Expand All @@ -44,23 +46,26 @@ def build_diagram(circuit: cir.Circuit) -> str:
if not circuit.instructions:
return ""

if all(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments):
return f"Global phase: {circuit.global_phase}"

circuit_qubits = circuit.qubits
circuit_qubits.sort()

# Y Axis Column
y_axis_width = len(str(int(max(circuit_qubits))))
y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1)
for qubit in circuit_qubits:
y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5)
y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width)
y_axis_str, global_phase = AsciiCircuitDiagram._prepare_diagram_vars(
circuit, circuit_qubits
)

time_slices = circuit.moments.time_slices()
column_strs = []

# Moment columns
for time, instructions in time_slices.items():
global_phase = AsciiCircuitDiagram._compute_moment_global_phase(
global_phase, instructions
)
moment_str = AsciiCircuitDiagram._ascii_diagram_column_set(
str(time), circuit_qubits, instructions
str(time), circuit_qubits, instructions, global_phase
)
column_strs.append(moment_str)

Expand All @@ -71,7 +76,7 @@ def build_diagram(circuit: cir.Circuit) -> str:
if target_result_types:
column_strs.append(
AsciiCircuitDiagram._ascii_diagram_column_set(
"Result Types", circuit_qubits, target_result_types
"Result Types", circuit_qubits, target_result_types, global_phase
)
)

Expand All @@ -84,6 +89,9 @@ def build_diagram(circuit: cir.Circuit) -> str:
# Time on top and bottom
lines.append(lines[0])

if global_phase:
lines.append(f"\nGlobal phase: {global_phase}")

# Additional result types line on bottom
if additional_result_types:
lines.append(f"\nAdditional result types: {', '.join(additional_result_types)}")
Expand All @@ -97,6 +105,49 @@ def build_diagram(circuit: cir.Circuit) -> str:

return "\n".join(lines)

@staticmethod
def _prepare_diagram_vars(
circuit: cir.Circuit, circuit_qubits: QubitSet
) -> tuple[str, float | None]:
# Y Axis Column
y_axis_width = len(str(int(max(circuit_qubits))))
y_axis_str = "{0:{width}} : |\n".format("T", width=y_axis_width + 1)

global_phase = None
if any(m.moment_type == MomentType.GLOBAL_PHASE for m in circuit._moments):
y_axis_str += "{0:{width}} : |\n".format("GP", width=y_axis_width)
global_phase = 0

for qubit in circuit_qubits:
y_axis_str += "{0:{width}}\n".format(" ", width=y_axis_width + 5)
y_axis_str += "q{0:{width}} : -\n".format(str(int(qubit)), width=y_axis_width)

return y_axis_str, global_phase

@staticmethod
def _compute_moment_global_phase(
global_phase: float | None, items: list[Instruction]
) -> float | None:
"""
Compute the integrated phase at a certain moment.

Args:
global_phase (float | None): The integrated phase up to the computed moment
items (list[Instruction]): list of instructions

Returns:
float | None: The updated integrated phase.
"""
moment_phase = 0
for item in items:
if (
isinstance(item, Instruction)
and isinstance(item.operator, Gate)
and item.operator.name == "GPhase"
):
moment_phase += item.operator.angle
return global_phase + moment_phase if global_phase is not None else None

@staticmethod
def _ascii_group_items(
circuit_qubits: QubitSet,
Expand All @@ -120,7 +171,15 @@ def _ascii_group_items(
):
continue

if (isinstance(item, ResultType) and not item.target) or (
# As a zero-qubit gate, GPhase can be grouped with anything. We set qubit_range
# to an empty list and we just add it to the first group below.
if (
isinstance(item, Instruction)
and isinstance(item.operator, Gate)
and item.operator.name == "GPhase"
):
qubit_range = QubitSet()
elif (isinstance(item, ResultType) and not item.target) or (
isinstance(item, Instruction) and isinstance(item.operator, CompilerDirective)
):
qubit_range = circuit_qubits
Expand Down Expand Up @@ -175,7 +234,10 @@ def _categorize_result_types(

@staticmethod
def _ascii_diagram_column_set(
col_title: str, circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]]
col_title: str,
circuit_qubits: QubitSet,
items: list[Union[Instruction, ResultType]],
global_phase: float | None,
) -> str:
"""
Return a set of columns in the ASCII string diagram of the circuit for a list of items.
Expand All @@ -184,6 +246,7 @@ def _ascii_diagram_column_set(
col_title (str): title of column set
circuit_qubits (QubitSet): qubits in circuit
items (list[Union[Instruction, ResultType]]): list of instructions or result types
global_phase (float | None): the integrated global phase up to this set

Returns:
str: An ASCII string diagram for the column set.
Expand All @@ -193,7 +256,7 @@ def _ascii_diagram_column_set(
groupings = AsciiCircuitDiagram._ascii_group_items(circuit_qubits, items)

column_strs = [
AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1])
AsciiCircuitDiagram._ascii_diagram_column(circuit_qubits, grouping[1], global_phase)
for grouping in groupings
]

Expand All @@ -220,17 +283,20 @@ def _ascii_diagram_column_set(

@staticmethod
def _ascii_diagram_column(
circuit_qubits: QubitSet, items: list[Union[Instruction, ResultType]]
circuit_qubits: QubitSet,
items: list[Union[Instruction, ResultType]],
global_phase: float | None = None,
) -> str:
"""
Return a column in the ASCII string diagram of the circuit for a given list of items.

Args:
circuit_qubits (QubitSet): qubits in circuit
items (list[Union[Instruction, ResultType]]): list of instructions or result types
global_phase (float | None): the integrated global phase up to this column

Returns:
str: An ASCII string diagram for the specified moment in time for a column.
str: an ASCII string diagram for the specified moment in time for a column.
"""
symbols = {qubit: "-" for qubit in circuit_qubits}
margins = {qubit: " " for qubit in circuit_qubits}
Expand All @@ -252,12 +318,26 @@ def _ascii_diagram_column(
num_after = len(circuit_qubits) - 1
after = ["|"] * (num_after - 1) + ([marker] if num_after else [])
ascii_symbols = [ascii_symbol] + after
elif (
isinstance(item, Instruction)
and isinstance(item.operator, Gate)
and item.operator.name == "GPhase"
):
target_qubits = circuit_qubits
control_qubits = QubitSet()
target_and_control = QubitSet()
qubits = circuit_qubits
ascii_symbols = "-" * len(circuit_qubits)
else:
if isinstance(item.target, list):
target_qubits = reduce(QubitSet.union, map(QubitSet, item.target), QubitSet())
else:
target_qubits = item.target
control_qubits = getattr(item, "control", QubitSet())
map_control_qubit_states = AsciiCircuitDiagram._build_map_control_qubits(
item, control_qubits
)

target_and_control = target_qubits.union(control_qubits)
qubits = QubitSet(range(min(target_and_control), max(target_and_control) + 1))

Expand Down Expand Up @@ -288,20 +368,54 @@ def _ascii_diagram_column(
else ascii_symbols[item_qubit_index]
)
elif qubit in control_qubits:
symbols[qubit] = "C"
symbols[qubit] = "C" if map_control_qubit_states[qubit] else "N"
jcjaskula-aws marked this conversation as resolved.
Show resolved Hide resolved
else:
symbols[qubit] = "|"

# Set the margin to be a connector if not on the first qubit
if qubit != min(target_and_control):
if target_and_control and qubit != min(target_and_control):
margins[qubit] = "|"

symbols_width = max([len(symbol) for symbol in symbols.values()])
output = AsciiCircuitDiagram._create_output(symbols, margins, circuit_qubits, global_phase)
return output

@staticmethod
def _create_output(
symbols: dict[Qubit, str],
margins: dict[Qubit, str],
qubits: QubitSet,
global_phase: float | None,
) -> str:
symbols_width = max([len(symbol) for symbol in symbols.values()])
output = ""
for qubit in circuit_qubits:

if global_phase is not None:
global_phase_str = (
f"{global_phase:.2f}" if isinstance(global_phase, float) else str(global_phase)
)
symbols_width = max([symbols_width, len(global_phase_str)])
output += "{0:{fill}{align}{width}}|\n".format(
global_phase_str,
fill=" ",
align="^",
width=symbols_width,
)

for qubit in qubits:
output += "{0:{width}}\n".format(margins[qubit], width=symbols_width + 1)
output += "{0:{fill}{align}{width}}\n".format(
symbols[qubit], fill="-", align="<", width=symbols_width + 1
)
return output

@staticmethod
def _build_map_control_qubits(item: Instruction, control_qubits: QubitSet) -> dict(Qubit, int):
control_state = getattr(item, "control_state", None)
if control_state is not None:
jcjaskula-aws marked this conversation as resolved.
Show resolved Hide resolved
map_control_qubit_states = {
qubit: state for qubit, state in zip(control_qubits, control_state)
}
else:
map_control_qubit_states = {qubit: 1 for qubit in control_qubits}

return map_control_qubit_states
11 changes: 9 additions & 2 deletions src/braket/circuits/braket_program_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,15 @@ def is_builtin_gate(self, name: str) -> bool:
user_defined_gate = self.is_user_defined_gate(name)
return name in BRAKET_GATES and not user_defined_gate

def add_phase_instruction(self, target: tuple[int], phase_value: int) -> None:
raise NotImplementedError
def add_phase_instruction(self, target: tuple[int], phase_value: float) -> None:
"""Add a global phase to the circuit.

Args:
target (tuple[int]): Unused
phase_value (float): The phase value to be applied
"""
instruction = Instruction(BRAKET_GATES["gphase"](phase_value))
self._circuit.add_instruction(instruction)

def add_gate_instruction(
self, gate_name: str, target: tuple[int], *params, ctrl_modifiers: list[int], power: float
Expand Down
13 changes: 12 additions & 1 deletion src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from braket.circuits.free_parameter_expression import FreeParameterExpression
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.moments import Moments
from braket.circuits.moments import Moments, MomentType
from braket.circuits.noise import Noise
from braket.circuits.noise_helpers import (
apply_noise_to_gates,
Expand Down Expand Up @@ -156,6 +156,17 @@ def depth(self) -> int:
"""int: Get the circuit depth."""
return self._moments.depth

@property
def global_phase(self) -> float:
"""float: Get the global phase of the circuit."""
return sum(
[
instr.operator.angle
for moment, instr in self._moments.items()
if moment.moment_type == MomentType.GLOBAL_PHASE
]
)

@property
def instructions(self) -> list[Instruction]:
"""Iterable[Instruction]: Get an `iterable` of instructions in the circuit."""
Expand Down
2 changes: 1 addition & 1 deletion src/braket/circuits/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ def _to_openqasm(

return (
f"{inv_prefix}{power_prefix}{control_prefix}"
f"{self._qasm_name}{param_string} {', '.join(qubits)};"
f"{self._qasm_name}{param_string}{','.join([f' {qubit}' for qubit in qubits])};"
jcjaskula-aws marked this conversation as resolved.
Show resolved Hide resolved
)

@property
Expand Down
Loading