Skip to content

Commit

Permalink
Merge branch 'main' into feature/autoqasm
Browse files Browse the repository at this point in the history
  • Loading branch information
rmshaffer authored Dec 15, 2023
2 parents 568bf9d + f44ab58 commit b6f7d2d
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Python
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: '3.9'
- name: Install dependencies
Expand Down
18 changes: 14 additions & 4 deletions .github/workflows/code-freeze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ jobs:
echo $BRANCH_NAME
echo $PR_TITLE
if [[ "$BRANCH_NAME" != $UNFROZEN_PREFIX* ]] &&
[[ "$PR_TITLE" != fix:* && "$PR_TITLE" != *"[critical]"* ]]; then
echo "Error: You can only merge from branches that start with '$UNFROZEN_PREFIX', or PRs titled with 'fix: ' and containing '[critical]'."
exit 1
# if it's not a critical fix
if ! [[ "$PR_TITLE" == fix\(critical\):* ]]; then
# and there's an unfrozen prefix
if ! [[ -z $UNFROZEN_PREFIX ]]; then
# check if the branch matches unfrozen prefix
if [[ "$BRANCH_NAME" != $UNFROZEN_PREFIX* ]]; then
echo "Error: You can only merge from branches that start with '$UNFROZEN_PREFIX', or PRs titled with prefix 'fix(critical): '."
exit 1
fi
# repo is fully frozen
else
echo "Error: You can only merge PRs titled with prefix 'fix(critical): '."
exit 1
fi
fi
2 changes: 1 addition & 1 deletion .github/workflows/dependent-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-to-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Python
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: '3.x'
- name: Install wheel
Expand All @@ -26,6 +26,6 @@ jobs:
- name: Build a binary wheel and a source tarball
run: python setup.py sdist bdist_wheel
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e # release/v1
uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf # release/v1
with:
password: ${{ secrets.pypi_token }}
5 changes: 2 additions & 3 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install --upgrade pip
pip install -e .[test]
pip install tox
- name: Run unit tests
run: |
tox -e unit-tests
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/twine-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Python
uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
with:
python-version: '3.x'
- name: Install wheel
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## v1.64.1 (2023-12-12)

### Bug Fixes and Other Changes

* make filter more convenient

## v1.64.0 (2023-12-07)

### Features

* add str, repr and getitem to BasisState

### Bug Fixes and Other Changes

* update: adding a test to check for circular imports

## v1.63.0 (2023-12-05)

### Features
Expand Down
7 changes: 6 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ test=pytest
xfail_strict = true
# https://pytest-xdist.readthedocs.io/en/latest/known-limitations.html
addopts =
--verbose -n auto --durations=0 --durations-min=1
--verbose -n logical --durations=0 --durations-min=1
testpaths = test/unit_tests
filterwarnings=
# Issue #557 in `pytest-cov` (currently v4.x) has not moved for a while now,
# but once a resolution has been adopted we can drop this "ignore".
# Ref: https://github.com/pytest-dev/pytest-cov/issues/557
ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning

[isort]
line_length = 100
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"pytest",
"pytest-cov",
"pytest-rerunfailures",
"pytest-xdist",
"pytest-xdist[psutil]",
"sphinx",
"sphinx-rtd-theme",
"sphinxcontrib-apidoc",
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.63.1.dev0"
__version__ = "1.64.2.dev0"
12 changes: 12 additions & 0 deletions src/braket/circuits/basis_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ def __iter__(self):
def __eq__(self, other):
return self.state == other.state

def __bool__(self):
return any(self.state)

def __str__(self):
return self.as_string

def __repr__(self):
return f'BasisState("{self.as_string}")'

def __getitem__(self, item):
return BasisState(self.state[item])


BasisStateInput = Union[int, list[int], str, BasisState]

Expand Down
25 changes: 15 additions & 10 deletions src/braket/circuits/gate_calibrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from __future__ import annotations

from copy import deepcopy
from typing import Any, Optional
from typing import Any

from braket.circuits.gate import Gate
from braket.circuits.serialization import (
Expand Down Expand Up @@ -91,35 +91,40 @@ def __len__(self):
return len(self._pulse_sequences)

def filter(
self, gates: Optional[list[Gate]] = None, qubits: Optional[QubitSet] = None
) -> Optional[GateCalibrations]:
self,
gates: list[Gate] | None = None,
qubits: QubitSet | list[QubitSet] | None = None,
) -> GateCalibrations:
"""
Filters the data based on optional lists of gates and QubitSets.
Args:
gates (Optional[list[Gate]]): An optional list of gates to filter on.
qubits (Optional[QubitSet]): An optional `QubitSet` to filter on.
gates (list[Gate] | None): An optional list of gates to filter on.
qubits (QubitSet | list[QubitSet] | None): An optional `QubitSet` or
list of `QubitSet` to filter on.
Returns:
Optional[GateCalibrations]: A filtered GateCalibrations object. Otherwise, returns
none if no matches are found.
GateCalibrations: A filtered GateCalibrations object.
""" # noqa: E501
keys = self.pulse_sequences.keys()
if isinstance(qubits, QubitSet):
qubits = [qubits]
filtered_calibration_keys = [
tup
for tup in keys
if (gates is None or tup[0] in gates) and (qubits is None or qubits.issubset(tup[1]))
if (gates is None or tup[0] in gates)
and (qubits is None or any(qset.issubset(tup[1]) for qset in qubits))
]
return GateCalibrations(
{k: v for (k, v) in self.pulse_sequences.items() if k in filtered_calibration_keys},
)

def to_ir(self, calibration_key: Optional[tuple[Gate, QubitSet]] = None) -> str:
def to_ir(self, calibration_key: tuple[Gate, QubitSet] | None = None) -> str:
"""
Returns the defcal representation for the `GateCalibrations` object.
Args:
calibration_key (Optional[tuple[Gate, QubitSet]]): An optional key to get a specific defcal.
calibration_key (tuple[Gate, QubitSet] | None): An optional key to get a specific defcal.
Default: None
Returns:
Expand Down
58 changes: 55 additions & 3 deletions test/unit_tests/braket/circuits/test_basis_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,58 @@
),
)
def test_as_props(basis_state_input, size, as_tuple, as_int, as_string):
assert BasisState(basis_state_input, size).as_tuple == as_tuple
assert BasisState(basis_state_input, size).as_int == as_int
assert BasisState(basis_state_input, size).as_string == as_string
basis_state = BasisState(basis_state_input, size)
assert basis_state.as_tuple == as_tuple
assert basis_state.as_int == as_int
assert basis_state.as_string == as_string == str(basis_state)
assert repr(basis_state) == f'BasisState("{as_string}")'


@pytest.mark.parametrize(
"basis_state_input, index, substate_input",
(
(
"1001",
slice(None),
"1001",
),
(
"1001",
3,
"1",
),
(
"1010",
slice(None, None, 2),
"11",
),
(
"1010",
slice(1, None, 2),
"00",
),
(
"1010",
slice(None, -2),
"10",
),
(
"1010",
-1,
"0",
),
),
)
def test_indexing(basis_state_input, index, substate_input):
assert BasisState(basis_state_input)[index] == BasisState(substate_input)


def test_bool():
assert all(
[
BasisState("100"),
BasisState("111"),
BasisState("1"),
]
)
assert not BasisState("0")
19 changes: 15 additions & 4 deletions test/unit_tests/braket/circuits/test_gate_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,30 @@ def test_gc_copy(pulse_sequence):


def test_filter(pulse_sequence):
calibration_key = (Gate.Z(), QubitSet([0, 1]))
calibration_key_2 = (Gate.H(), QubitSet([0, 1]))
calibration_key = (Gate.Z(), QubitSet([0]))
calibration_key_2 = (Gate.H(), QubitSet([1]))
calibration_key_3 = (Gate.CZ(), QubitSet([0, 1]))
calibration = GateCalibrations(
{calibration_key: pulse_sequence, calibration_key_2: pulse_sequence}
{
calibration_key: pulse_sequence,
calibration_key_2: pulse_sequence,
calibration_key_3: pulse_sequence,
}
)
expected_calibration_1 = GateCalibrations({calibration_key: pulse_sequence})
expected_calibration_2 = GateCalibrations(
{calibration_key: pulse_sequence, calibration_key_2: pulse_sequence}
{calibration_key: pulse_sequence, calibration_key_3: pulse_sequence}
)
expected_calibration_3 = GateCalibrations({calibration_key_2: pulse_sequence})
expected_calibration_4 = GateCalibrations({})
expected_calibration_5 = calibration
expected_calibration_6 = GateCalibrations({calibration_key_3: pulse_sequence})
assert expected_calibration_1 == calibration.filter(gates=[Gate.Z()])
assert expected_calibration_2 == calibration.filter(qubits=QubitSet(0))
assert expected_calibration_3 == calibration.filter(gates=[Gate.H()], qubits=QubitSet(1))
assert expected_calibration_4 == calibration.filter(gates=[Gate.Z()], qubits=QubitSet(1))
assert expected_calibration_5 == calibration.filter(qubits=[QubitSet(0), QubitSet(1)])
assert expected_calibration_6 == calibration.filter(qubits=QubitSet([0, 1]))


def test_to_ir(pulse_sequence):
Expand Down
48 changes: 48 additions & 0 deletions test/unit_tests/braket/test_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import importlib
import multiprocessing
import os
import pathlib

import pytest


def test_for_import_cycles():
# Note, because of all the multiprocessing in this test, when running 'tox', the process
# threads may be made to wait as other tests are running in parallel, making it seems like
# this test is much slower than it actually is. However, splitting the test into a
# parameterized version wasn't able to correctly detect some circular imports when running tox.
modules = get_modules_to_test()
processes = []
multiprocessing.set_start_method("spawn")
for module in modules:
# We create a separate process to make sure the imports do not interfere with each-other.
process = multiprocessing.Process(target=import_module, args=(module,))
processes.append(process)
process.start()

for index, process in enumerate(processes):
process.join()
if process.exitcode != 0:
pytest.fail(
f"Unable to import '{modules[index]}'."
" If all other tests are passing, check for cyclical dependencies."
)


def get_modules_to_test():
curr_path = pathlib.Path(__file__).resolve()
while "test" in str(curr_path):
curr_path = curr_path.parent
curr_path = curr_path.joinpath("src")
curr_path_len = len(str(curr_path)) + len(os.sep)
modules = []
for dir_, temp, files in os.walk(curr_path):
# Rather than testing every single python file we just test modules, for now.
if "__init__.py" in files:
braket_module = dir_[curr_path_len:].replace(os.sep, ".")
modules.append(braket_module)
return modules


def import_module(module):
importlib.import_module(module)

0 comments on commit b6f7d2d

Please sign in to comment.