Skip to content

Commit

Permalink
Merge branch 'main' into phaserx
Browse files Browse the repository at this point in the history
  • Loading branch information
AbeCoull authored Apr 9, 2024
2 parents 16aad8d + 3ea6899 commit 486ca00
Show file tree
Hide file tree
Showing 23 changed files with 1,039 additions and 81 deletions.
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
# Changelog

## v1.76.3 (2024-04-09)

### Bug Fixes and Other Changes

* Replace pkg_resources with importlib.metadata

### Documentation Changes

* Improve gphase unitary matrix definition in docstring

## v1.76.2 (2024-04-08)

### Bug Fixes and Other Changes

* backwards compatiblity for local detuning

## v1.76.1 (2024-04-08)

### Bug Fixes and Other Changes

* Support single-register measurements in `from_ir`
* prevent repeated measurements on a qubit

## v1.76.0 (2024-04-01)

### Features

* add support for OpenQASM measure on a subset of qubits

### Bug Fixes and Other Changes

* restore the dependent test back to pennylane

### Documentation Changes

* fix GPI2 gate matrix representation

## v1.75.0 (2024-03-28)

### Features

* upgrade to pydantic 2.x

### Bug Fixes and Other Changes

* change schemas constraint

## v1.74.1 (2024-03-27)

### Bug Fixes and Other Changes

* temporarily pin the schemas version

## v1.74.0 (2024-03-21)

### Features

* Allow sets of calibrations in batches

### Bug Fixes and Other Changes

* batch tasking passing lists to single tasks

## v1.73.3 (2024-03-18)

### Bug Fixes and Other Changes
Expand Down
5 changes: 2 additions & 3 deletions doc/conf.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Sphinx configuration."""

import datetime

import pkg_resources
from importlib.metadata import version

# Sphinx configuration below.
project = "amazon-braket-sdk"
version = pkg_resources.require(project)[0].version
version = version(project)
release = version
copyright = "{}, Amazon.com".format(datetime.datetime.now().year)

Expand Down
Binary file added model.tar.gz
Binary file not shown.
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@
packages=find_namespace_packages(where="src", exclude=("test",)),
package_dir={"": "src"},
install_requires=[
"amazon-braket-schemas>=1.20.2",
"amazon-braket-default-simulator>=1.19.1",
"amazon-braket-schemas>=1.21.0",
"amazon-braket-default-simulator>=1.21.2",
"oqpy~=0.3.5",
"setuptools",
"backoff",
"boltons",
"boto3>=1.28.53",
Expand All @@ -41,6 +40,7 @@
"openpulse",
"openqasm3",
"sympy",
"backports.entry-points-selectable",
],
extras_require={
"test": [
Expand Down
2 changes: 1 addition & 1 deletion src/braket/_sdk/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "1.73.4.dev0"
__version__ = "1.76.4.dev0"
2 changes: 1 addition & 1 deletion src/braket/aws/aws_quantum_task_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def _tasks_inputs_gatedefs(
batch_length = arg_length

for i, arg_length in enumerate(arg_lengths):
if arg_length == 1:
if isinstance(args[i], (dict, single_task_type)):
args[i] = repeat(args[i], batch_length)

tasks_inputs_definitions = list(zip(*args))
Expand Down
11 changes: 11 additions & 0 deletions src/braket/circuits/braket_program_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

from braket.circuits import Circuit, Instruction
from braket.circuits.gates import Unitary
from braket.circuits.measure import Measure
from braket.circuits.noises import Kraus
from braket.circuits.translations import (
BRAKET_GATES,
Expand Down Expand Up @@ -159,3 +160,13 @@ def handle_parameter_value(
return evaluated_value
return FreeParameterExpression(evaluated_value)
return value

def add_measure(self, target: tuple[int]) -> None:
"""Add a measure instruction to the circuit
Args:
target (tuple[int]): the target qubits to be measured.
"""
for index, qubit in enumerate(target):
instruction = Instruction(Measure(index=index), qubit)
self._circuit.add_instruction(instruction)
161 changes: 159 additions & 2 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from __future__ import annotations

from collections import Counter
from collections.abc import Callable, Iterable
from numbers import Number
from typing import Any, Optional, TypeVar, Union
Expand All @@ -26,6 +27,7 @@
from braket.circuits.free_parameter_expression import FreeParameterExpression
from braket.circuits.gate import Gate
from braket.circuits.instruction import Instruction
from braket.circuits.measure import Measure
from braket.circuits.moments import Moments, MomentType
from braket.circuits.noise import Noise
from braket.circuits.noise_helpers import (
Expand Down Expand Up @@ -148,6 +150,7 @@ def __init__(self, addable: AddableTypes | None = None, *args, **kwargs):
self._parameters = set()
self._observables_simultaneously_measurable = True
self._has_compiler_directives = False
self._measure_targets = None

if addable is not None:
self.add(addable, *args, **kwargs)
Expand Down Expand Up @@ -273,6 +276,7 @@ def add_result_type(
Raises:
TypeError: If both `target_mapping` and `target` are supplied.
ValueError: If a meaure instruction exists on the current circuit.
Examples:
>>> result_type = ResultType.Probability(target=[0, 1])
Expand All @@ -298,6 +302,12 @@ def add_result_type(
if target_mapping and target is not None:
raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.")

if self._measure_targets:
raise ValueError(
"cannot add a result type to a circuit which already contains a "
"measure instruction."
)

if not target_mapping and not target:
# Nothing has been supplied, add result_type
result_type_to_add = result_type
Expand Down Expand Up @@ -407,6 +417,46 @@ def _add_to_qubit_observable_set(self, result_type: ResultType) -> None:
if isinstance(result_type, ObservableResultType) and result_type.target:
self._qubit_observable_set.update(result_type.target)

def _check_if_qubit_measured(
self,
instruction: Instruction,
target: QubitSetInput | None = None,
target_mapping: dict[QubitInput, QubitInput] | None = None,
) -> None:
"""Checks if the target qubits are measured. If the qubit is already measured
the instruction will not be added to the Circuit.
Args:
instruction (Instruction): `Instruction` to add into `self`.
target (QubitSetInput | None): Target qubits for the
`instruction`. If a single qubit gate, an instruction is created for every index
in `target`.
Default = `None`.
target_mapping (dict[QubitInput, QubitInput] | None): A dictionary of
qubit mappings to apply to the `instruction.target`. Key is the qubit in
`instruction.target` and the value is what the key will be changed to.
Default = `None`.
Raises:
ValueError: If adding a gate or noise operation after a measure instruction.
"""
if (
# check if there is a measure instruction on the target qubit
target
and target in self._measure_targets
# check if there is a measure instruction on any qubits in the target_mapping
or (target_mapping and any(targ in self._measure_targets for targ in target_mapping))
# If no target or target_mapping is supplied, check if there is a measure
# instruction on the current instructions target qubit
or (
instruction.target
and any(targ in self._measure_targets for targ in instruction.target)
)
):
raise ValueError(
"cannot add a gate or noise operation on a qubit after a measure instruction."
)

def add_instruction(
self,
instruction: Instruction,
Expand All @@ -431,6 +481,7 @@ def add_instruction(
Raises:
TypeError: If both `target_mapping` and `target` are supplied.
ValueError: If adding a gate or noise after a measure instruction.
Examples:
>>> instr = Instruction(Gate.CNot(), [0, 1])
Expand Down Expand Up @@ -458,6 +509,10 @@ def add_instruction(
if target_mapping and target is not None:
raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.")

# Check if there is a measure instruction on the circuit
if not isinstance(instruction.operator, Measure) and self._measure_targets:
self._check_if_qubit_measured(instruction, target, target_mapping)

if not target_mapping and not target:
# Nothing has been supplied, add instruction
instructions_to_add = [instruction]
Expand Down Expand Up @@ -635,6 +690,9 @@ def add_verbatim_box(
if verbatim_circuit.result_types:
raise ValueError("Verbatim subcircuit is not measured and cannot have result types")

if verbatim_circuit._measure_targets:
raise ValueError("cannot measure a subcircuit inside a verbatim box.")

if verbatim_circuit.instructions:
self.add_instruction(Instruction(compiler_directives.StartVerbatimBox()))
for instruction in verbatim_circuit.instructions:
Expand All @@ -643,6 +701,99 @@ def add_verbatim_box(
self._has_compiler_directives = True
return self

def _add_measure(self, target_qubits: QubitSetInput) -> None:
"""Adds a measure instruction to the the circuit
Args:
target_qubits (QubitSetInput): target qubits to measure.
"""
for idx, target in enumerate(target_qubits):
num_qubits_measured = (
len(self._measure_targets)
if self._measure_targets and len(target_qubits) == 1
else 0
)
self.add_instruction(
Instruction(
operator=Measure(index=idx + num_qubits_measured),
target=target,
)
)
if self._measure_targets:
self._measure_targets.append(target)
else:
self._measure_targets = [target]

def measure(self, target_qubits: QubitSetInput | None = None) -> Circuit:
"""
Add a `measure` operator to `self` ensuring only the target qubits are measured.
Args:
target_qubits (QubitSetInput | None): target qubits to measure.
Default=None
Returns:
Circuit: self
Raises:
IndexError: If `self` has no qubits.
IndexError: If target qubits are not within the range of the current circuit.
ValueError: If the current circuit contains any result types.
ValueError: If the target qubit is already measured.
Examples:
>>> circ = Circuit.h(0).cnot(0, 1).measure([0])
>>> circ.print(list(circ.instructions))
[Instruction('operator': H('qubit_count': 1), 'target': QubitSet([Qubit(0)]),
Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(0),
Qubit(1)]),
Instruction('operator': H('qubit_count': 1), 'target': QubitSet([Qubit(2)]),
Instruction('operator': Measure, 'target': QubitSet([Qubit(0)])]
"""
# check whether measuring an empty circuit
if not self.qubits:
raise IndexError("cannot measure an empty circuit.")

if isinstance(target_qubits, int):
target_qubits = [target_qubits]

# Check if result types are added on the circuit
if self.result_types:
raise ValueError("a circuit cannot contain both measure instructions and result types.")

if target_qubits:
# Check if the target_qubits are already measured
if self._measure_targets and any(
target in self._measure_targets for target in target_qubits
):
intersection = set(target_qubits) & set(self._measure_targets)
raise ValueError(
f"cannot measure the same qubit(s) {', '.join(map(str, intersection))} "
"more than once."
)
# Check if there are repeated qubits in the same measurement
if len(target_qubits) != len(set(target_qubits)):
intersection = [
qubit for qubit, count in Counter(target_qubits).items() if count > 1
]
raise ValueError(
f"cannot repeat qubit(s) {', '.join(map(str, intersection))} "
"in the same measurement."
)
self._add_measure(target_qubits=target_qubits)
else:
# Check if any qubits are already measured
if self._measure_targets:
intersection = set(self.qubits) & set(self._measure_targets)
raise ValueError(
f"cannot measure the same qubit(s) {', '.join(map(str, intersection))} "
"more than once."
)
# Measure all the qubits
self._add_measure(target_qubits=self.qubits)

return self

def apply_gate_noise(
self,
noise: Union[type[Noise], Iterable[type[Noise]]],
Expand Down Expand Up @@ -1208,7 +1359,8 @@ def _to_openqasm(
for result_type in self.result_types
]
)
else:
# measure all the qubits if a measure instruction is not provided
elif self._measure_targets is None:
qubits = (
sorted(self.qubits)
if serialization_properties.qubit_reference_type == QubitReferenceType.VIRTUAL
Expand All @@ -1230,7 +1382,12 @@ 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;")
bit_count = (
len(self._measure_targets)
if self._measure_targets is not None
else self.qubit_count
)
ir_instructions.append(f"bit[{bit_count}] b;")

if serialization_properties.qubit_reference_type == QubitReferenceType.VIRTUAL:
total_qubits = max(self.qubits).real + 1
Expand Down
Loading

0 comments on commit 486ca00

Please sign in to comment.