-
Notifications
You must be signed in to change notification settings - Fork 146
Improved adjoint Select implementation #2729
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
base: main
Are you sure you want to change the base?
Conversation
Change in memory usage detected by benchmark. Memory Report for b44f15e
|
Change in memory usage detected by benchmark. Memory Report for fb4d8bc
|
Does this change the profile compatibility of the |
|
OK, maybe we are both right. :) It was using measurement based uncomputation but maybe in a way suitable for base profile - i.e. nothing actually depended on those measurements? I will need to dig deeper. qdk/library/std/src/Std/TableLookup.qs Line 191 in f2ca520
|
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.
Wow, you are right and I missed that! It turns out that this particular line you quote is exactly the problem; there's a class of code patterns where RCA can't definitively determine the compatibility of an operation (usually when it is passed as callable argument into another operation, as is done with ForEach
and MResetX
here). We used to err on the side of caution and reject these with an error, but too many valid programs were rejected that way. So instead we relaxed the restriction and deferred validation of these passed callables to QIR codegen (see #1497). That means that even though the editor doesn't show an error, Adjoint Select
with more than one data entry will fail Base profile QIR codegen.
Long story short, this is not a breaking change at all, and arguably is an improvement since the editor now shows the incompatibility at design time rather than at codegen time.
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.
Really exciting new operation to have in the libraries!
|
||
// From the second row on, take control from the first half and apply multi-target CZ gates. | ||
for row in 0..Length(products1)-1 { | ||
ApplyMaskedMultitargetCZ(products1[row], products2, Rest(ColumnAt(row + 1, mask))); |
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.
ApplyMaskedMultitargetCZ(products1[row], products2, Rest(ColumnAt(row + 1, mask))); | |
Controlled ApplyMaskedMultitargetZ([products1[row]], (products2, Rest(ColumnAt(row + 1, mask)))); |
You could do this and remove ApplyMaskedMultitargetCZ
by also making ApplyMaskedMultitargetZ
controlled. Or you do this with Controlled ApplyPauliFromBitString
.
Fact(Length(mask[0]) == Length(products1) + 1, "Mask column count must match products1 length."); | ||
|
||
// products1[0] doesn't include any qbits from the first half, so we need to apply Z instead of CZ. | ||
ApplyMaskedMultitargetZ(products2, Rest(ColumnAt(0, mask))); |
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.
ApplyMaskedMultitargetZ(products2, Rest(ColumnAt(0, mask))); | |
ApplyPauliFromBitString(PauliZ, true, Rest(ColumnAt(0, mask)), products2); |
Same for the other ones.
ApplyMaskedMultitargetZ(products2, Rest(ColumnAt(0, mask))); | ||
|
||
// products2[0] doesn't include any qubits from the second half, so we need to apply Z instead of CZ. | ||
ApplyMaskedMultitargetZ(products1, Rest(mask[0])); |
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.
One could apply the operation once to produts2 + products1
by also concatenating the masks.
This check-in improves implementation of adjoin variant of Select operation (Unlookup operation). Previously it was based on May 2019 paper by Craig Gidney, and this proposed implementation is based on 2025 paper by Craig Gidney.
The implementation of Unlookup is measurement-based with phase correction via Phase Lookup operation described in the aforementioned paper. Phase Lookup is implemented by splitting address register and computing power products for both halves. Then the necessary phase corrections are applied and power products are uncomputed.
This implementation uses fast Boolean Mobius transform instead of matrix multiplication as in the paper to convert classical data from truth table representation to coefficients of an algebraic normal form over GF(2) field. This results in simpler code.
A test is added to check that address register remains in correct state after uncomputation.
This implementation may reduce required resources to uncompute lookup operation. As one example, the circuit to uncompute one particular case with previous and proposed approaches. This is a highly dynamic algorithm that uses results of measurements to apply gates conditionally; actual execution trace and actual savings may vary.
Before:


After: