Skip to content
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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open

Fix bugs in ECAdd bloq #1489

wants to merge 13 commits into from

Conversation

fpapa250
Copy link
Contributor

@fpapa250 fpapa250 commented Nov 7, 2024

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:

  1. Set a new flag bit when computed lam = lam_r (before writing lam from computed z register to lam register)
  2. Control existing equals bloq to clear f1 only when lam = lam_r AND the ancilla flag is set
  3. Unset ancilla and free when z4 (containing computed lam) = lam_r

2 - Step 5: free dirty reg lam when a,b=x,y
BUGFIX:

  1. set a new flag if x (denominator of lambda computation) = 0
  2. Clear lam using lam_r controlled on ctrl and new flag
  3. unset new flag when x = 0

3 - Step 6: free dirty reg when x,y = 0,0 and b = y = 0, but x != 0: f2 never gets flipped back
BUGFIX:

  1. unset f2 when x,y = 0 and b = 0 OR a,b = 0 and y = 0

4 - Step 6: when p1 = p2 and f4 not set f4 gets flipped on
BUGFIX:

  1. Move CModSub and CModAdd to before Equals(xy, ab) bloq

@fpapa250 fpapa250 marked this pull request as ready for review November 27, 2024 20:48
@fpapa250
Copy link
Contributor Author

fpapa250 commented Dec 3, 2024

@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.

@mpharrigan
Copy link
Collaborator

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

@mpharrigan
Copy link
Collaborator

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

@fpapa250
Copy link
Contributor Author

fpapa250 commented Jan 2, 2025

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!

Copy link
Contributor

@anurudhp anurudhp left a 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!

Comment on lines +753 to +774
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Contributor Author

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

Copy link
Contributor

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.

Copy link
Contributor

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,
Copy link
Contributor

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,
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants