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

update: restricting parameter names to not collide with ones we use for OpenQASM generation. #675

Merged
merged 14 commits into from
Aug 21, 2023
6 changes: 3 additions & 3 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,7 @@ def _to_openqasm(
)
for idx, qubit in enumerate(qubits):
qubit_target = serialization_properties.format_target(int(qubit))
ir_instructions.append(f"b[{idx}] = measure {qubit_target};")
ir_instructions.append(f"__bits__[{idx}] = measure {qubit_target};")

return OpenQasmProgram.construct(source="\n".join(ir_instructions), inputs={})

Expand All @@ -1214,11 +1214,11 @@ def _create_openqasm_header(
for parameter in self.parameters:
ir_instructions.append(f"input float {parameter};")
if not self.result_types:
ir_instructions.append(f"bit[{self.qubit_count}] b;")
ir_instructions.append(f"bit[{self.qubit_count}] __bits__;")

if serialization_properties.qubit_reference_type == QubitReferenceType.VIRTUAL:
total_qubits = max(self.qubits).real + 1
ir_instructions.append(f"qubit[{total_qubits}] q;")
ir_instructions.append(f"qubit[{total_qubits}] __qubits__;")
elif serialization_properties.qubit_reference_type != QubitReferenceType.PHYSICAL:
raise ValueError(
f"Invalid qubit_reference_type "
Expand Down
4 changes: 2 additions & 2 deletions src/braket/circuits/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class OpenQASMSerializationProperties:
Properties for serializing a circuit to OpenQASM.

qubit_reference_type (QubitReferenceType): determines whether to use
logical qubits or physical qubits (q[i] vs $i).
logical qubits or physical qubits (__qubits__[i] vs $i).
"""

qubit_reference_type: QubitReferenceType = QubitReferenceType.VIRTUAL
Expand All @@ -53,7 +53,7 @@ def format_target(self, target: int) -> str:
str: The OpenQASM representation of the target qubit.
"""
qubit_reference_format = (
"q[{}]" if self.qubit_reference_type == QubitReferenceType.VIRTUAL else "${}"
"__qubits__[{}]" if self.qubit_reference_type == QubitReferenceType.VIRTUAL else "${}"
)
return qubit_reference_format.format(target)

Expand Down
24 changes: 23 additions & 1 deletion src/braket/parametric/free_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from numbers import Number
from typing import Dict, Union
from unicodedata import category

from sympy import Symbol

Expand Down Expand Up @@ -43,12 +44,17 @@ def __init__(self, name: str):
Initializes a new :class:'FreeParameter' object.

Args:
name (str): Name of the :class:'FreeParameter'. Can be a unicode value.
name (str): Name of the :class:'FreeParameter'. Must begin with a letter [A-Za-z],
an underscore or an element from the Unicode character categories Lu/Ll/Lt/Lm/Lo/Nl.
Must not begin with two underscores '__'. May contain numbers [0-9] after the
first character.

Examples:
>>> param1 = FreeParameter("theta")
>>> param1 = FreeParameter("\u03B8")
>>> param1 = FreeParameter("a1b2")
"""
FreeParameter._validate_name(name)
self._name = Symbol(name)
super().__init__(expression=self._name)

Expand Down Expand Up @@ -99,6 +105,22 @@ def to_dict(self) -> dict:
"name": self.name,
}

@staticmethod
def _validate_name(name: str) -> None:
AbeCoull marked this conversation as resolved.
Show resolved Hide resolved
if not name:
raise ValueError("Symbol names must be non empty")
krneta marked this conversation as resolved.
Show resolved Hide resolved
if not isinstance(name, str):
raise TypeError("Symbol name must be a string")
if name.startswith("_"):
krneta marked this conversation as resolved.
Show resolved Hide resolved
if name.startswith("__"):
raise ValueError("Symbol names must not start with two underscores '__'")
else:
unicode_category = category(name[0])
if not unicode_category:
raise ValueError("Symbol names must start with a valid character")
if not unicode_category.startswith("L") and unicode_category != "Nl":
krneta marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError("Symbol names must start with a letter or underscore")

@classmethod
def from_dict(cls, parameter: dict) -> FreeParameter:
return FreeParameter(parameter["name"])
4 changes: 2 additions & 2 deletions src/braket/pulse/ast/qasm_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ def visit_ExpressionStatement(self, expression_statement: ast.ExpressionStatemen
):
# For capture_v0 nodes, it replaces it with classical assignment statements
# of the form:
# b[0] = capture_v0(...)
# b[1] = capture_v0(...)
# __bits__[0] = capture_v0(...)
# __bits__[1] = capture_v0(...)
new_val = ast.ClassicalAssignment(
# Ideally should use IndexedIdentifier here, but this works since it is just
# for printing.
Expand Down
32 changes: 17 additions & 15 deletions test/integ_tests/test_adjoint_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def test_adjoint_gradient_quantum_task_with_nested_targets(
expected_openqasm = (
"OPENQASM 3.0;\n"
"input float theta;\n"
"qubit[4] q;\n"
"rx(theta) q[0];\n"
"#pragma braket result adjoint_gradient expectation(-6 * y(q[0]) @ i(q[1]) + 0.75 * "
"y(q[2]) @ z(q[3])) theta"
"qubit[4] __qubits__;\n"
"rx(theta) __qubits__[0];\n"
"#pragma braket result adjoint_gradient expectation(-6 * "
"y(__qubits__[0]) @ i(__qubits__[1]) + 0.75 * y(__qubits__[2]) @ z(__qubits__[3])) theta"
)

gradient_task = AwsQuantumTask.create(
Expand Down Expand Up @@ -83,10 +83,10 @@ def test_adjoint_gradient_with_standard_observable_terms(
expected_openqasm = (
"OPENQASM 3.0;\n"
"input float theta;\n"
"qubit[3] q;\n"
"rx(theta) q[0];\n"
"#pragma braket result adjoint_gradient expectation(2 * x(q[0]) + 3 * y(q[1]) "
"- 1 * z(q[2])) theta"
"qubit[3] __qubits__;\n"
"rx(theta) __qubits__[0];\n"
"#pragma braket result adjoint_gradient expectation(2 * "
"x(__qubits__[0]) + 3 * y(__qubits__[1]) - 1 * z(__qubits__[2])) theta"
)

gradient_task = AwsQuantumTask.create(
Expand Down Expand Up @@ -132,17 +132,19 @@ def test_adjoint_gradient_with_batch_circuits(aws_session, s3_destination_folder
(
"OPENQASM 3.0;\n"
"input float theta;\n"
"qubit[2] q;\n"
"rx(theta) q[0];\n"
"#pragma braket result adjoint_gradient expectation(6 * y(q[0]) @ i(q[1])) theta"
"qubit[2] __qubits__;\n"
"rx(theta) __qubits__[0];\n"
"#pragma braket result adjoint_gradient expectation(6 *"
" y(__qubits__[0]) @ i(__qubits__[1])) theta"
),
(
"OPENQASM 3.0;\n"
"input float theta;\n"
"qubit[2] q;\n"
"rx(theta) q[0];\n"
"#pragma braket result adjoint_gradient expectation(-6 * y(q[0]) @ i(q[1]) + 0.75 * "
"y(q[0]) @ z(q[1])) theta"
"qubit[2] __qubits__;\n"
"rx(theta) __qubits__[0];\n"
"#pragma braket result adjoint_gradient expectation(-6 *"
" y(__qubits__[0]) @ i(__qubits__[1]) + 0.75 *"
" y(__qubits__[0]) @ z(__qubits__[1])) theta"
),
]

Expand Down
6 changes: 3 additions & 3 deletions test/unit_tests/braket/aws/test_aws_quantum_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,12 @@ def test_create_pulse_gate_circuit(
expected_openqasm = "\n".join(
(
"OPENQASM 3.0;",
"bit[2] b;",
"bit[2] __bits__;",
"cal {",
" set_frequency(predefined_frame_1, 6000000.0);",
"}",
"b[0] = measure $0;",
"b[1] = measure $1;",
"__bits__[0] = measure $0;",
"__bits__[1] = measure $1;",
)
)

Expand Down
Loading