-
Notifications
You must be signed in to change notification settings - Fork 54
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
Fix bugs in ECAdd bloq #1489
base: main
Are you sure you want to change the base?
Fix bugs in ECAdd bloq #1489
Conversation
Co-authored-by: Anurudh Peduri <[email protected]>
@mpharrigan Here are the bug fixes to the ECAdd circuit that the paper didn't consider. They are thoroughly tested with the existing unit tests. |
Nice! The issue and PR description describes the modifications and why they were needed -- but I suspect only the most dedicated users will find them! Do you want to put a description of the modifications into the library somewhere? Ideally this would be in the docstrings of the public bloq classes so they'd get rendered into the docs |
an alternative would be to have a dedicated jupyter notebook written as a mini-paper that goes through all the crypto bloqs and includes a section on the modifications. This would be more work but this is the sort of thing we can slap a byline on and get a zenodo doi to make it citable |
Sorry for the radio silence, took a nice break for the holidays. I'll find some time in the next two weeks to make a dedicated jupyter notebook. Sounds like a good idea! |
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.
sorry for all the nitpicking, it can be done in a follow-up as well!
ancilla = bb.allocate() | ||
x_split = bb.split(x) | ||
x_split, ancilla = bb.add( | ||
MultiControlX(cvs=[0] * int(self.n)), controls=x_split, target=ancilla | ||
) | ||
lam_r_split = bb.split(lam_r) | ||
lam_split = bb.split(lam) | ||
for i in range(int(self.n)): | ||
ctrls = [ctrl, ancilla, lam_r_split[i]] | ||
ctrls, lam_split[i] = bb.add( | ||
MultiControlX(cvs=[1, 1, 1]), controls=ctrls, target=lam_split[i] | ||
) | ||
ctrl = ctrls[0] | ||
ancilla = ctrls[1] | ||
lam_r_split[i] = ctrls[2] | ||
lam_r = bb.join(lam_r_split, dtype=QMontgomeryUInt(self.n)) | ||
lam = bb.join(lam_split, dtype=QMontgomeryUInt(self.n)) | ||
x_split, ancilla = bb.add( | ||
MultiControlX(cvs=[0] * int(self.n)), controls=x_split, target=ancilla | ||
) | ||
x = bb.join(x_split, dtype=QMontgomeryUInt(self.n)) | ||
bb.free(ancilla) |
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.
ancilla = bb.allocate() | |
x_split = bb.split(x) | |
x_split, ancilla = bb.add( | |
MultiControlX(cvs=[0] * int(self.n)), controls=x_split, target=ancilla | |
) | |
lam_r_split = bb.split(lam_r) | |
lam_split = bb.split(lam) | |
for i in range(int(self.n)): | |
ctrls = [ctrl, ancilla, lam_r_split[i]] | |
ctrls, lam_split[i] = bb.add( | |
MultiControlX(cvs=[1, 1, 1]), controls=ctrls, target=lam_split[i] | |
) | |
ctrl = ctrls[0] | |
ancilla = ctrls[1] | |
lam_r_split[i] = ctrls[2] | |
lam_r = bb.join(lam_r_split, dtype=QMontgomeryUInt(self.n)) | |
lam = bb.join(lam_split, dtype=QMontgomeryUInt(self.n)) | |
x_split, ancilla = bb.add( | |
MultiControlX(cvs=[0] * int(self.n)), controls=x_split, target=ancilla | |
) | |
x = bb.join(x_split, dtype=QMontgomeryUInt(self.n)) | |
bb.free(ancilla) | |
clear_lam = ( | |
Xor(QMontgomeryUInt(self.n)) | |
.controlled(CtrlSpec(qdtypes=QMontgomeryUInt(self.n), cvs=0)) | |
.controlled() | |
) | |
ctrl, x, lam_r, lam = bb.add(clear_lam, ctrl1=ctrl, ctrl2=x, x=lam_r, y=lam) |
You can also factor the clear_lam
bloq into a class property, so it can also be used in the call graph.
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.
I tried replacing the code as suggested with the clear_lam bloq, but it gives me an error: qualtran._infra.composite_bloq.BloqError: During finalization, {Soquet(binst=BloqInstance(bloq=ControlledViaAnd(subbloq=Xor(dtype=QMontgomeryUInt(bitsize=8, modulus=None)), ctrl_spec=CtrlSpec(qdtypes=(QBit(), QMontgomeryUInt(bitsize=8, modulus=None)), cvs=(array(1), array(0)))), i=14), reg=Register(name='y', dtype=QMontgomeryUInt(bitsize=8, modulus=None), _shape=(), side=<Side.THRU: 3>), idx=())} Soquets were not used.
Do you know what's going wrong here? I'm not really understanding why it's complaining
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.
Hey, could you tell me which test raises this error? I replaced it locally and the tests seem to all pass.
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.
Did you accidentally remove the next line that frees lam
? That may have caused the above issue
@@ -771,6 +817,8 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: | |||
KaliskiModInverse(bitsize=self.n, mod=self.mod).adjoint(): 1, | |||
ModAdd(self.n, mod=self.mod): 1, | |||
MultiControlX(cvs=[1, 1]): self.n, | |||
MultiControlX(cvs=cvs): 2, | |||
MultiControlX(cvs=[1, 1, 1]): self.n, |
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.
can be replaced by the above clear_lam
bloq
return { | ||
MultiControlX(cvs=cvs): 1, | ||
MultiControlX(cvs=cvs2): 1, | ||
MultiControlX(cvs=cvs3): 2, |
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.
is this the newly refactored mcx
gate? if yes, replacing will also simplify the symbolic case
The circuit described in the paper had 4 bugs (#1461) which caused (with edge case inputs) flags f1, f2, f4 and lambda to be freed (considered = 0 in the paper) while having values > 0.
Bugs:
1 - Step 2: lam = lam_r undoes f1 when it should only happen if lam is set to lam_r (very rare case)
BUGFIX:
2 - Step 5: free dirty reg lam when a,b=x,y
BUGFIX:
3 - Step 6: free dirty reg when x,y = 0,0 and b = y = 0, but x != 0: f2 never gets flipped back
BUGFIX:
4 - Step 6: when p1 = p2 and f4 not set f4 gets flipped on
BUGFIX: