Skip to content

Commit

Permalink
feature: Always accept identity observable factors (#178)
Browse files Browse the repository at this point in the history
This enables simultaneous measurement of qubit-wise commuting Pauli words.
  • Loading branch information
speller26 authored Dec 4, 2020
1 parent b67507a commit 92aaafa
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 37 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,29 @@ To view the generated documentation, open the following file in a browser:
This repository has both unit and integration tests.

To run the tests, make sure to install test dependencies first:

```bash
pip install -e "amazon-braket-sdk-python[test]"
```

### Unit Tests

To run the unit tests:

```bash
tox -e unit-tests
```

You can also pass in various pytest arguments `tox -e unit-tests -- your-arguments` to run selected tests. For more information, please see [pytest usage](https://docs.pytest.org/en/stable/usage.html).
You can also pass in various pytest arguments to run selected tests:

```bash
tox -e unit-tests -- your-arguments
```

For more information, please see [pytest usage](https://docs.pytest.org/en/stable/usage.html).

To run linters and doc generators and unit tests:

To run linters and doc generators and unit tests
```bash
tox
```
Expand All @@ -186,12 +196,17 @@ After you create a profile, use the following command to set the `AWS_PROFILE` s
export AWS_PROFILE=YOUR_PROFILE_NAME
```

Run the tests
Run the tests:

```bash
tox -e integ-tests
```

You can also pass in various pytest arguments `tox -e integ-tests -- your-arguments` to run selected tests. For more information, please see [pytest usage](https://docs.pytest.org/en/stable/usage.html).
As with unit tests, you can also pass in various pytest arguments:

```bash
tox -e integ-tests -- your-arguments
```

## License
This project is licensed under the Apache-2.0 License.
61 changes: 39 additions & 22 deletions src/braket/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,9 @@ def _add_to_qubit_observable_mapping(self, result_type: ResultType) -> None:
observable = result_type.observable
else:
return

targets = result_type.target or list(self._qubit_observable_set)
all_qubits_observable = self._qubit_observable_mapping.get(Circuit._ALL_QUBITS)

for i in range(len(targets)):
target = targets[i]
tensor_product_dict = (
Expand All @@ -268,34 +268,32 @@ def _add_to_qubit_observable_mapping(self, result_type: ResultType) -> None:
)
new_observable = tensor_product_dict[i][0] if tensor_product_dict else observable
current_observable = all_qubits_observable or self._qubit_observable_mapping.get(target)
if current_observable and current_observable != new_observable:
raise ValueError(
f"Existing result type for observable {current_observable} for target {target}"
f" conflicts with observable {new_observable} for new result type"
)

add_observable = Circuit._validate_observable_to_add_for_qubit(
current_observable, new_observable, target
)

if result_type.target:
if new_observable.qubit_count > 1:
new_targets = (
tuple(
result_type.target[
tensor_product_dict[i][1][0] : tensor_product_dict[i][1][1]
]
)
if tensor_product_dict
else tuple(result_type.target)
new_targets = (
tuple(
result_type.target[
tensor_product_dict[i][1][0] : tensor_product_dict[i][1][1]
]
)
if tensor_product_dict
else tuple(result_type.target)
)
if add_observable:
self._qubit_target_mapping[target] = new_targets
self._qubit_observable_mapping[target] = new_observable
elif new_observable.qubit_count > 1 and new_observable != Observable.I():
current_target = self._qubit_target_mapping.get(target)
if current_target and current_target != new_targets:
raise ValueError(
f"Target order {current_target} of existing result type with observable"
f" {current_observable} conflicts with order {targets} of new"
" result type"
f"Target order {current_target} of existing result type with"
f" observable {current_observable} conflicts with order {targets}"
" of new result type"
)
self._qubit_target_mapping[target] = new_targets
else:
self._qubit_target_mapping[target] = tuple([target])
self._qubit_observable_mapping[target] = new_observable

if not result_type.target:
if all_qubits_observable and all_qubits_observable != observable:
Expand All @@ -305,6 +303,25 @@ def _add_to_qubit_observable_mapping(self, result_type: ResultType) -> None:
)
self._qubit_observable_mapping[Circuit._ALL_QUBITS] = observable

@staticmethod
def _validate_observable_to_add_for_qubit(current_observable, new_observable, target):
identity = Observable.I()
add_observable = False
if not current_observable or (
current_observable == identity and new_observable != identity
):
add_observable = True
elif (
current_observable != identity
and new_observable != identity
and current_observable != new_observable
):
raise ValueError(
f"Observable {new_observable} specified for target {target} conflicts with"
+ f" existing observable {current_observable} on this target."
)
return add_observable

@staticmethod
def _tensor_product_index_dict(observable: TensorProduct) -> Dict[int, Observable]:
obj_dict = {}
Expand Down
32 changes: 31 additions & 1 deletion test/unit_tests/braket/circuits/test_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def test_basis_rotation_instructions_tensor_product():
assert circ.basis_rotation_instructions == expected


def test_basis_rotation_instructions_tensor_product_commuting():
def test_basis_rotation_instructions_tensor_product_shared_factors():
circ = (
Circuit()
.h(0)
Expand All @@ -505,6 +505,34 @@ def test_basis_rotation_instructions_tensor_product_commuting():
assert circ.basis_rotation_instructions == expected


def test_basis_rotation_instructions_identity():
circ = (
Circuit()
.h(0)
.cnot(0, 1)
.cnot(1, 2)
.cnot(2, 3)
.cnot(3, 4)
.expectation(observable=Observable.X(), target=[0])
.expectation(observable=Observable.I(), target=[2])
.expectation(observable=Observable.I() @ Observable.Y(), target=[1, 3])
.expectation(observable=Observable.I(), target=[0])
.expectation(observable=Observable.X() @ Observable.I(), target=[1, 3])
.expectation(observable=Observable.Y(), target=[2])
)
expected = [
Instruction(Gate.H(), 0),
Instruction(Gate.H(), 1),
Instruction(Gate.Z(), 2),
Instruction(Gate.S(), 2),
Instruction(Gate.H(), 2),
Instruction(Gate.Z(), 3),
Instruction(Gate.S(), 3),
Instruction(Gate.H(), 3),
]
assert circ.basis_rotation_instructions == expected


def test_basis_rotation_instructions_multiple_result_types_different_targets():
circ = (
Circuit()
Expand Down Expand Up @@ -623,10 +651,12 @@ def test_basis_rotation_instructions_multiple_result_types_tensor_product_hermit
.h(0)
.cnot(0, 1)
.cnot(1, 2)
.expectation(observable=Observable.I(), target=[1])
.sample(
observable=Observable.Hermitian(matrix=np.eye(4)) @ Observable.H(), target=[0, 1, 2]
)
.variance(observable=Observable.H(), target=[2])
.expectation(observable=Observable.I(), target=[0])
)
expected = [
Instruction(Gate.Unitary(matrix=np.eye(4)), target=[0, 1]),
Expand Down
19 changes: 9 additions & 10 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
envlist = linters,docs,unit-tests

[testenv:unit-tests]
basepython = python3.7
whitelist_externals = coverage
basepython = python3
# {posargs} contains additional arguments specified when invoking tox. e.g. tox -- -s -k test_foo.py
commands =
coverage run -m pytest {posargs}
Expand All @@ -13,7 +12,7 @@ commands =
extras = test

[testenv:integ-tests]
basepython = python3.7
basepython = python3
# {posargs} contains additional arguments specified when invoking tox. e.g. tox -- -s -k test_foo.py
passenv =
AWS_PROFILE
Expand All @@ -22,7 +21,7 @@ commands =
extras = test

[testenv:linters]
basepython = python3.7
basepython = python3
skip_install = true
deps =
{[testenv:isort]deps}
Expand All @@ -35,31 +34,31 @@ commands =
{[testenv:flake8]commands}

[testenv:flake8]
basepython = python3.7
basepython = python3
skip_install = true
deps =
flake8
commands =
flake8 {posargs}

[testenv:isort]
basepython = python3.7
basepython = python3
skip_install = true
deps =
isort
commands =
isort -rc . {posargs}

[testenv:black]
basepython = python3.7
basepython = python3
skip_install = true
deps =
black
commands =
black ./ {posargs}

[testenv:docs]
basepython = python3.7
basepython = python3
deps =
sphinx
sphinx-rtd-theme
Expand All @@ -68,14 +67,14 @@ commands =
sphinx-build -E -T -b html doc build/documentation/html

[testenv:serve-docs]
basepython = python3.7
basepython = python3
skip_install = true
changedir = build/documentation/html
commands =
python -m http.server {posargs}

[testenv:zip-build]
basepython = python3.7
basepython = python3
skip_install = true
commands =
/bin/sh -c 'tar -czvf build_files.tar.gz build/'

0 comments on commit 92aaafa

Please sign in to comment.