-
Notifications
You must be signed in to change notification settings - Fork 90
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
Durr hoyer library min and max #1936
Closed
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
c320532
refactors searching pattern as python build.py was throwing error unt…
mertall 3d7722f
initial version of min max of durr hoyer algorithm, issues with DrawR…
mertall 6b339de
adds finding max testing
mertall c16fa6d
Update imports to match other pattern for other Std files
mertall 77a1f95
removes unecessary imports and moves Random generation outside of Std…
mertall 96e5f87
fixes imports for tests and addresses redundant logic, renaming vars …
mertall 1f17e43
seperates common logic out of unit tests for findng max and min, corr…
mertall ea88f5f
adds required summary annotations to all operations/functions
mertall d6949d0
reverts back to older commit, functionality of 50% accuracy was lost.…
mertall 7951452
fixes testing file
mertall e0259fe
changes requested implemented
mertall 689e346
adds unit tests for 0 in list, updates algorithm
mertall 0ab0fba
fixes typo for betterCandidateFound
mertall 9c1013e
formatting
mertall 1d55cfb
formatting for test operation and removes redundant test
mertall 651886a
updates testing suite, code is exectuable now, but rust script cannot…
mertall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use super::test_expression_with_lib; | ||
use qsc::interpret::Value; | ||
|
||
const DURR_HOYER_LIB: &str = include_str!("resources/src/durrhoyerlibrary.qs"); | ||
|
||
#[test] | ||
fn check_durr_hoyer_minimum_test_case() { | ||
// Call the test expression for running the Durr-Hoyer Minimum Unit Test | ||
test_expression_with_lib( | ||
"Test.RunDurrHoyerMinimumUnitTestWithShots(1000)", | ||
DURR_HOYER_LIB, | ||
&Value::unit(), | ||
); | ||
} | ||
|
||
#[test] | ||
fn check_durr_hoyer_maximum_test_case() { | ||
// Call the test expression for running the Durr-Hoyer Maximum Unit Test | ||
test_expression_with_lib( | ||
"Test.RunDurrHoyerMaximumUnitTestWithShots(1000)", | ||
DURR_HOYER_LIB, | ||
&Value::unit(), | ||
); | ||
} | ||
|
||
#[test] | ||
fn check_durr_hoyer_zero_test_case() { | ||
// Call the test expression for running the Durr-Hoyer Maximum Unit Test | ||
test_expression_with_lib( | ||
"Test.RunDurrHoyerZeroValuesUnitTestWithShots(1000)", | ||
DURR_HOYER_LIB, | ||
&Value::unit(), | ||
); | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
namespace Test { | ||
import Std.Math.*; | ||
import Std.Arrays.*; | ||
import Std.Convert.*; | ||
import Std.Random.*; | ||
import Std.Diagnostics.*; | ||
import Std.DurrHoyerLibrary.*; | ||
|
||
function MaxIntArray(arr : Int[]) : Int { | ||
mutable max = arr[0]; | ||
for i in 1..(Length(arr) - 1) { | ||
if (arr[i] > max) { | ||
set max = arr[i]; | ||
} | ||
} | ||
return max; | ||
} | ||
|
||
/// Shared logic for running the Durr-Hoyer test cases | ||
operation RunDurrHoyerTestWithShots(testLists : Int[][], expectedIndices : Int[], shots : Int, testType : String) : Unit { | ||
// Iterate over test cases | ||
for (list, expectedIndex) in Zipped(testLists, expectedIndices) { | ||
let maxValue = MaxIntArray(list); | ||
let double : Double = IntAsDouble(maxValue + 1); | ||
let log : Double = Log(double) / Log(2.0); | ||
let nQubits = Ceiling(log); | ||
let listSize = Length(list); | ||
|
||
|
||
// Variable to track how many times we find the correct index | ||
mutable correctCount = 0; | ||
|
||
// Run the Durr-Hoyer algorithm multiple times (shots) | ||
for _ in 1..shots { | ||
let candidate = DrawRandomInt(0, Length(list) - 1); | ||
let foundIndex : Int = DurrHoyerAlgorithm(list, nQubits, testType, candidate, listSize); | ||
|
||
// Check if the found index matches the expected index | ||
if (foundIndex == expectedIndex) { | ||
set correctCount += 1; | ||
} | ||
} | ||
|
||
// Calculate the probability of finding the correct index | ||
let probability = IntAsDouble(correctCount) / IntAsDouble(shots); | ||
|
||
// Assert that the probability is above 50% | ||
Fact(probability >= 0.5, $"Probability of finding the {testType} for list {list} is less than 50%. Found: {probability * 100.0}%"); | ||
|
||
// Optionally print debugging info | ||
Message($"List: {list}"); | ||
Message($"Probability of finding the {testType} is {probability * 100.0}%"); | ||
} | ||
} | ||
|
||
// Minimum test case using the shared logic | ||
operation RunDurrHoyerMinimumUnitTestWithShots(shots : Int) : Unit { | ||
let testLists = [ | ||
[5, 3, 1, 2, 4], | ||
[6, 5, 4, 3, 1], | ||
[7, 5, 6, 1, 2] | ||
]; | ||
|
||
// Expected results (minimum element index for each list) | ||
let expectedMinIndices = [2, 4, 3]; | ||
|
||
// Use the shared logic to run the test with "min" type | ||
RunDurrHoyerTestWithShots(testLists, expectedMinIndices, shots, "min"); | ||
} | ||
operation RunDurrHoyerMaximumUnitTestWithShots(shots : Int) : Unit { | ||
let testLists : Int[][] = [ | ||
[2, 3, 1, 5, 4], | ||
[1, 5, 4, 3, 6], | ||
[7, 5, 6, 1, 2] | ||
]; | ||
|
||
// Expected results (maximum element index for each list) | ||
let expectedMaxIndices : Int[] = [3, 4, 0]; | ||
|
||
// Use the shared logic to run the test with "max" type | ||
RunDurrHoyerTestWithShots(testLists, expectedMaxIndices, shots, "max"); | ||
} | ||
operation RunDurrHoyerZeroValuesUnitTestWithShots(shots : Int) : Unit { | ||
let testLists = [ | ||
[0, 3, 1, 2, 4], | ||
[6, 0, 4, 3, 1], | ||
[7, 5, 6, 0, 2] | ||
]; | ||
|
||
// Expected results (minimum element index for each list) | ||
let expectedMinIndices = [0, 1, 3]; | ||
|
||
// Use the shared logic to run the test with "min" type | ||
RunDurrHoyerTestWithShots(testLists, expectedMinIndices, shots, "min"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
import Std.Math.*; | ||
import Std.Arrays.*; | ||
import Std.Convert.*; | ||
|
||
/// # Summary | ||
/// Counts the number of marked states to be used to optimize Grover iterations to do. | ||
/// | ||
function CountElements(list : Int[], comparison : Int -> Bool) : Int { | ||
mutable count = 0; | ||
|
||
for element in list { | ||
if comparison(element) { | ||
set count += 1; | ||
} | ||
} | ||
|
||
return count; | ||
} | ||
|
||
/// # Summary | ||
/// Converts an integer to its binary representation as an array of Results. | ||
/// The least significant bit is at index 0. | ||
/// | ||
function ConvertToBinary(value : Int, length : Int) : Result[] { | ||
// Validate input | ||
if (length <= 0) { | ||
fail "Length must be a positive integer."; | ||
} | ||
|
||
// Ensure the value fits within the specified length | ||
let maxVal = (1 <<< length) - 1; | ||
if (value < 0 or value > maxVal) { | ||
fail $"Value {value} cannot be represented with {length} bits."; | ||
} | ||
|
||
// Initialize the binary array with default values | ||
mutable binary : Result[] = []; | ||
|
||
// Generate the binary array | ||
for i in 0..length - 1 { | ||
let bitValue = value &&& (1 <<< i); // Extract the i-th bit | ||
let res = if (bitValue != 0) { One } else { Zero }; // Determine Result | ||
// Correct syntax to assign to the array | ||
set binary += [res]; | ||
|
||
} | ||
|
||
// Return the constructed binary array | ||
return binary; | ||
} | ||
|
||
/// # Summary | ||
/// Oracle that marks elements less than the threshold through Most Signficant Bit comparision | ||
/// | ||
operation OracleLessThan(threshold : Int, inputQubits : Qubit[], auxQubit : Qubit) : Unit is Adj + Ctl { | ||
// Convert the threshold to binary and compare | ||
let thresholdBits = ConvertToBinary(threshold, Length(inputQubits)); | ||
for i in 0..Length(thresholdBits) - 1 { | ||
if (thresholdBits[i] == Zero) { | ||
// Most Signficant Bit comparision, if There is a zero when the bits are compared we have something less than | ||
X(inputQubits[i]); // Flip qubits that should be zero in the threshold | ||
} | ||
} | ||
|
||
// Controlled-Z gate to flip the phase of the state if the element is less than the threshold | ||
Controlled Z(inputQubits, auxQubit); | ||
|
||
// Undo the X operations to revert qubits | ||
for i in 0..Length(thresholdBits) - 1 { | ||
if (thresholdBits[i] == Zero) { | ||
X(inputQubits[i]); | ||
} | ||
} | ||
} | ||
|
||
// Oracle that marks elements more than the threshold through Most Signficant Bit comparision | ||
operation OracleMoreThan(threshold : Int, inputQubits : Qubit[], auxQubit : Qubit) : Unit is Adj + Ctl { | ||
// Convert the threshold to binary and compare | ||
let thresholdBits = ConvertToBinary(threshold, Length(inputQubits)); | ||
for i in 0..Length(thresholdBits) - 1 { | ||
if (thresholdBits[i] == One) { | ||
// Most Signficant Bit comparision, if tbere is a one when the bits are compared we have something more than | ||
X(inputQubits[i]); // Flip qubits that should be zero in the threshold | ||
} | ||
} | ||
|
||
// Controlled-Z gate to flip the phase of the state if the element is less than the threshold | ||
Controlled Z(inputQubits, auxQubit); | ||
|
||
// Undo the X operations to revert qubits | ||
for i in 0..Length(thresholdBits) - 1 { | ||
if (thresholdBits[i] == One) { | ||
X(inputQubits[i]); | ||
} | ||
} | ||
} | ||
|
||
/// # Summary | ||
/// Diffusion operator (Grover's diffusion) | ||
/// | ||
operation DiffusionOperator(qubits : Qubit[]) : Unit { | ||
ApplyToEach(H, qubits); | ||
ApplyToEach(X, qubits); | ||
Controlled Z(qubits[0..Length(qubits) - 2], qubits[Length(qubits) - 1]); | ||
ApplyToEach(X, qubits); | ||
ApplyToEach(H, qubits); | ||
} | ||
|
||
// Grover iteration with the oracle and diffusion operator for min | ||
operation GroverIterationMin(threshold : Int, inputQubits : Qubit[], auxQubit : Qubit) : Unit { | ||
OracleLessThan(threshold, inputQubits, auxQubit); | ||
DiffusionOperator(inputQubits); | ||
} | ||
/// # Summary | ||
/// Grover iteration with the oracle and diffusion operator for max | ||
/// | ||
operation GroverIterationMax(threshold : Int, inputQubits : Qubit[], auxQubit : Qubit) : Unit { | ||
OracleMoreThan(threshold, inputQubits, auxQubit); | ||
DiffusionOperator(inputQubits); | ||
} | ||
|
||
/// # Summary | ||
/// Dürr-Høyer for finding min or max algorithm | ||
/// | ||
operation DurrHoyerAlgorithm(list : Int[], nQubits : Int, type : String, candidate : Int, listSize : Int) : Int { | ||
mutable candidate = candidate; // Random initial candidate | ||
|
||
use inputQubits = Qubit[nQubits] { | ||
use auxQubit = Qubit() { | ||
// Create a superposition of all states | ||
ApplyToEach(H, inputQubits); | ||
|
||
// Continue Grover search until no better candidate is found | ||
mutable betterCandidateFound = true; | ||
mutable iterationCount = 1; // Track the iteration count manually | ||
mutable optimalIterations = 5; | ||
mutable validIterations = 0; | ||
|
||
while (validIterations < optimalIterations) { | ||
set betterCandidateFound = false; | ||
let threshold = list[candidate]; | ||
|
||
// Define the comparison function based on the type | ||
let comparison = type == "min" ? (x -> x < threshold) | (x -> x > threshold); | ||
|
||
// Calculate M: the number of elements smaller than the current candidate (for min) | ||
let M = CountElements(list, comparison); | ||
|
||
// If there are no more elements smaller/larger, return the candidate | ||
if (M == 0) { | ||
Message("No more elements to compare, search complete."); | ||
ResetAll(inputQubits + [auxQubit]); // Ensure qubits are reset before returning | ||
return candidate; | ||
} | ||
|
||
// Calculate the optimal number of Grover iterations | ||
let N = Length(list); | ||
let optimalIterations = Round((PI() / 4.0) * Sqrt(IntAsDouble(N) / IntAsDouble(M))); | ||
|
||
// Perform Grover iterations for min or max | ||
for i in 1..optimalIterations { | ||
let groverIteration = (type == "min") ? GroverIterationMin | GroverIterationMax; | ||
groverIteration(list[candidate], inputQubits, auxQubit); | ||
|
||
// Measure qubits and convert to an integer index | ||
mutable results = []; | ||
for qubit in inputQubits { | ||
let result = Measure([PauliZ], [qubit]); | ||
set results += [result]; | ||
|
||
// Reset qubit if it is in the |1⟩ state | ||
if (result == One) { | ||
X(qubit); | ||
} | ||
} | ||
|
||
let candidateIndex = ResultArrayAsInt(results); | ||
|
||
// Check if the new candidate is valid and within bounds | ||
if (candidateIndex >= 0 and candidateIndex < listSize) { | ||
let candidateValue = list[candidateIndex]; | ||
|
||
// Update the candidate if a better one is found | ||
if (type == "min" and candidateValue < list[candidate]) { | ||
OracleLessThan(list[candidate], inputQubits, auxQubit); // Mark the last candidate | ||
set candidate = candidateIndex; | ||
set betterCandidateFound = true; | ||
} elif (type == "max" and candidateValue > list[candidate]) { | ||
OracleMoreThan(list[candidate], inputQubits, auxQubit); // Mark the last candidate | ||
set candidate = candidateIndex; | ||
set betterCandidateFound = true; | ||
} | ||
set validIterations += 1; | ||
|
||
// Output intermediate results for debugging | ||
Message($"Iteration {validIterations}: Measured index = {candidateIndex}, Value = {candidateValue}"); | ||
} | ||
// Reset all qubits to |0⟩ before returning | ||
ResetAll(inputQubits + [auxQubit]); | ||
|
||
} | ||
|
||
} | ||
|
||
// Reset all qubits to |0⟩ before returning | ||
ResetAll(inputQubits + [auxQubit]); | ||
|
||
// Return the found minimum or maximum index | ||
return candidate; | ||
} | ||
} | ||
} | ||
export DurrHoyerAlgorithm; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to change this? IMO this PR should focus only on implementing DH library.
I saw your commit message saying that build.py doesnt work for you without this change. I will leave this decision to @sezna.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is a way to skip over the error when I run the build.py I am happy to change it. I agree it is out of place, and will be hard to log trace down the line while it is included in this PR. maybe we can create a ticket to address this and I can push a PR for that?