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 with classical return values #1858

Merged
merged 2 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 15 additions & 0 deletions samples/testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Testing Q# Code

The samples in this folder demonstrate different approaches to testing Q# code using AQDK built-in tools.

## Installation
- Install the `qsharp` Python package by following the instructions mentioned [here](https://learn.microsoft.com/azure/quantum/install-overview-qdk#add-support-for-python-and-jupyter-notebooks).
- Install `pytest` Python package.

## Running the samples
Open the directory containing the test (for example, `samples/testing/operations`), and run `pytest` command.

## Reference Links:
- [Q# Testing guide](https://learn.microsoft.com/azure/quantum/user-guide/testing-debugging).
- [Getting started with the QDK](https://learn.microsoft.com/azure/quantum/install-overview-qdk).
- [Getting started with Pytest](https://docs.pytest.org/en/stable/getting-started.html).
23 changes: 23 additions & 0 deletions samples/testing/classical_values/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Testing Classical Return Values

This sample project demonstrates testing Q# code that returns classical values.

## Testing Methods

There are two primary ways to test classical values in Q#:

1. **Return them to Python and run checks in Python:**
Use the `eval` Python API to get the results of Q# code and check that they are as expected.

2. **Q# `Fact` Assertions:**
Use a `Fact` function in your Q# code that checks whether the classical value within it are correct. The `Fact` function asserts that the check returns `true`.

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

- src
- `ClassicalFunction.qs`: Q# file containing the classical function to be tested
- `Measurement.qs`: Q# file containing the operation with `Result[]` return type to be tested
- `TestCode.qs`: Q# file containing the test logic for the first two files to be called in Python wrapper
- `qsharp.json`: Q# project manifest file, instructing compiler to include all files in `src` directory.
- `test_classical_values.py`: Python wrapper containing tests.
1 change: 1 addition & 0 deletions samples/testing/classical_values/qsharp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions samples/testing/classical_values/src/ClassicalFunction.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// # Summary
/// Returns the square of the input integer.
function Square(x : Int) : Int {
x * x
}
7 changes: 7 additions & 0 deletions samples/testing/classical_values/src/Measurement.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/// # Summary
/// Prepares the qubits in the given basis state, measures them, and returns the measurement result.
operation MeasureBasisState(bits : Bool[]) : Result[] {
use qs = Qubit[Length(bits)];
ApplyPauliFromBitString(PauliX, true, bits, qs);
return MResetEachZ(qs);
}
27 changes: 27 additions & 0 deletions samples/testing/classical_values/src/TestCode.qs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Std.Diagnostics.Fact;
open Std.Arrays;
open Std.Random;
import ClassicalFunction.Square;
import Measurement.MeasureBasisState;

/// # Summary
/// Test code that verifies the classical values returned by the rest of the code.
/// Throw exceptions if the test fails.

function TestSquare() : Unit {
for i in -10..10 {
let (actual, expected) = (Square(i), i * i);
Fact(actual == expected, $"Incorrect function value for {i}: expected {expected}, got {actual}");
}
}

operation TestMeasurement() : Unit {
for _ in 1..10 {
let n = DrawRandomInt(2, 10);
let bits = ForEach(x => DrawRandomBool(0.5), [0, size = n]);
let res = MeasureBasisState(bits);
for (bit, resBit) in Zipped(bits, res) {
Fact(bit == (resBit == One), $"Incorrect measurement result for {bit}");
}
}
}
39 changes: 39 additions & 0 deletions samples/testing/classical_values/test_classical_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from random import randint
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


def test_classical_computation() -> None:
"""Test that Q# code computes f(x) = x^2 correctly using Python test code."""
for x in range(-10, 11):
res = qsharp.eval(f"ClassicalFunction.Square({x})")
assert res == x ** 2


def test_classical_computation_qsharp() -> None:
"""Test that Q# code computes f(x) = x^2 correctly using Q# test code."""
qsharp.eval("TestCode.TestSquare()")


def test_measurement_results() -> None:
"""Test that measuring a basis state returns correct measurement results using Python test code."""
for _ in range(10):
n = randint(2, 10)
bits = [bool(randint(0, 1)) for _ in range(n)]
# When passing Boolean values to Q#, remember to convert them to lowercase
# (Python uses True and False, while Q# uses true and false)
res = qsharp.eval(f"Measurement.MeasureBasisState({str(bits).lower()})")
for i in range(n):
assert (res[i] == qsharp.Result.One) == bits[i]


def test_measurement_results_qsharp() -> None:
"""Test that measuring a basis state returns correct measurement results using Q# test code."""
qsharp.eval("TestCode.TestMeasurement()")
24 changes: 6 additions & 18 deletions samples/testing/operations/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Testing Operations in the QDK
This sample project demonstrates different approaches to testing operations in the QDK, both via Python and within Q# itself.
# Testing Unitary Implementations
This sample project demonstrates different approaches to testing operations that implement unitary transformations in the QDK, both via Python and within Q# itself.

## Testing Methods

There are two primary ways to test operations in the QDK:

1. **Using Operation Matrix Representation:**
- Use the `dump_operation` Python API to retrieve the operation's representation as a matrix and compare it against the expected matrix.
1. **Using operation matrix representation:**
Use the `dump_operation` Python API to retrieve the operation's representation as a matrix and compare it against the expected matrix.

2. **Q# `Fact` Assertions:**
- Use a `Fact` function in your Q# code that uses the `CheckOperationsAreEqual` operation to verify if two operations are identical up to a global phase. The `Fact` function asserts that the check returns `true`.
1. **`CheckOperationsAreEqual` operation and `Fact` assertion:**
Use a `Fact` function in your Q# code that uses the `CheckOperationsAreEqual` operation to verify if two operations are identical up to a global phase. The `Fact` function asserts that the check returns `true`.

## Project Structure
This sample project is a multi-file Q# project that showcases both testing methods. The project structure is as follows:
Expand All @@ -20,15 +20,3 @@ This sample project is a multi-file Q# project that showcases both testing metho
- `OperationEquivalence.qs`: Q# file containing the `TestEquivalence` operation to be called in Python wrapper
- `qsharp.json`: Q# project manifest file, instructing compiler to include all files in `src` directory.
- `test_dump_operation.py`: Python wrapper containing tests.

## Installation
- Install the `qsharp` Python package by following the instructions mentioned [here](https://learn.microsoft.com/azure/quantum/install-overview-qdk#add-support-for-python-and-jupyter-notebooks).
- Install `pytest` Python package.

## Running the sample
Open the `samples/testing/operations` directory, and run `pytest` command.

## Reference Links:
- [Q# Testing guide](https://learn.microsoft.com/azure/quantum/user-guide/testing-debugging).
- [Getting started with the QDK](https://learn.microsoft.com/azure/quantum/install-overview-qdk)
- [Getting started with Pytest](https://docs.pytest.org/en/stable/getting-started.html)
4 changes: 2 additions & 2 deletions samples/testing/operations/src/CustomOperation.qs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// Licensed under the MIT License.

namespace CustomOperation {
// # Summary
/// CNOT based operation for testing with `TestEquivalence` operation.
/// # Summary
/// CNOT based SWAP operation for testing with `TestEquivalence` operation.
///
/// # Input
/// ## q1
Expand Down
Loading