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

Add samples of testing Q# code that prepares a quantum state #1873

Merged
merged 3 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions samples/testing/states/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Testing Quantum States

This sample project demonstrates testing Q# code that has to end up with a certain quantum state.

## Testing Method

The most convenient way to validate the quantum state of the program in Q# is using `dump_machine` Python API.

1. **Check that the amplitudes match the dense array of expected amplitudes:**
Use the `as_dense_state()` method of `StateDump` class to convert it to an array of amplitudes and compare it with the expected one.

2. **Check that the state matches the expected one up to a global phase:**
Use the `check_eq()` method of `StateDump` class to compare it to the given array of amplitudes, taking into account the possible global phase difference.


## Project Structure

This sample project is a multi-file Q# project that showcases both testing methods. The project structure is as follows:

- src
- `StatePrep.qs`: Q# file containing the state preparation operations to be tested.
- `qsharp.json`: Q# project manifest file, instructing the compiler to include all files in `src` directory.
- `test_states.py`: Python wrapper containing tests.
1 change: 1 addition & 0 deletions samples/testing/states/qsharp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
18 changes: 18 additions & 0 deletions samples/testing/states/src/StatePrep.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// # Summary
/// Prepares the state 1/2 (|00⟩ + i|01⟩ - |10⟩ - i|11⟩) = |-⟩ ⊗ |i⟩.
operation PrepareStateWithComplexPhases(qs : Qubit[]) : Unit {
H(qs[0]);
Z(qs[0]);
H(qs[1]);
S(qs[1]);
}

/// # Summary
/// Prepares the state 1/2 (-|00⟩ - i|01⟩ + |10⟩ + i|11⟩) = -(|-⟩ ⊗ |i⟩) = -|-⟩ ⊗ |i⟩.
operation PrepareStateWithGlobalPhase(qs : Qubit[]) : Unit {
H(qs[0]);
Z(qs[0]);
X(qs[0]);
H(qs[1]);
S(qs[1]);
}
42 changes: 42 additions & 0 deletions samples/testing/states/test_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import pytest
import qsharp

@pytest.fixture(autouse=True)
def setup():
"""Fixture to execute before a test is run"""
# Setting the project root to current folder.
qsharp.init(project_root=".")
yield # this is where the testing happens

# 1/2 (|00⟩ + i|01⟩ - |10⟩ - i|11⟩)
# The basis states are converted to indices of amplitudes in the array using big endian notation.
expected_state = [0.5, 0.5j, -0.5, -0.5j]

def test_state_exact() -> None:
"""Test that Q# code prepares the expected state exactly using Python test code."""
# Run Q# code that allocates the qubits and prepares the state but doesn't deallocate the qubits.
qsharp.eval(f"use qs = Qubit[2]; StatePrep.PrepareStateWithComplexPhases(qs);")
# Get the state of the allocated qubits and convert it to a dense vector.
state = qsharp.dump_machine().as_dense_state()
# Compare two vectors.
assert state == pytest.approx(expected_state)
Fixed Show fixed Hide fixed


def test_state_exact_rejects_global_phase() -> None:
"""Test that shows that the exact check from the previous test fails if the state is different by a global phase."""
# Run Q# code that allocates the qubits and prepares the state but doesn't deallocate the qubits.
qsharp.eval(f"use qs = Qubit[2]; StatePrep.PrepareStateWithGlobalPhase(qs);")
# Get the state of the allocated qubits and convert it to a dense vector.
state = qsharp.dump_machine().as_dense_state()
# Compare two vectors. Here we expect them to be _not equal_ due to the global phase -1.
assert state != pytest.approx(expected_state)


def test_state_global_phase() -> None:
"""Test that Q# code prepares the expected state up to a global phase using Python test code."""
# Run Q# code that allocates the qubits and prepares the state but doesn't deallocate the qubits.
qsharp.eval(f"use qs = Qubit[2]; StatePrep.PrepareStateWithGlobalPhase(qs);")
# Get the state of the allocated qubits.
state = qsharp.dump_machine()
Fixed Show fixed Hide fixed
Comment on lines +18 to +40

Check notice

Code scanning / devskim

If untrusted data (data from HTTP requests, user submitted files, etc.) is included in an eval statement it can allow an attacker to inject their own code. Note test

Review eval for untrusted data
# Compare the state to the expected one, taking into account the possible global phase difference.
assert state.check_eq(expected_state)
Loading