-
Notifications
You must be signed in to change notification settings - Fork 162
First version of table lookup library #2834
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
Merged
DmitryVasilevsky
merged 12 commits into
main
from
dmitryv/radiansdmitryv/table-lookup-lib
Jan 15, 2026
Merged
Changes from 6 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
232a52b
Initial version of table lookup library
4deb0dc
Updated optimized recursive implementation
851605e
More tests for select
ec20134
More options and comments
508fc64
More comments and tests. Bug fix.
e6c6f95
More comments and documentation
b3eb6a7
Update library/table_lookup/README.md
DmitryVasilevsky 6299d07
Review feedback
0834c30
Review feedback - MeasureAndComputePhaseData suitable for QIR
07742a9
Adjusted comments
bfc0a24
Review feedback - added reference section numbers
19d32d1
Review feedback - missing reference
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 hidden or 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,31 @@ | ||
| # table_lookup library | ||
|
|
||
| The `table_lookup` library defines various primitives useful to perform computation and uncomputation of table lookup. It also defines wrapper function | ||
| which uses one of the approaches depending on options. | ||
|
|
||
| ## Lookup | ||
|
|
||
| `Lookup` is the main operation implementing various table lookup algorithms and options. Note, that most unlookup algorithms are measurement-based and return target register to zero state. | ||
|
|
||
| ### Options for lookup: | ||
|
|
||
| * `DoStdLookup` - Use lookup algorithm defined in the Q# standard library. | ||
| * `DoMCXLookup` - Use naive lookup algorithm via multicontrolled X gates. | ||
| * `DoRecursiveSelectLookup` - Use select network algorithm via recursion. See [arXiv:2211.01133](https://arxiv.org/abs/2211.01133) | ||
| * `DoPPLookup` - Use lookup algorithm via power products without address split. See [arXiv:2505.15917](https://arxiv.org/abs/2505.15917) | ||
| * `DoSplitPPLookup` - Use lookup algorithm via power products with address split. See [arXiv:2505.15917](https://arxiv.org/abs/2505.15917) | ||
|
|
||
| ### Options for unlookup: | ||
|
|
||
| * `DoStdUnlookup` - Use unlookup algorithm defined in the Q# standard library. | ||
| * `DoUnlookupViaLookup` - Perform unlookup via the same algorithm as lookup as it is self-adjoint. | ||
| * `DoMCXUnlookup` - Perform measurement-based unlookup with corrections via multicontrolled X gates. See [arXiv:2211.01133](https://arxiv.org/abs/2211.01133) | ||
| * `DoPPUnlookup` - Perform measurement-based unlookup with corrections via power products without address split (Phase lookup). See [arXiv:2505.15917](https://arxiv.org/abs/2505.15917) | ||
| * `DoSplitPPUnlookup` - Perform measurement-based unlookup with corrections via power products with address split (Phase lookup). See [arXiv:2505.15917](https://arxiv.org/abs/2505.15917) | ||
|
|
||
| ## Potential future work | ||
|
|
||
| * Add more control how uncomputation of AND gate is performed. | ||
| * Add resource estimation hints. | ||
| * If gate set includes multi-target gates, code can be optimized to use those. | ||
| * Implement delayed combined corrections. | ||
This file contains hidden or 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,25 @@ | ||
| { | ||
| "author": "Microsoft", | ||
| "license": "MIT", | ||
| "dependencies": { | ||
| "Qtest": { | ||
| "github": { | ||
| "owner": "Microsoft", | ||
| "repo": "qsharp", | ||
| "ref": "v1.21.0", | ||
| "path": "library/qtest" | ||
| } | ||
| } | ||
DmitryVasilevsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| "files": [ | ||
| "src/Lookup.qs", | ||
| "src/LookupViaPP.qs", | ||
| "src/Main.qs", | ||
| "src/Multicontrolled.qs", | ||
| "src/PhaseLookup.qs", | ||
| "src/PowerProducts.qs", | ||
| "src/RecursiveSelect.qs", | ||
| "src/Tests.qs", | ||
| "src/Utils.qs" | ||
| ] | ||
| } | ||
This file contains hidden or 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,292 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| import Std.Arrays.*; | ||
|
|
||
| import Utils.*; | ||
| import Multicontrolled.*; | ||
| import RecursiveSelect.*; | ||
| import LookupViaPP.*; | ||
| import PhaseLookup.*; | ||
|
|
||
| // ---------------------------------------------- | ||
| // Lookup algorithm options. | ||
|
|
||
| /// Use lookup algorithm defined in the standard library. | ||
| function DoStdLookup() : Int { | ||
| 0 | ||
| } | ||
|
|
||
| /// Use basic lookup algorithm with multicontrolled X gates. | ||
| function DoMCXLookup() : Int { | ||
| 1 | ||
| } | ||
|
|
||
| /// Use recursive SELECT network as lookup algorithm. | ||
| function DoRecursiveSelectLookup() : Int { | ||
| 2 | ||
| } | ||
|
|
||
| /// Use lookup algorithm via power products without address split. | ||
| function DoPPLookup() : Int { | ||
| 3 | ||
| } | ||
|
|
||
| /// Use lookup algorithm via power products with address split. | ||
| function DoSplitPPLookup() : Int { | ||
| 4 | ||
| } | ||
|
|
||
| // ---------------------------------------------- | ||
| // Unlookup algorithm options. | ||
|
|
||
| /// Use unlookup algorithm defined in the standard library. | ||
| function DoStdUnlookup() : Int { | ||
| 0 | ||
| } | ||
|
|
||
| /// Use the same unlookup algorithm as lookup. | ||
| /// This is always possible as lookup is self-adjoint. | ||
| function DoUnlookupViaLookup() : Int { | ||
| 1 | ||
| } | ||
|
|
||
| /// Use unlookup algorithm with multicontrolled X gates. | ||
| /// This options is measurement based and returns target to zero state. | ||
| function DoMCXUnlookup() : Int { | ||
| 2 | ||
| } | ||
|
|
||
| /// Use unlookup algorithm via power products without address split (Phase lookup). | ||
| /// This options is measurement based and returns target to zero state. | ||
| function DoPPUnlookup() : Int { | ||
| 3 | ||
| } | ||
|
|
||
| /// Use unlookup algorithm via power products with address split (Phase lookup). | ||
| /// This options is measurement based and returns target to zero state. | ||
| function DoSplitPPUnlookup() : Int { | ||
| 4 | ||
| } | ||
|
|
||
| /// # Summary | ||
| /// Options for table lookup and unlookup operations. | ||
| struct LookupOptions { | ||
| // Specifies lookup algorithm. Options: | ||
| // `DoStdLookup`, `DoMCXLookup`, `DoRecursiveSelectLookup`, `DoPPLookup`, `DoSplitPPLookup`. | ||
| lookupAlgorithm : Int, | ||
|
|
||
| // Specifies unlookup algorithm. Options: | ||
| // `DoStdUnlookup`, `DoUnlookupViaLookup`, `DoMCXUnlookup`, `DoPPUnlookup`, `DoSplitPPUnlookup`. | ||
| unlookupAlgorithm : Int, | ||
| // Suggests using measurement-based uncomputation where applicable. | ||
| // Note that some algorithms are measurement-based only and some cannot use measurements. | ||
| // If `true`, use measurement-based uncomputations. Example: prefer adjoint AND. | ||
| // If `false`, avoid measurement-based uncomputations. Example: prefer adjoint CCNOT. | ||
| preferMeasurementBasedUncomputation : Bool, | ||
|
|
||
| // If `true`, an error is raised if data is longer than addressable space. | ||
| // If `false`, longer data beyond addressable space is ignored. | ||
| failOnLongData : Bool, | ||
|
|
||
| // If `true`, an error is raised if data is shorter than addressable space. | ||
| // If `false`, shorter data is tolerated according to respectExcessiveAddress. | ||
| failOnShortData : Bool, | ||
|
|
||
| // If `true`, all address qubits are respected and used. | ||
| // Addressing beyond data length yields the same result as if the data was padded with `false` values. | ||
| // If `false`, addressing beyond data length yields undefined results. | ||
| // As one consequence, when data is shorter than addressable space, higher address qubits are ignored. | ||
| respectExcessiveAddress : Bool, | ||
| } | ||
|
|
||
| /// # Summary | ||
| /// Default lookup options. Use power products with register split for lookup and unlookup. | ||
| function DefaultLookupOptions() : LookupOptions { | ||
| new LookupOptions { | ||
| lookupAlgorithm = DoSplitPPLookup(), | ||
| unlookupAlgorithm = DoSplitPPUnlookup(), | ||
| failOnLongData = false, | ||
| failOnShortData = false, | ||
| respectExcessiveAddress = false, | ||
| preferMeasurementBasedUncomputation = true, | ||
| } | ||
| } | ||
|
|
||
| /// # Summary | ||
| /// Performs table lookup/unlookup operation using specified algorithm and other options. | ||
| /// | ||
| /// # Input | ||
| /// ## options | ||
| /// LookupOptions defining lookup and unlookup algorithms and other parameters. | ||
| /// ## data | ||
| /// The data table to be looked up. Each entry is a Bool array the size of the target register. | ||
| /// ## address | ||
| /// Qubit register representing the address in little-endian format. | ||
| /// If the state of this register is one of the basis states |i⟩, and the target register is in |0⟩, | ||
| /// the target register will be set to the value data[i] during lookup. Address can also be in superposition. | ||
| /// ## target | ||
| /// Qubit register to accept the target data. Must be in the |0⟩ state for some algorithms. | ||
| /// For specifics see the corresponding algorithm implementation. | ||
| operation Lookup( | ||
| options : LookupOptions, | ||
| data : Bool[][], | ||
| address : Qubit[], | ||
| target : Qubit[] | ||
| ) : Unit is Adj + Ctl { | ||
| body (...) { | ||
| if (options.lookupAlgorithm == DoStdLookup()) { | ||
| // Don't do anthing beyond standard library select. | ||
| Std.TableLookup.Select(data, address, target); | ||
| return (); | ||
| } | ||
|
|
||
| let input = PrepareAddressAndData(options, address, data); | ||
|
|
||
| if options.lookupAlgorithm == DoMCXLookup() { | ||
| // Basic lookup via multicontrolled X gates. | ||
| LookupViaMCX(input.fitData, input.fitAddress, target); | ||
| return (); | ||
| } | ||
|
|
||
| if options.lookupAlgorithm == DoRecursiveSelectLookup() { | ||
| // Recursive select implementation. | ||
| if (options.respectExcessiveAddress) { | ||
| RecursiveLookup(options.preferMeasurementBasedUncomputation, input.fitData, input.fitAddress, target); | ||
| } else { | ||
| RecursiveLookupOpt(options.preferMeasurementBasedUncomputation, input.fitData, input.fitAddress, target); | ||
| } | ||
| return (); | ||
| } | ||
|
|
||
| if options.lookupAlgorithm == DoPPLookup() { | ||
| // Lookup via power products without address split. | ||
| LookupViaPP(input.fitData, input.fitAddress, target); | ||
| return (); | ||
| } | ||
|
|
||
| if options.lookupAlgorithm == DoSplitPPLookup() { | ||
| LookupViaSplitPP(input.fitData, input.fitAddress, target); | ||
| return (); | ||
| } | ||
|
|
||
| fail $"Unknown lookup algorithm specified ({options.lookupAlgorithm})."; | ||
| } | ||
|
|
||
| controlled (controls, ...) { | ||
| let control_size = Length(controls); | ||
| if control_size == 0 { | ||
| Lookup(options, data, address, target); | ||
| return (); | ||
| } | ||
|
|
||
| if options.lookupAlgorithm == DoStdLookup() { | ||
| // Don't do anthing beyond standard library select. | ||
| Controlled Std.TableLookup.Select(controls, (data, address, target)); | ||
| return (); | ||
| } | ||
|
|
||
| let input = PrepareAddressAndData(options, address, data); | ||
|
|
||
| if options.lookupAlgorithm == DoMCXLookup() { | ||
| // This is already a multicontrolled approach. Just add more controls. | ||
| Controlled LookupViaMCX(controls, (data, address, target)); | ||
| return (); | ||
| } | ||
|
|
||
| // Combine multiple controls into one. | ||
| use aux = Qubit[control_size - 1]; | ||
| within { | ||
| CombineControls(controls, aux); | ||
| } apply { | ||
| let single_control = GetCombinedControl(controls, aux); | ||
|
|
||
| if options.lookupAlgorithm == DoRecursiveSelectLookup() { | ||
| // Recursive select implementation. | ||
| if (options.respectExcessiveAddress) { | ||
| ControlledRecursiveSelect( | ||
| options.preferMeasurementBasedUncomputation, | ||
| single_control, | ||
| input.fitData, | ||
| input.fitAddress, | ||
| target | ||
| ); | ||
| } else { | ||
| ControlledRecursiveSelectOpt( | ||
| options.preferMeasurementBasedUncomputation, | ||
| single_control, | ||
| input.fitData, | ||
| input.fitAddress, | ||
| target | ||
| ); | ||
| } | ||
| } else { | ||
| // To use control qubit as an extra address qubit we need to respect entire address. | ||
| // Power products implementation already does that. | ||
| within { | ||
| // Invert control so that data is selected when control is |1> | ||
DmitryVasilevsky marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| X(single_control); | ||
| } apply { | ||
| // Add control as the most significant address qubit. | ||
| if options.lookupAlgorithm == DoPPLookup() { | ||
| LookupViaPP(input.fitData, input.fitAddress + [single_control], target); | ||
| } elif options.lookupAlgorithm == DoSplitPPLookup() { | ||
| LookupViaSplitPP(input.fitData, input.fitAddress + [single_control], target); | ||
| } else { | ||
| fail $"Unknown lookup algorithm specified ({options.lookupAlgorithm})."; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| adjoint (...) { | ||
| if (options.unlookupAlgorithm == DoStdUnlookup()) { | ||
| // Don't do anthing beyond standard library select. | ||
| Adjoint Std.TableLookup.Select(data, address, target); | ||
| return (); | ||
| } | ||
| if (options.unlookupAlgorithm == DoUnlookupViaLookup()) { | ||
| // Perform same lookup operation (as it is self-adjoint). | ||
| Lookup(options, data, address, target); | ||
| return (); | ||
| } | ||
|
|
||
| // Perform measurement-based uncomputation. | ||
| let input = PrepareAddressAndData(options, address, data); | ||
| let phaseData = MeasureAndComputePhaseData(target, input.fitData, Length(input.fitAddress)); | ||
| // Now apply phase corrections after measurement-based uncomputation. | ||
|
|
||
| if options.unlookupAlgorithm == DoMCXUnlookup() { | ||
| // Phase lookup via multicontrolled X gates. | ||
| PhaseLookupViaMCX(phaseData, input.fitAddress); | ||
| return (); | ||
| } | ||
|
|
||
| if options.unlookupAlgorithm == DoPPUnlookup() { | ||
| // Phase lookup via power products without address split. | ||
| PhaseLookupViaPP(input.fitAddress, phaseData); | ||
| return (); | ||
| } | ||
|
|
||
| if options.unlookupAlgorithm == DoSplitPPUnlookup() { | ||
| // Phase lookup via power products with address split. | ||
| PhaseLookupViaSplitPP(input.fitAddress, phaseData); | ||
| return (); | ||
| } | ||
|
|
||
| fail $"Unknown unlookup algorithm specified ({options.unlookupAlgorithm})."; | ||
| } | ||
|
|
||
| controlled adjoint (controls, ...) { | ||
| if options.unlookupAlgorithm == DoStdUnlookup() { | ||
| // Don't do anthing beyond standard library select. | ||
| Controlled Adjoint Std.TableLookup.Select(controls, (data, address, target)); | ||
| return (); | ||
| } | ||
|
|
||
| // In all other cases we perform controlled lookup as | ||
| // we cannot do controlled measurement-based uncomputation. | ||
| Controlled Lookup(controls, (options, data, address, target)); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.