diff --git a/dev_tools/requirements/envs/dev.env.txt b/dev_tools/requirements/envs/dev.env.txt index b7f7e5879..55ebe6919 100644 --- a/dev_tools/requirements/envs/dev.env.txt +++ b/dev_tools/requirements/envs/dev.env.txt @@ -16,7 +16,7 @@ anyio==4.8.0 # via # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via qsharp-widgets argon2-cffi==23.1.0 # via jupyter-server @@ -68,7 +68,7 @@ blinker==1.9.0 # via flask build==1.2.2.post1 # via pip-tools -cachetools==5.5.1 +cachetools==5.5.2 # via -r deps/runtime.txt certifi==2025.1.31 # via @@ -97,11 +97,11 @@ comm==0.2.2 # ipywidgets contourpy==1.3.1 # via matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via quimb -coverage[toml]==7.6.10 +coverage[toml]==7.6.12 # via pytest-cov -cryptography==44.0.0 +cryptography==44.0.2 # via secretstorage cycler==0.12.1 # via matplotlib @@ -115,9 +115,9 @@ dash-html-components==2.0.0 # via dash dash-table==5.0.0 # via dash -debugpy==1.8.12 +debugpy==1.8.13 # via ipykernel -decorator==5.1.1 +decorator==5.2.1 # via ipython defusedxml==0.7.1 # via nbconvert @@ -173,7 +173,7 @@ grpcio-tools==1.70.0 # via -r deps/packaging.txt h11==0.14.0 # via httpcore -h5py==3.12.1 +h5py==3.13.0 # via # openfermion # pyscf @@ -204,7 +204,7 @@ ipykernel==6.29.5 # -r deps/pytest.txt # jupyterlab # myst-nb -ipython==8.32.0 +ipython==8.33.0 # via # -r deps/runtime.txt # ipykernel @@ -230,15 +230,15 @@ jaraco-context==6.0.1 # via keyring jaraco-functools==4.1.0 # via keyring -jax==0.5.0 +jax==0.4.38 # via openfermion -jaxlib==0.5.0 +jaxlib==0.4.38 # via # jax # openfermion jedi==0.19.2 # via ipython -jeepney==0.8.0 +jeepney==0.9.0 # via # keyring # secretstorage @@ -320,7 +320,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -r deps/runtime.txt # ase @@ -335,7 +335,7 @@ mdit-py-plugins==0.4.2 # via myst-parser mdurl==0.1.2 # via markdown-it-py -mistune==3.1.1 +mistune==3.1.2 # via nbconvert ml-dtypes==0.5.1 # via @@ -357,9 +357,9 @@ mypy-protobuf==3.6.0 # via -r deps/mypy.txt myst-nb==1.2.0 # via -r deps/docs.txt -myst-parser==4.0.0 +myst-parser==4.0.1 # via myst-nb -narwhals==1.25.2 +narwhals==1.29.0 # via plotly nbclient==0.10.2 # via @@ -390,7 +390,7 @@ networkx==3.4.2 # -r deps/runtime.txt # cirq-core # openfermion -nh3==0.2.20 +nh3==0.2.21 # via readme-renderer notebook==7.3.2 # via @@ -424,7 +424,7 @@ numpy==1.26.4 # pyzx # quimb # scipy -openfermion[resources]==1.6.1 +openfermion[resources]==1.7.0 # via # -r deps/pylint.txt # -r deps/pytest.txt @@ -484,7 +484,7 @@ protobuf==5.29.3 # grpcio-tools # mypy-protobuf # tensorflow-docs -psutil==6.1.1 +psutil==7.0.0 # via # ipykernel # quimb @@ -534,7 +534,7 @@ pyproject-hooks==1.2.0 # pip-tools pyscf==2.8.0 # via openfermion -pytest==8.3.4 +pytest==8.3.5 # via # -r deps/pylint.txt # -r deps/pytest.txt @@ -575,9 +575,9 @@ qref==0.9.0 # via # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via -r deps/runtime.txt quimb==1.10.0 # via -r deps/runtime.txt @@ -613,11 +613,11 @@ rfc3986-validator==0.1.1 # jupyter-events rich==13.9.4 # via twine -rpds-py==0.22.3 +rpds-py==0.23.1 # via # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # ase # cirq-core @@ -731,7 +731,7 @@ traitlets==5.14.3 # nbformat twine==6.1.0 # via -r deps/packaging.txt -types-protobuf==5.29.1.20241207 +types-protobuf==5.29.1.20250208 # via mypy-protobuf types-python-dateutil==2.9.0.20241206 # via arrow @@ -766,7 +766,7 @@ urllib3==2.3.0 # via # requests # twine -virtualenv==20.29.1 +virtualenv==20.29.2 # via -r deps/packaging.txt wcwidth==0.2.13 # via prompt-toolkit diff --git a/dev_tools/requirements/envs/docs.env.txt b/dev_tools/requirements/envs/docs.env.txt index df8008c78..e776921f8 100644 --- a/dev_tools/requirements/envs/docs.env.txt +++ b/dev_tools/requirements/envs/docs.env.txt @@ -25,7 +25,7 @@ anyio==4.8.0 # -c envs/dev.env.txt # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via # -c envs/dev.env.txt # qsharp-widgets @@ -89,7 +89,7 @@ blinker==1.9.0 # via # -c envs/dev.env.txt # flask -cachetools==5.5.1 +cachetools==5.5.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -125,7 +125,7 @@ contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via # -c envs/dev.env.txt # quimb @@ -153,11 +153,11 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.12 +debugpy==1.8.13 # via # -c envs/dev.env.txt # ipykernel -decorator==5.1.1 +decorator==5.2.1 # via # -c envs/dev.env.txt # ipython @@ -250,7 +250,7 @@ ipykernel==6.29.5 # -c envs/dev.env.txt # jupyterlab # myst-nb -ipython==8.32.0 +ipython==8.33.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -385,7 +385,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -403,7 +403,7 @@ mdurl==0.1.2 # via # -c envs/dev.env.txt # markdown-it-py -mistune==3.1.1 +mistune==3.1.2 # via # -c envs/dev.env.txt # nbconvert @@ -415,11 +415,11 @@ myst-nb==1.2.0 # via # -c envs/dev.env.txt # -r deps/docs.txt -myst-parser==4.0.0 +myst-parser==4.0.1 # via # -c envs/dev.env.txt # myst-nb -narwhals==1.25.2 +narwhals==1.29.0 # via # -c envs/dev.env.txt # plotly @@ -542,7 +542,7 @@ protobuf==5.29.3 # -c envs/dev.env.txt # -r deps/runtime.txt # tensorflow-docs -psutil==6.1.1 +psutil==7.0.0 # via # -c envs/dev.env.txt # ipykernel @@ -637,11 +637,11 @@ qref==0.9.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -675,12 +675,12 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.22.3 +rpds-py==0.23.1 # via # -c envs/dev.env.txt # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # -c envs/dev.env.txt # cirq-core diff --git a/dev_tools/requirements/envs/format.env.txt b/dev_tools/requirements/envs/format.env.txt index 44a10b8f4..d92793b4e 100644 --- a/dev_tools/requirements/envs/format.env.txt +++ b/dev_tools/requirements/envs/format.env.txt @@ -13,7 +13,7 @@ anyio==4.8.0 # -c envs/dev.env.txt # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via # -c envs/dev.env.txt # qsharp-widgets @@ -77,7 +77,7 @@ blinker==1.9.0 # via # -c envs/dev.env.txt # flask -cachetools==5.5.1 +cachetools==5.5.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -113,7 +113,7 @@ contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via # -c envs/dev.env.txt # quimb @@ -141,11 +141,11 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.12 +debugpy==1.8.13 # via # -c envs/dev.env.txt # ipykernel -decorator==5.1.1 +decorator==5.2.1 # via # -c envs/dev.env.txt # ipython @@ -225,7 +225,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.32.0 +ipython==8.33.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -350,7 +350,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -360,7 +360,7 @@ matplotlib-inline==0.1.7 # -c envs/dev.env.txt # ipykernel # ipython -mistune==3.1.1 +mistune==3.1.2 # via # -c envs/dev.env.txt # nbconvert @@ -372,7 +372,7 @@ mypy-extensions==1.0.0 # via # -c envs/dev.env.txt # black -narwhals==1.25.2 +narwhals==1.29.0 # via # -c envs/dev.env.txt # plotly @@ -492,7 +492,7 @@ protobuf==5.29.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.1.1 +psutil==7.0.0 # via # -c envs/dev.env.txt # ipykernel @@ -576,11 +576,11 @@ qref==0.9.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -613,12 +613,12 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.22.3 +rpds-py==0.23.1 # via # -c envs/dev.env.txt # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # -c envs/dev.env.txt # cirq-core diff --git a/dev_tools/requirements/envs/pylint.env.txt b/dev_tools/requirements/envs/pylint.env.txt index dd89ad53e..187ea597a 100644 --- a/dev_tools/requirements/envs/pylint.env.txt +++ b/dev_tools/requirements/envs/pylint.env.txt @@ -21,7 +21,7 @@ anyio==4.8.0 # -c envs/dev.env.txt # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via # -c envs/dev.env.txt # qsharp-widgets @@ -90,7 +90,7 @@ blinker==1.9.0 # via # -c envs/dev.env.txt # flask -cachetools==5.5.1 +cachetools==5.5.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -126,7 +126,7 @@ contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via # -c envs/dev.env.txt # quimb @@ -154,11 +154,11 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.12 +debugpy==1.8.13 # via # -c envs/dev.env.txt # ipykernel -decorator==5.1.1 +decorator==5.2.1 # via # -c envs/dev.env.txt # ipython @@ -228,7 +228,7 @@ h11==0.14.0 # via # -c envs/dev.env.txt # httpcore -h5py==3.12.1 +h5py==3.13.0 # via # -c envs/dev.env.txt # openfermion @@ -264,7 +264,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.32.0 +ipython==8.33.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -288,11 +288,11 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jax==0.5.0 +jax==0.4.38 # via # -c envs/dev.env.txt # openfermion -jaxlib==0.5.0 +jaxlib==0.4.38 # via # -c envs/dev.env.txt # jax @@ -400,7 +400,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -415,7 +415,7 @@ mccabe==0.7.0 # via # -c envs/dev.env.txt # pylint -mistune==3.1.1 +mistune==3.1.2 # via # -c envs/dev.env.txt # nbconvert @@ -428,7 +428,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -narwhals==1.25.2 +narwhals==1.29.0 # via # -c envs/dev.env.txt # plotly @@ -495,7 +495,7 @@ numpy==1.26.4 # pyzx # quimb # scipy -openfermion[resources]==1.6.1 +openfermion[resources]==1.7.0 # via # -c envs/dev.env.txt # -r deps/pylint.txt @@ -568,7 +568,7 @@ protobuf==5.29.3 # -c envs/dev.env.txt # -r deps/runtime.txt # tensorflow-docs -psutil==6.1.1 +psutil==7.0.0 # via # -c envs/dev.env.txt # ipykernel @@ -631,7 +631,7 @@ pyscf==2.8.0 # via # -c envs/dev.env.txt # openfermion -pytest==8.3.4 +pytest==8.3.5 # via # -c envs/dev.env.txt # -r deps/pylint.txt @@ -670,11 +670,11 @@ qref==0.9.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -709,12 +709,12 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.22.3 +rpds-py==0.23.1 # via # -c envs/dev.env.txt # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # -c envs/dev.env.txt # ase diff --git a/dev_tools/requirements/envs/pytest.env.txt b/dev_tools/requirements/envs/pytest.env.txt index 3ddd3c5e7..045b53072 100644 --- a/dev_tools/requirements/envs/pytest.env.txt +++ b/dev_tools/requirements/envs/pytest.env.txt @@ -13,7 +13,7 @@ anyio==4.8.0 # -c envs/dev.env.txt # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via # -c envs/dev.env.txt # qsharp-widgets @@ -73,7 +73,7 @@ blinker==1.9.0 # via # -c envs/dev.env.txt # flask -cachetools==5.5.1 +cachetools==5.5.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -109,11 +109,11 @@ contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via # -c envs/dev.env.txt # quimb -coverage[toml]==7.6.10 +coverage[toml]==7.6.12 # via # -c envs/dev.env.txt # pytest-cov @@ -141,11 +141,11 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.12 +debugpy==1.8.13 # via # -c envs/dev.env.txt # ipykernel -decorator==5.1.1 +decorator==5.2.1 # via # -c envs/dev.env.txt # ipython @@ -211,7 +211,7 @@ h11==0.14.0 # via # -c envs/dev.env.txt # httpcore -h5py==3.12.1 +h5py==3.13.0 # via # -c envs/dev.env.txt # openfermion @@ -244,7 +244,7 @@ ipykernel==6.29.5 # -c envs/dev.env.txt # -r deps/pytest.txt # jupyterlab -ipython==8.32.0 +ipython==8.33.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -264,11 +264,11 @@ itsdangerous==2.2.0 # via # -c envs/dev.env.txt # flask -jax==0.5.0 +jax==0.4.38 # via # -c envs/dev.env.txt # openfermion -jaxlib==0.5.0 +jaxlib==0.4.38 # via # -c envs/dev.env.txt # jax @@ -374,7 +374,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -385,7 +385,7 @@ matplotlib-inline==0.1.7 # -c envs/dev.env.txt # ipykernel # ipython -mistune==3.1.1 +mistune==3.1.2 # via # -c envs/dev.env.txt # nbconvert @@ -398,7 +398,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -narwhals==1.25.2 +narwhals==1.29.0 # via # -c envs/dev.env.txt # plotly @@ -464,7 +464,7 @@ numpy==1.26.4 # pyzx # quimb # scipy -openfermion[resources]==1.6.1 +openfermion[resources]==1.7.0 # via # -c envs/dev.env.txt # -r deps/pytest.txt @@ -534,7 +534,7 @@ protobuf==5.29.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.1.1 +psutil==7.0.0 # via # -c envs/dev.env.txt # ipykernel @@ -592,7 +592,7 @@ pyscf==2.8.0 # via # -c envs/dev.env.txt # openfermion -pytest==8.3.4 +pytest==8.3.5 # via # -c envs/dev.env.txt # -r deps/pytest.txt @@ -645,11 +645,11 @@ qref==0.9.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -683,12 +683,12 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.22.3 +rpds-py==0.23.1 # via # -c envs/dev.env.txt # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # -c envs/dev.env.txt # ase diff --git a/dev_tools/requirements/envs/runtime.env.txt b/dev_tools/requirements/envs/runtime.env.txt index 7f55987f0..4e2896374 100644 --- a/dev_tools/requirements/envs/runtime.env.txt +++ b/dev_tools/requirements/envs/runtime.env.txt @@ -13,7 +13,7 @@ anyio==4.8.0 # -c envs/dev.env.txt # httpx # jupyter-server -anywidget==0.9.13 +anywidget==0.9.15 # via # -c envs/dev.env.txt # qsharp-widgets @@ -69,7 +69,7 @@ blinker==1.9.0 # via # -c envs/dev.env.txt # flask -cachetools==5.5.1 +cachetools==5.5.2 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -104,7 +104,7 @@ contourpy==1.3.1 # via # -c envs/dev.env.txt # matplotlib -cotengra==0.7.0 +cotengra==0.7.1 # via # -c envs/dev.env.txt # quimb @@ -132,11 +132,11 @@ dash-table==5.0.0 # via # -c envs/dev.env.txt # dash -debugpy==1.8.12 +debugpy==1.8.13 # via # -c envs/dev.env.txt # ipykernel -decorator==5.1.1 +decorator==5.2.1 # via # -c envs/dev.env.txt # ipython @@ -212,7 +212,7 @@ ipykernel==6.29.5 # via # -c envs/dev.env.txt # jupyterlab -ipython==8.32.0 +ipython==8.33.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -333,7 +333,7 @@ markupsafe==3.0.2 # jinja2 # nbconvert # werkzeug -matplotlib==3.10.0 +matplotlib==3.10.1 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -343,7 +343,7 @@ matplotlib-inline==0.1.7 # -c envs/dev.env.txt # ipykernel # ipython -mistune==3.1.1 +mistune==3.1.2 # via # -c envs/dev.env.txt # nbconvert @@ -351,7 +351,7 @@ mpmath==1.3.0 # via # -c envs/dev.env.txt # sympy -narwhals==1.25.2 +narwhals==1.29.0 # via # -c envs/dev.env.txt # plotly @@ -465,7 +465,7 @@ protobuf==5.29.3 # via # -c envs/dev.env.txt # -r deps/runtime.txt -psutil==6.1.1 +psutil==7.0.0 # via # -c envs/dev.env.txt # ipykernel @@ -549,11 +549,11 @@ qref==0.9.0 # -c envs/dev.env.txt # -r deps/runtime.txt # bartiq -qsharp==1.13.2 +qsharp==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt -qsharp-widgets==1.13.2 +qsharp-widgets==1.14.0 # via # -c envs/dev.env.txt # -r deps/runtime.txt @@ -586,12 +586,12 @@ rfc3986-validator==0.1.1 # -c envs/dev.env.txt # jsonschema # jupyter-events -rpds-py==0.22.3 +rpds-py==0.23.1 # via # -c envs/dev.env.txt # jsonschema # referencing -scipy==1.15.1 +scipy==1.15.2 # via # -c envs/dev.env.txt # cirq-core diff --git a/qualtran/bloqs/cryptography/ecc/ec_add.py b/qualtran/bloqs/cryptography/ecc/ec_add.py index 0c5d4109a..0401890bd 100644 --- a/qualtran/bloqs/cryptography/ecc/ec_add.py +++ b/qualtran/bloqs/cryptography/ecc/ec_add.py @@ -23,6 +23,7 @@ bloq_example, BloqBuilder, BloqDocSpec, + CtrlSpec, DecomposeTypeError, QBit, QMontgomeryUInt, @@ -32,8 +33,8 @@ Soquet, SoquetT, ) -from qualtran.bloqs.arithmetic.comparison import Equals -from qualtran.bloqs.basic_gates import CNOT, IntState, Toffoli, ZeroState +from qualtran.bloqs.arithmetic import Equals, Xor +from qualtran.bloqs.basic_gates import CNOT, IntState, Toffoli, XGate, ZeroState from qualtran.bloqs.bookkeeping import Free from qualtran.bloqs.mcmt import MultiAnd, MultiControlX, MultiTargetCNOT from qualtran.bloqs.mod_arithmetic import ( @@ -192,6 +193,12 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: class _ECAddStepTwo(Bloq): r"""Performs step two of the ECAdd bloq. + Includes a bugfix for the scenario where the calculated λ ( = (y - b) / (x - a)) is equivalent + to λ_r ( = 3 * a ^ 2 + c_1 / (2 * b)) and f_1 is wrongfully cleared. We accomplish this by + introducing a new ancilla qubit set by an equals operation on the computed λ and the classical, + pre-computed λ_r. We then control the equals bloq on this ancilla qubit which will only clear + the f_1 flag in the correct situation. Finally, we clear and free the ancilla afterwards. + Args: n: The bitsize of the two registers storing the elliptic curve point mod: The modulus of the field in which we do the addition. @@ -253,10 +260,6 @@ def on_classical_vals( lam = QMontgomeryUInt(self.n, self.mod).montgomery_product( int(y), QMontgomeryUInt(self.n, self.mod).montgomery_inverse(int(x)) ) - # TODO(https://github.com/quantumlib/Qualtran/issues/1461): Fix bug in circuit - # which flips f1 when lam and lam_r are equal. - if lam == lam_r: - f1 = (f1 + 1) % 2 else: lam = 0 return {'f1': f1, 'ctrl': ctrl, 'a': a, 'b': b, 'x': x, 'y': y, 'lam': lam, 'lam_r': lam_r} @@ -296,6 +299,12 @@ def build_composite_bloq( y=y, ) + # Allocate an ancilla qubit that acts as a flag for the rare condition that the + # pre-computed lambda_r is equal to the calculated lambda. This ancilla is used to properly + # clear the f1 qubit when lambda is set to lambda_r. + ancilla = bb.allocate() + z4, lam_r, ancilla = bb.add(Equals(QMontgomeryUInt(self.n)), x=z4, y=lam_r, target=ancilla) + # If ctrl = 1 and x != a: lam = (y - b) / (x - a) % p. z4_split = bb.split(z4) lam_split = bb.split(lam) @@ -323,7 +332,18 @@ def build_composite_bloq( lam = bb.join(lam_split, dtype=QMontgomeryUInt(self.n)) # If lam = lam_r: return f1 = 0. (If not we will flip f1 to 0 at the end iff x_r = y_r = 0). - lam, lam_r, f1 = bb.add(Equals(QMontgomeryUInt(self.n)), x=lam, y=lam_r, target=f1) + # Only flip when lam is set to lam_r. + ancilla, lam, lam_r, f1 = bb.add( + Equals(QMontgomeryUInt(self.n)).controlled(ctrl_spec=CtrlSpec(cvs=0)), + ctrl=ancilla, + x=lam, + y=lam_r, + target=f1, + ) + + # Clear the ancilla bit and free it. + z4, lam_r, ancilla = bb.add(Equals(QMontgomeryUInt(self.n)), x=z4, y=lam_r, target=ancilla) + bb.free(ancilla) # Uncompute the modular multiplication then the modular inversion. x, y = bb.add( @@ -343,7 +363,8 @@ def build_composite_bloq( def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: return { - Equals(QMontgomeryUInt(self.n)): 1, + Equals(QMontgomeryUInt(self.n)): 2, + Equals(QMontgomeryUInt(self.n)).controlled(ctrl_spec=CtrlSpec(cvs=0)): 1, ModSub(QMontgomeryUInt(self.n), mod=self.mod): 1, CModSub(QMontgomeryUInt(self.n), mod=self.mod): 1, KaliskiModInverse(bitsize=self.n, mod=self.mod): 1, @@ -639,6 +660,13 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: class _ECAddStepFive(Bloq): r"""Performs step five of the ECAdd bloq. + Includes a bugfix for the scenario where (a, b) = (x, y) and a - x_r = 0. In this situation, + f_1 is set and f_2 - f_4 is cleared (which means that the ctrl qubit is set). Because a - x_r + is 0, the computed λ is undefined (and with this construction the computed λ will be set to 0), + however the λ is non-zero and should be cleared with λ_r. We accomplish this with a controled + Xor bloq controlled on the ctrl qubit and the condition that the x register (a - x_r) = 0. In + this ase we clear the λ register with λ_r. + Args: n: The bitsize of the two registers storing the elliptic curve point mod: The modulus of the field in which we do the addition. @@ -652,6 +680,7 @@ class _ECAddStepFive(Bloq): will contain the x component of the resultant curve point. y: The y component of the second input elliptic curve point of bitsize `n` in montgomery form, which will contain the y component of the resultant curve point. + lam_r: The precomputed lambda slope used in the addition operation if (a, b) = (x, y) in montgomery form. lam: The lambda slope used in the addition operation. References: @@ -672,6 +701,7 @@ def signature(self) -> 'Signature': Register('b', QMontgomeryUInt(self.n)), Register('x', QMontgomeryUInt(self.n)), Register('y', QMontgomeryUInt(self.n)), + Register('lam_r', QMontgomeryUInt(self.n)), Register('lam', QMontgomeryUInt(self.n), side=Side.LEFT), ] ) @@ -683,6 +713,7 @@ def on_classical_vals( b: 'ClassicalValT', x: 'ClassicalValT', y: 'ClassicalValT', + lam_r: 'ClassicalValT', lam: 'ClassicalValT', ) -> Dict[str, 'ClassicalValT']: if ctrl == 1: @@ -690,7 +721,7 @@ def on_classical_vals( y = (y - b) % self.mod else: x = (x + a) % self.mod - return {'ctrl': ctrl, 'a': a, 'b': b, 'x': x, 'y': y} + return {'ctrl': ctrl, 'a': a, 'b': b, 'x': x, 'y': y, 'lam_r': lam_r} def build_composite_bloq( self, @@ -700,6 +731,7 @@ def build_composite_bloq( b: Soquet, x: Soquet, y: Soquet, + lam_r: Soquet, lam: Soquet, ) -> Dict[str, 'SoquetT']: if is_symbolic(self.n): @@ -729,9 +761,15 @@ def build_composite_bloq( z4_split[i] = ctrls[1] z4 = bb.join(z4_split, dtype=QMontgomeryUInt(self.n)) lam = bb.join(lam_split, dtype=QMontgomeryUInt(self.n)) - # TODO(https://github.com/quantumlib/Qualtran/issues/1461): Fix bug in circuit where lambda - # is not set to 0 before being freed. - bb.add(Free(QMontgomeryUInt(self.n), dirty=True), reg=lam) + + # If the denominator of lambda is 0, lam = lam_r so we clear lam with lam_r. + 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) + bb.add(Free(QMontgomeryUInt(self.n)), reg=lam) # Uncompute multiplication and inverse. x, y = bb.add( @@ -756,9 +794,14 @@ def build_composite_bloq( ctrl, b, y = bb.add(CModSub(QMontgomeryUInt(self.n), mod=self.mod), ctrl=ctrl, x=b, y=y) # Return the output registers. - return {'ctrl': ctrl, 'a': a, 'b': b, 'x': x, 'y': y} + return {'ctrl': ctrl, 'a': a, 'b': b, 'x': x, 'y': y, 'lam_r': lam_r} def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: + clear_lam = ( + Xor(QMontgomeryUInt(self.n)) + .controlled(CtrlSpec(qdtypes=QMontgomeryUInt(self.n), cvs=0)) + .controlled() + ) return { CModSub(QMontgomeryUInt(self.n), mod=self.mod): 1, KaliskiModInverse(bitsize=self.n, mod=self.mod): 1, @@ -771,6 +814,7 @@ 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, + clear_lam: 1, CModNeg(QMontgomeryUInt(self.n), mod=self.mod): 1, } @@ -779,6 +823,16 @@ def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: class _ECAddStepSix(Bloq): r"""Performs step six of the ECAdd bloq. + Include bugfixes for the following scenarios: + 1. f_2 is improperly cleared when ((x, y) = (0, 0) AND b = 0) OR ((a, b) = (0, 0) AND + y = 0). + 2. f_4 is improperly cleared when P_1 = P_2 AND f_4 is set. + + The bugs are fixed respectively by: + 1. Clearing f_2 when x = y = b = 0 OR a = b = y = 0 using an XGate controlled on those + registers. + 2. Moving the CModSub and CModAdd bloqs before the Equals bloq. + Args: n: The bitsize of the two registers storing the elliptic curve point mod: The modulus of the field in which we do the addition. @@ -863,6 +917,11 @@ def build_composite_bloq( f3 = f_ctrls[1] f4 = f_ctrls[2] + # Unset f2 if ((a, b) = (0, 0) AND y = 0) OR ((x, y) = (0, 0) AND b = 0). + mcx = XGate().controlled(CtrlSpec(qdtypes=QMontgomeryUInt(self.n), cvs=[0, 0, 0])) + [a, b, y], f2 = bb.add(mcx, ctrl=[a, b, y], q=f2) + [x, y, b], f2 = bb.add(mcx, ctrl=[x, y, b], q=f2) + # Set (x, y) to (a, b) if f4 is set. a_split = bb.split(a) x_split = bb.split(x) @@ -883,24 +942,6 @@ def build_composite_bloq( b = bb.join(b_split, QMontgomeryUInt(self.n)) y = bb.join(y_split, QMontgomeryUInt(self.n)) - # Unset f4 if (x, y) = (a, b). - ab = bb.join(np.concatenate([bb.split(a), bb.split(b)]), dtype=QMontgomeryUInt(2 * self.n)) - xy = bb.join(np.concatenate([bb.split(x), bb.split(y)]), dtype=QMontgomeryUInt(2 * self.n)) - ab, xy, f4 = bb.add(Equals(QMontgomeryUInt(2 * self.n)), x=ab, y=xy, target=f4) - ab_split = bb.split(ab) - a = bb.join(ab_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) - b = bb.join(ab_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) - xy_split = bb.split(xy) - x = bb.join(xy_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) - y = bb.join(xy_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) - - # Unset f3 if (a, b) = (0, 0). - ab_arr = np.concatenate([bb.split(a), bb.split(b)]) - ab_arr, f3 = bb.add(MultiControlX(cvs=[0] * 2 * self.n), controls=ab_arr, target=f3) - ab_arr = np.split(ab_arr, 2) - a = bb.join(ab_arr[0], dtype=QMontgomeryUInt(self.n)) - b = bb.join(ab_arr[1], dtype=QMontgomeryUInt(self.n)) - # If f1 and f2 are set, subtract a from x and add b to y. ancilla = bb.add(ZeroState()) toff_ctrl = [f1, f2] @@ -923,6 +964,24 @@ def build_composite_bloq( f2 = toff_ctrl[1] bb.add(Free(QBit()), reg=ancilla) + # Unset f4 if (x, y) = (a, b). + ab = bb.join(np.concatenate([bb.split(a), bb.split(b)]), dtype=QMontgomeryUInt(2 * self.n)) + xy = bb.join(np.concatenate([bb.split(x), bb.split(y)]), dtype=QMontgomeryUInt(2 * self.n)) + ab, xy, f4 = bb.add(Equals(QMontgomeryUInt(2 * self.n)), x=ab, y=xy, target=f4) + ab_split = bb.split(ab) + a = bb.join(ab_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) + b = bb.join(ab_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) + xy_split = bb.split(xy) + x = bb.join(xy_split[: int(self.n)], dtype=QMontgomeryUInt(self.n)) + y = bb.join(xy_split[int(self.n) :], dtype=QMontgomeryUInt(self.n)) + + # Unset f3 if (a, b) = (0, 0). + ab_arr = np.concatenate([bb.split(a), bb.split(b)]) + ab_arr, f3 = bb.add(MultiControlX(cvs=[0] * 2 * self.n), controls=ab_arr, target=f3) + ab_arr = np.split(ab_arr, 2) + a = bb.join(ab_arr[0], dtype=QMontgomeryUInt(self.n)) + b = bb.join(ab_arr[1], dtype=QMontgomeryUInt(self.n)) + # Unset f1 and f2 if (x, y) = (0, 0). xy_arr = np.concatenate([bb.split(x), bb.split(y)]) xy_arr, junk, out = bb.add(MultiAnd(cvs=[0] * 2 * self.n), ctrl=xy_arr) @@ -939,33 +998,32 @@ def build_composite_bloq( y = bb.join(xy_arr[1], dtype=QMontgomeryUInt(self.n)) # Free all ancilla qubits in the zero state. - # TODO(https://github.com/quantumlib/Qualtran/issues/1461): Fix bugs in circuit where f1, - # f2, and f4 are freed before being set to 0. - bb.add(Free(QBit(), dirty=True), reg=f1) - bb.add(Free(QBit(), dirty=True), reg=f2) + bb.add(Free(QBit()), reg=f1) + bb.add(Free(QBit()), reg=f2) bb.add(Free(QBit()), reg=f3) - bb.add(Free(QBit(), dirty=True), reg=f4) + bb.add(Free(QBit()), reg=f4) bb.add(Free(QBit()), reg=ctrl) # Return the output registers. return {'a': a, 'b': b, 'x': x, 'y': y} def build_call_graph(self, ssa: SympySymbolAllocator) -> BloqCountDictT: - cvs: Union[list[int], HasLength] + cvs2: Union[list[int], HasLength] if isinstance(self.n, int): - cvs = [0] * 2 * self.n + cvs2 = [0] * 2 * self.n else: - cvs = HasLength(2 * self.n) + cvs2 = HasLength(2 * self.n) return { - MultiControlX(cvs=cvs): 1, + MultiControlX(cvs=cvs2): 1, + XGate().controlled(CtrlSpec(qdtypes=QMontgomeryUInt(self.n), cvs=[0, 0, 0])): 2, MultiControlX(cvs=[0] * 3): 1, CModSub(QMontgomeryUInt(self.n), mod=self.mod): 1, CModAdd(QMontgomeryUInt(self.n), mod=self.mod): 1, Toffoli(): 2 * self.n + 4, Equals(QMontgomeryUInt(2 * self.n)): 1, - MultiAnd(cvs=cvs): 1, + MultiAnd(cvs=cvs2): 1, MultiTargetCNOT(2): 1, - MultiAnd(cvs=cvs).adjoint(): 1, + MultiAnd(cvs=cvs2).adjoint(): 1, } @@ -1044,13 +1102,14 @@ def build_composite_bloq( x, y, lam = bb.add( _ECAddStepFour(n=self.n, mod=self.mod, window_size=self.window_size), x=x, y=y, lam=lam ) - ctrl, a, b, x, y = bb.add( + ctrl, a, b, x, y, lam_r = bb.add( _ECAddStepFive(n=self.n, mod=self.mod, window_size=self.window_size), ctrl=ctrl, a=a, b=b, x=x, y=y, + lam_r=lam_r, lam=lam, ) a, b, x, y = bb.add( diff --git a/qualtran/bloqs/cryptography/ecc/ec_add_test.py b/qualtran/bloqs/cryptography/ecc/ec_add_test.py index 23839bc10..8614332d8 100644 --- a/qualtran/bloqs/cryptography/ecc/ec_add_test.py +++ b/qualtran/bloqs/cryptography/ecc/ec_add_test.py @@ -111,6 +111,7 @@ def test_ec_add_steps_classical_fast(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) ret2 = bloq.decompose_bloq().call_classically( @@ -119,6 +120,7 @@ def test_ec_add_steps_classical_fast(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) assert ret1 == ret2 @@ -129,6 +131,7 @@ def test_ec_add_steps_classical_fast(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) bloq = _ECAddStepSix(n=n, mod=p) @@ -252,6 +255,7 @@ def test_ec_add_steps_classical(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) ret2 = bloq.decompose_bloq().call_classically( @@ -260,6 +264,7 @@ def test_ec_add_steps_classical(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) assert ret1 == ret2 @@ -270,6 +275,7 @@ def test_ec_add_steps_classical(n, m, a, b, x, y): b=step_3['b'], x=step_4['x'], y=step_4['y'], + lam_r=step_2['lam_r'], lam=step_4['lam'], ) bloq = _ECAddStepSix(n=n, mod=p) @@ -417,12 +423,13 @@ def test_ec_add_symbolic_cost(): # Litinski 2023 https://arxiv.org/abs/2306.08585 # Based on the counts from Figures 3, 5, and 8 the toffoli count for ECAdd is 126.5n^2 + 189n. - # The following formula is 126.5n^2 + 195.5n - 31. We account for the discrepancy in the + # The following formula is 126.5n^2 + 215.5n - 34. We account for the discrepancy in the # coefficient of n by a reduction in the toffoli cost of Montgomery ModMult, an increase in the # toffoli cost for Kaliski Mod Inverse, n extra toffolis in ModNeg, 2n extra toffolis to do n - # 3-controlled toffolis in step 2. The expression is written with rationals because sympy - # comparison fails with floats. - assert total_toff == sympy.Rational(253, 2) * n**2 + sympy.Rational(407, 2) * n - 31 + # 3-controlled toffolis in step 2, and a few extra gates added to fix bugs found in the circuit + # (see class docstrings). The expression is written with rationals because sympy comparison + # fails with floats. + assert total_toff == sympy.Rational(253, 2) * n**2 + sympy.Rational(431, 2) * n - 34 def test_ec_add(bloq_autotester):