microsoft/qdk
Publicmirrored fromhttps://github.com/microsoft/qdkAvailable
library/qtest/src/Operations.qs
141lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | import Util.TestCaseResult, Util.OutputMessage; |
| 5 | import Std.Arrays.Mapped, Std.Arrays.All, Std.Arrays.Enumerated; |
| 6 | |
| 7 | /// # Summary |
| 8 | /// Runs a number of test cases and returns true if all tests passed, false otherwise. |
| 9 | /// Prints a report of what passed and what failed as output. |
| 10 | /// |
| 11 | /// For a more flexible test running function, see `RunAllTestCases` which returns |
| 12 | /// test results instead of printing out to output. |
| 13 | /// |
| 14 | /// # Input |
| 15 | /// Takes a list of test cases. A test case is a tuple of `(String, Int, Qubit[] => Unit, Qubit[] => 'T, 'T)`, where |
| 16 | /// the first String is the name of the test, the int is the number of qubits to allocate for this test, |
| 17 | /// the first function is a qubit state prep function to be run before the test, the second function is the test case itself, and the |
| 18 | /// final element of the tuple is the expected return value from the test case. |
| 19 | /// |
| 20 | /// # Example |
| 21 | /// ```qsharp |
| 22 | /// CheckAllTestCases([("0b0001 == 1", 4, (qs) => X(qs[0]), (qs) => MeasureSignedInteger(qs, 4), 1)]); |
| 23 | /// ``` |
| 24 | operation CheckAllTestCases<'T : Eq + Show>(test_cases : (String, Int, Qubit[] => (), Qubit[] => 'T, 'T)[]) : Bool { |
| 25 | let test_results = RunAllTestCases(test_cases); |
| 26 | |
| 27 | OutputMessage(test_results); |
| 28 | |
| 29 | All(test_case -> test_case.did_pass, test_results) |
| 30 | } |
| 31 | |
| 32 | /// # Summary |
| 33 | /// Runs all given test cases and returns a `TestCaseResult` for each test, representing whether or not it passed |
| 34 | /// and what the failure message, if any. |
| 35 | /// This is a good alternative to `CheckAllTestCases` when you want custom output based on the results of your tests, |
| 36 | /// or more control over how test results are rendered. |
| 37 | /// # Input |
| 38 | /// Takes a list of test cases. A test case is a tuple of `(String, Int, Qubit[] => Unit, Qubit[] => 'T, 'T)`, where |
| 39 | /// the first String is the name of the test, the Int is the number of qubits, the first operation prepares the state, |
| 40 | /// the second operation is the test case itself, and the final element is the expected return value. |
| 41 | /// |
| 42 | /// # Example |
| 43 | /// ```qsharp |
| 44 | /// RunAllTestCases([("0b0001 == 1", 4, (qs) => X(qs[0]), (qs) => MeasureSignedInteger(qs, 4), 1)]); |
| 45 | /// ``` |
| 46 | operation RunAllTestCases<'T : Eq + Show>(test_cases : (String, Int, Qubit[] => Unit, Qubit[] => 'T, 'T)[]) : TestCaseResult[] { |
| 47 | MappedOperation((name, num_qubits, prepare_state, case, result) => { |
| 48 | use qubits = Qubit[num_qubits]; |
| 49 | prepare_state(qubits); |
| 50 | let res = TestCase(name, qubits, case, result); |
| 51 | ResetAll(qubits); |
| 52 | res |
| 53 | }, test_cases) |
| 54 | } |
| 55 | |
| 56 | /// Helper function, copy of `Std.Arrays.Mapped` which works on operations instead |
| 57 | /// of functions. |
| 58 | operation MappedOperation<'T, 'U>(mapper : ('T => 'U), array : 'T[]) : 'U[] { |
| 59 | mutable mapped = []; |
| 60 | for element in array { |
| 61 | set mapped += [mapper(element)]; |
| 62 | } |
| 63 | mapped |
| 64 | } |
| 65 | |
| 66 | |
| 67 | /// # Summary |
| 68 | /// Given an operation on some qubits `func` which returns some value to test and a number of qubits to use `num_qubits`, |
| 69 | /// runs a number of test cases of the form `(Qubit[] => Unit, 'O)` where the first element is a qubit |
| 70 | /// state preparation operation and the second element is the expected output of the operation. |
| 71 | /// Returns the result of the `mode` function which takes a list of test cases and returns a value of type `'U`. |
| 72 | /// |
| 73 | /// # Input |
| 74 | /// - `test_suite_name` : A string representing the name of the test suite. |
| 75 | /// - `func` : An operation which takes an array of qubits and returns a value of type `'O`. |
| 76 | /// - `num_qubits` : The number of qubits to use in the test. These are allocated before the test and reset before each test case. |
| 77 | /// - `test_cases` : A list of test cases, each of the form `(Qubit[] => Unit, 'O)`. The lambda operation should set up the qubits |
| 78 | /// in a specific state for `func` to operate on. |
| 79 | /// - `mode` : A function which takes a list of test cases and returns a value of type `'U`. Intended to be either `Qtest.Operations.CheckAllTestCases` or `Qtest.Operations.RunAllTestCases`. |
| 80 | /// |
| 81 | /// # Example |
| 82 | /// ```qsharp |
| 83 | /// let test_cases: (Qubit[] => Unit, Int)[] = [ |
| 84 | /// (qs => { X(qs[0]); X(qs[3]); }, 0b1001), |
| 85 | /// (qs => { X(qs[0]); X(qs[1]); }, 0b0011) |
| 86 | /// ]; |
| 87 | /// |
| 88 | /// let res : Util.TestCaseResult[] = Operations.TestMatrix( |
| 89 | /// // test name |
| 90 | /// "QubitTestMatrix", |
| 91 | /// // operation to test |
| 92 | /// qs => MeasureInteger(qs), |
| 93 | /// // number of qubits |
| 94 | /// 4, |
| 95 | /// // test cases |
| 96 | /// test_cases, |
| 97 | /// // test mode |
| 98 | /// Operations.RunAllTestCases |
| 99 | /// ); |
| 100 | /// ``` |
| 101 | |
| 102 | operation TestMatrix<'O : Show + Eq, 'U>( |
| 103 | test_suite_name : String, |
| 104 | func : Qubit[] => 'O, |
| 105 | num_qubits : Int, |
| 106 | test_cases : (Qubit[] => Unit, 'O)[], |
| 107 | mode : ((String, Int, Qubit[] => Unit, Qubit[] => 'O, 'O)[]) => 'U |
| 108 | ) : 'U { |
| 109 | let test_cases_qs = Mapped((ix, (qubit_prep_function, expected)) -> (test_suite_name + $" {ix + 1}", num_qubits, qubit_prep_function, func, expected), Enumerated(test_cases)); |
| 110 | mode(test_cases_qs) |
| 111 | } |
| 112 | |
| 113 | operation CheckTestMatrix<'O : Show + Eq>( |
| 114 | test_suite_name : String, |
| 115 | func : Qubit[] => 'O, |
| 116 | num_qubits : Int, |
| 117 | test_cases : (Qubit[] => Unit, 'O)[] |
| 118 | ) : Bool { |
| 119 | TestMatrix(test_suite_name, func, num_qubits, test_cases, CheckAllTestCases) |
| 120 | } |
| 121 | |
| 122 | operation RunTestMatrix<'O : Show + Eq>( |
| 123 | test_suite_name : String, |
| 124 | func : Qubit[] => 'O, |
| 125 | num_qubits : Int, |
| 126 | test_cases : (Qubit[] => Unit, 'O)[] |
| 127 | ) : TestCaseResult[] { |
| 128 | TestMatrix(test_suite_name, func, num_qubits, test_cases, RunAllTestCases) |
| 129 | } |
| 130 | |
| 131 | /// Internal (non-exported) helper function. Runs a test case and produces a `TestCaseResult` |
| 132 | operation TestCase<'T : Eq + Show>(name : String, qubits : Qubit[], test_case : (Qubit[]) => 'T, expected : 'T) : TestCaseResult { |
| 133 | let result = test_case(qubits); |
| 134 | if result == expected { |
| 135 | new TestCaseResult { did_pass = true, message = "" } |
| 136 | } else { |
| 137 | new TestCaseResult { did_pass = false, message = $"{name}: expected: {expected}, got: {result}" } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | export CheckAllTestCases, RunAllTestCases, TestMatrix, CheckTestMatrix, RunTestMatrix; |