diff --git a/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.ipynb b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.ipynb new file mode 100644 index 000000000..5059b4c37 --- /dev/null +++ b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.ipynb @@ -0,0 +1,1715 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Solving Elliptic Curve Discrete Logarithm Problem with Shor's Algorithm\n", + "\n", + "## 1. Introduction and Problem Statement\n", + "\n", + "The **Elliptic Curve Discrete Logarithm Problem (ECDLP)** is a fundamental cryptographic challenge that underlies the security of elliptic curve cryptography (ECC). While classical algorithms require exponential time to solve ECDLP, Shor's quantum algorithm can solve it efficiently in polynomial time.\n", + "\n", + "\n", + "### Elliptic Curve Definition\n", + "\n", + "An elliptic curve is a special type of mathematical curve defined by a cubic equation. For our purposes, we can think of it as the set of points $(x, y)$ that satisfy the **Weierstrass equation**:\n", + "\n", + "$$E : y^2 = x^3 + ax + b$$\n", + "\n", + "where $a$ and $b$ are constants that define the specific shape of the curve.\n", + "\n", + "**Key Properties**:\n", + "\n", + "- **Smooth curve**: The curve has no sharp corners, breaks, or self-intersections (this requires $4a^3 + 27b^2 \\neq 0$)\n", + "- **Symmetric**: The curve is symmetric about the x-axis - if $(x, y)$ is on the curve, then $(x, -y)$ is also on the curve\n", + "- **Point at infinity**: We add a special \"point at infinity\" denoted $\\mathcal{O}$ that serves as the identity element for point addition , as will be explained in the next section. \n", + "\n", + "\n", + "**Cryptographic Context**:\n", + "\n", + "For cryptographic applications like Bitcoin's digital signatures, we work with elliptic curves over **finite fields** $\\mathbb{F}_p$ where $p$ is a large prime number. Instead of the smooth curves we might visualize over real numbers, we get a discrete set of points:\n", + "\n", + "$$E(\\mathbb{F}_p) = \\{(x, y) \\in \\mathbb{F}_p \\times \\mathbb{F}_p \\mid y^2 = x^3 + ax + b\\} \\cup \\{\\mathcal{O}\\}$$\n", + "\n", + "\n", + "The crucial property for cryptography is that these points form a **group** under a special addition operation. This means:\n", + "- You can **\"add\"** any two points on the curve to get another point on the curve\n", + "- There's an identity element ($\\mathcal{O}$) where $P + \\mathcal{O} = P$ for any point $P$\n", + "- Every point has an inverse\n", + "- Addition is associative: $(P + Q) + R = P + (Q + R)$\n", + "\n", + "\n", + "### How Elliptic Curve Point Addition Works\n", + "\n", + "\n", + "**The elliptic curve \"addition\" operation geometric interpretation**: To add two points $P$ and $Q$ on the curve, draw a straight line through them. This line will intersect the elliptic curve at exactly one more point $R'$. The sum $P + Q$ is then defined as the reflection of $R'$ across the x-axis.\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "***Figure 1**: Visualized representation of the addition operation on the elliptic curve $y^2 = x^3 - 5x + 6$ over the real field.*\n", + "\n", + "**Algebraic Process**:\n", + "This geometric process translates into explicit coordinate formulas:\n", + "1. Calculate a slope $\\lambda$ based on the input points\n", + "2. Use $\\lambda$ to compute the x-coordinate of the result: $x_3 = \\lambda^2 - x_1 - x_2$\n", + "3. Use $\\lambda$ and $x_3$ to compute the y-coordinate: $y_3 = \\lambda(x_1 - x_3) - y_1$\n", + "\n", + "**Elliptic Curve Addition Cases**:\n", + "In general, elliptic curve point addition must handle several cases:\n", + "1. **Generic case**: Two distinct points $P \\neq Q$ where $P \\neq -Q$\n", + " - Slope: $\\lambda = \\frac{y_Q - y_P}{x_Q - x_P} \\pmod{p}$\n", + "2. **Point doubling**: Adding a point to itself $P + P$\n", + " - Slope: $\\lambda = \\frac{3x_P^2 + a}{2y_P} \\pmod{p}$\n", + "3. **Inverse points**: $P + (-P) = \\mathcal{O}$ (point at infinity)\n", + "4. **Adding point at infinity**: $P + \\mathcal{O} = P$\n", + "\n", + "**Group Property**:\n", + "This addition rule ensures that the sum of any two points on the curve is always another point on the curve, maintaining the group property essential.\n", + "\n", + "\n", + "\n", + "### The Elliptic Curve Discrete Logarithm Problem (ECDLP)\n", + "\n", + "*
Formally, an instance of the **Elliptic Curve Discrete Logarithm Problem (ECDLP)** is defined as follows: Let $G \\in E(\\mathbb{F}_p)$ be a fixed and publicly known generator of a cyclic subgroup of $E(\\mathbb{F}_p)$ with known order $\\text{ord}(G) = r$. Let $P \\in \\langle G \\rangle$ be a fixed and publicly known element in the subgroup generated by $G$. **The problem is to find the unique integer $l \\in \\{1, 2, \\ldots, r\\}$, called the discrete logarithm, such that $P = l \\cdot G$.**
*\n", + "\n", + "\n", + "\n", + "\n", + "**Why \"Logarithm\" for Point Addition?**\n", + "\n", + "The term **\"discrete logarithm\"** might seem confusing at first in the context of elliptic curves, since the group operation here is **point addition**, not multiplication. However, the **elliptic curve discrete logarithm problem (ECDLP)** is a **special case of the general discrete logarithm problem (DLP)** (see the [\"discrete log\" notebook](https://github.com/Classiq/classiq-library/blob/main/algorithms/number_theory_and_cryptography/discrete_log/discrete_log.ipynb)), where the group happens to be defined by elliptic curve point addition rather than modular multiplication.\n", + "\n", + "In the Discrete Logarithm Problem, the group operation is modular multiplication, and exponentiation refers to repeated multiplication . In the elliptic curve setting, the scalar multiplication $l \\cdot G$ refers to adding the point $G$ to itself $l$ times.\n", + "\n", + "Below is a comparison of the two settings:\n", + "\n", + "| | **Discrete Logarithm Problem (DLP)** | **Elliptic Curve DLP (ECDLP)** |\n", + "|---------------------------|-----------------------------------------------|-----------------------------------------------|\n", + "| **Group operation** | Modular multiplication | Modular point addition |\n", + "| **Expression** | Modular exponentiation: $g^l = h$ | Modular scalar multiplication: $l \\cdot G = P_{target}$ |\n", + "| **Goal** | Find $l$ given $g$ and $h$ | Find $l$ given $G$ and $P_{target}$ |\n", + "\n", + "\n", + "In both cases:\n", + "- A **generator** element ($g$ or $G$) is known\n", + "- A **target** element ($h$ or $P_{target}$) is known\n", + "- The task is to find the **exponent or scalar** $l$ such that the group operation applied to the generator yields the target\n", + "\n", + "The term \"logarithm\" is used by analogy: just as logarithms solve for exponents in multiplicative groups, the discrete logarithm solves for the scalar in additive or elliptic curve groups.\n", + "\n", + "\n", + "\n", + "### Real-World Impact and Applications\n", + "\n", + "**Cryptographic Applications**:\n", + "\n", + "\n", + "- **Digital Signatures**: Power cryptocurrency systems like Bitcoin and Ethereum, enabling secure ownership proofs without revealing private keys \n", + "- **TLS/SSL Certificates**: Use ECC-based key exchange (e.g., ECDHE) to secure HTTPS traffic across the internet \n", + "- **Mobile Communications**: Employ protocols such as ECDSA and ECIES for device authentication and secure messaging in 4G/5G networks \n", + "- **Lightweight Cryptography**: ECC is well-suited for resource-constrained environments, and is used in protocols like DTLS and TLS-PSK for IoT security \n", + "- **Secure Messaging and Encryption Standards**: ECC forms the basis of cryptographic protocols in standards like Suite B, used in high-security communications\n", + "\n", + "\n", + "**Why ECC is Preferred Over RSA**:\n", + "\n", + "Elliptic curve cryptography offers the same security as RSA but with much smaller key sizes:\n", + "- **ECC-256** provides security equivalent to **RSA-3072**\n", + "- Smaller keys mean faster computations and less storage\n", + "- Critical for resource-constrained devices like smartphones and IoT sensors\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "### Our Specific Example $E(\\mathbb{F}_7)$\n", + "\n", + "We'll work with a concrete example on a small finite field to demonstrate the concepts, following the approach outlined in Roetteler et al. [[1]](#Roetteler). Our specific example will demonstrate elliptic curve operations over points $(x, y) \\in \\mathbb{F}_7 \\times \\mathbb{F}_7$:\n", + "\n", + "**Elliptic Curve**: $y^2 = x^3 + 5x + 4 \\pmod{7}$\n", + "\n", + "\n", + "**Problem**: Find the discrete logarithm $l$ such that:\n", + "$$l \\cdot G = P_{target}$$\n", + "\n", + "Where:\n", + "- **Generator point** $G = [0, 5]$ \n", + "- **Target point** $P_{target} = [0, 2]$\n", + "\n", + "This means we need to find how many times we must add $G$ to itself to get $P_{target}$.\n", + "\n", + "**Solution**: $l = 4$, since $4 \\cdot [0, 5] = [0, 2]$\n", + "\n", + "Let's verify by computing the multiples of $G = [0, 5]$:\n", + "- $1 \\cdot G = [0, 5]$\n", + "- $2 \\cdot G = [0, 5] + [0, 5] = [2, 1]$ (point doubling)\n", + "- $3 \\cdot G = [2, 1] + [0, 5] = [2, 6]$\n", + "- $4 \\cdot G = [2, 6] + [0, 5] = [0, 2]$ ✓\n", + "\n", + "Therefore, $l = 4$ is indeed the discrete logarithm we're seeking.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "**Curve Properties**\n", + "\n", + "The elliptic curve $y^2 = x^3 + 5x + 4 \\pmod{7}$ contains the following points:\n", + "- $[0, 2], [0, 5], [2, 1], [2, 6], [3, 2], [3, 5], [4, 2], [4, 5], [5, 0]$\n", + "- Plus the point at infinity $\\mathcal{O}$\n", + "\n", + "**Generator $G = [0, 5]$ properties**:\n", + "\n", + "- Multiples: $1 \\cdot G = [0, 5]$, $2 \\cdot G = [2, 1]$, $3 \\cdot G = [2, 6]$, $4 \\cdot G = [0, 2]$, $5 \\cdot G = \\mathcal{O}$\n", + "- Order: $r=5$ (since $5 \\cdot G = \\mathcal{O}$)\n", + " \n", + "***Note:** The order $r$ is the smallest positive integer such that $r \\cdot G = \\mathcal{O}$ (point at infinity).* " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "# Define our elliptic curve and problem parameters\n", + "class EllipticCurve:\n", + " def __init__(self, p, a, b):\n", + " \"\"\"\n", + " Represents an elliptic curve of the form y^2 = x^3 + a*x + b (mod p)\n", + " \"\"\"\n", + " self.p = p\n", + " self.a = a\n", + " self.b = b\n", + "\n", + " def __repr__(self):\n", + " return f\"EllipticCurve(p={self.p}, a={self.a}, b={self.b})\"\n", + "\n", + "\n", + "# Problem parameters for our specific ECDLP instance\n", + "CURVE = EllipticCurve(p=7, a=5, b=4)\n", + "GENERATOR_G = [0, 5]\n", + "GENERATOR_ORDER = 5\n", + "INITIAL_POINT = [4, 2]\n", + "TARGET_POINT = [0, 2]" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## 2. Shor's Algorithm for ECDLP\n", + "\n", + "\n", + "Shor's quantum algorithm [[2]](#Shor) provides an efficient way to solve the discrete logarithm problem that would be intractable for classical computers. The algorithm proceeds as follows:\n", + "\n", + "### Algorithm Overview\n", + "\n", + "1. **Superposition Preparation**: Create two quantum registers in a superposition of all possible integers between 0 and a large number\n", + "2. **Periodic Function Evaluation**: Apply a periodic function that takes the two registers as input and computes the output in an auxiliary register\n", + "3. **Period Extraction**: Apply an inverse Quantum Fourier Transform to reveal the hidden period of the function\n", + "\n", + "Shor's algorithm for solving the ECDLP is analogous to Shor's algorithm for integer factorization. Both prepare large superpositions of inputs, feed them to a periodic function, and exploit the QFT routine to reveal hidden periodicities that allow efficient computation of the discrete logarithm (see the [\"discrete log\" notebook](https://github.com/Classiq/classiq-library/blob/main/algorithms/number_theory_and_cryptography/discrete_log/discrete_log.ipynb)).\n", + "\n", + "#### The Periodic Function for Elliptic Curve\n", + "\n", + "The key function evaluated in quantum superposition is:\n", + "$$f(x_1, x_2) = x_1 \\cdot G - x_2 \\cdot P_{target} = (x_1 - x_2 \\cdot l) \\cdot G$$\n", + "\n", + "where the last equality holds by the definition of the discrete logarithm $l$ (since $P_{target} = l \\cdot G$).\n", + "\n", + "\n", + "The function $f$ exhibits periodicity in both variables:\n", + "$$\\forall r, \\quad f(x_1 + r \\cdot l, x_2 + r) = f(x_1, x_2)$$\n", + "\n", + "where $r$ is the order of the generator $G$. \n", + "\n", + "When the quantum measurement finds inputs where $f(x_1, x_2) = \\mathcal{O}$, the quantum Fourier transform extracts this period structure, revealing relationships that allow us to solve for the discrete logarithm $l$.\n", + "\n", + "\n", + "\n", + "*In our quantum implementation, we actually evaluate $P_0 + x_1 \\cdot G - x_2 \\cdot P_{target}$ where $P_0$ serves as an auxiliary starting point that doesn't affect the final discrete logarithm but helps avoid certain edge cases in the quantum arithmetic.*\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "### Quantum Oracle Function Implementation \n", + "\n", + "The core of our implementation is the `shor_ecdlp` quantum function that orchestrates the entire algorithm. \n", + "\n", + "It implements the function\n", + "$$\n", + "|x_1\\rangle|x_2\\rangle|0\\rangle \\rightarrow |x_1\\rangle|x_2\\rangle| P_0 + x_1 \\cdot G - x_2 \\cdot P_{target}\\rangle. \n", + "$$\n", + "\n", + "#### Expected Quantum Flow\n", + "\n", + "1. **Initialization** : All variables start in $|0\\rangle$ state\n", + "2. **State Preparation** : Set initial elliptic curve point \n", + "3. **Superposition Creation** : Apply transformation to create superposition over the possible values of $x_1$ and $x_2$ \n", + "4. **Quantum Arithmetic** : Perform elliptic curve computation $P_0 + x_1 \\cdot G - x_2 \\cdot P_{target}$ in superposition\n", + "5. **Period Extraction** : Apply inverse QFT to extract period information\n", + "\n", + "**Quantum Struct Approach**:\n", + "We use a `QStruct` called `EllipticCurvePoint` to group the x and y coordinates together, making the code more organized and mathematically intuitive. \n", + "\n", + "**Quantum Variables:**\n", + "| Variable | Purpose |\n", + "|----------|---------|\n", + "| `x1` | First quantum register for period finding (ranges 0-7) |\n", + "| `x2` | Second quantum register for period finding (ranges 0-7) |\n", + "| `ecp` | Quantum elliptic curve point during computation |\n", + "\n", + "**Classical Parameters:**\n", + "| Variable | Purpose |\n", + "|----------|---------|\n", + "| `P_0` | Starting point |\n", + "| `G` | Generator point |\n", + "| `P_target` | Target point |" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "*Note: In this case the order is not a power of 2. So instead of creating the entire uniform distribution on the `x1`, `x2` variables, we load them with the uniform superposition of only the first `#GENERATOR_ORDER` states (see the [\"discrete log\" notebook](https://github.com/Classiq/classiq-library/blob/main/algorithms/number_theory_and_cryptography/discrete_log/discrete_log.ipynb) for more examples).*\n", + "\n", + "*We do that using the `prepare_uniform_trimmed_state` library function, which efficiently prepares such a state.*" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "from classiq import *\n", + "from classiq.qmod.symbolic import ceiling, log\n", + "\n", + "\n", + "class EllipticCurvePoint(QStruct):\n", + " x: QNum[CURVE.p.bit_length()]\n", + " y: QNum[CURVE.p.bit_length()]\n", + "\n", + "\n", + "@qfunc\n", + "def shor_ecdlp(\n", + " x1: Output[QNum], # first quantum variable for period finding\n", + " x2: Output[QNum], # second quantum variable for period finding\n", + " ecp: Output[\n", + " EllipticCurvePoint\n", + " ], # third quantum variable for elliptic curve point coordinates\n", + " P_0: list[int], # starting point - classical\n", + " G: list[int], # generator point - classical\n", + " P_target: list[int], # target point - classical\n", + ") -> None:\n", + " \"\"\"\n", + " Main quantum function implementing Shor's algorithm for ECDLP.\n", + " \"\"\"\n", + " # Step 1: Allocate quantum resources to variables\n", + " var_len = GENERATOR_ORDER.bit_length()\n", + " allocate(var_len, False, var_len, x1)\n", + " allocate(var_len, False, var_len, x2)\n", + "\n", + " # Step 2: Initialize ecp to P_0\n", + " allocate(ecp)\n", + " ecp.x ^= P_0[0]\n", + " ecp.y ^= P_0[1]\n", + "\n", + " # Step 3: Create superposition on x1 and x2\n", + " hadamard_transform(x1)\n", + " hadamard_transform(x2)\n", + "\n", + " # Step 4: Quantum elliptic curve arithmetic in superposition\n", + " # First: ecp = P_0 + x1·G\n", + " ec_scalar_mult_add(ecp, x1, G, CURVE.p, CURVE.a, CURVE.b)\n", + "\n", + " # Second: ecp = P_0 + x1·G - x2·P_target\n", + " neg_target = [P_target[0], (-P_target[1]) % CURVE.p]\n", + " ec_scalar_mult_add(ecp, x2, neg_target, CURVE.p, CURVE.a, CURVE.b)\n", + "\n", + " # Step 5: Inverse Quantum Fourier Transform for period extraction\n", + " invert(lambda: qft(x1))\n", + " invert(lambda: qft(x2))" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## 3. Quantum Elliptic Curve Addition\n", + "\n", + "### Algorithm Hierarchy Overview\n", + "\n", + "The `shor_ecdlp` function orchestrates Shor's algorithm, but its computational core is the `ec_scalar_mult_add` function, which performs quantum scalar multiplication in superposition. This function computes the expression $P_0 + k \\cdot G$ where $k$ is in quantum superposition, enabling the algorithm to evaluate all possible scalar values simultaneously.\n", + "\n", + "\n", + "### Quantum Scalar Multiplication (`ec_scalar_mult_add`)\n", + "\n", + "Our implementation uses an efficient hybrid classical-quantum approach. We precompute all the powers-of-two multiples of the base point $P$ classically: $P, 2P, 4P, 8P, \\ldots, 2^{n-1}P$. Then we compute the scalar multiple using controlled additions of these precomputed points following the binary representation of the scalar.\n", + "\n", + "**Algorithm**: For scalar $k = \\sum_{i=0}^{n-1} k_i 2^i$ with $k_i \\in \\{0, 1\\}$:\n", + "\n", + "$$k \\cdot P = \\sum_{i=0}^{n-1} k_i 2^i P = \\sum_{i=0}^{n-1} k_i (2^i P)$$\n", + "\n", + "**Implementation Steps**:\n", + "1. **Classical Preprocessing**: Compute powers $P, 2P, 4P, \\ldots$ classically\n", + "2. **Quantum Control**: For each bit $k_i$, perform controlled addition of $2^i P$ to the accumulator\n", + "\n", + "When $k$ is in superposition $|k\\rangle$, all possible scalar values are processed simultaneously:\n", + "$$|k\\rangle|(x,y)\\rangle \\rightarrow |k\\rangle|(x,y) + k \\cdot P\\rangle$$\n", + "\n", + "All doubling operations happen classically through the `ell_double_classical` function, leaving only controlled point additions for the in-place elliptic curve point addition quantum function `ec_point_add`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "@qperm\n", + "def ec_scalar_mult_add(\n", + " ecp: EllipticCurvePoint, # elliptic curve point\n", + " k: QArray[QBit], # scalar in binary representation (LSB to MSB)\n", + " P: list[int], # classical point to multiply [x, y]\n", + " p: int, # prime modulus\n", + " a: int,\n", + " b: int, # curve parameters\n", + ") -> None:\n", + " \"\"\"\n", + " Quantum scalar multiplication: computes k*P and adds to ecp in-place.\n", + " \"\"\"\n", + " n = k.size # Number of bits in scalar k\n", + " current_power = P.copy() # Start with 1·P = P\n", + " # Process each bit of k from LSB (bit 0) to MSB (bit n-1)\n", + " for i in range(n):\n", + " # Controlled point addition: if k[i] = 1, add current_power to accumulator\n", + " control(k[i], lambda: ec_point_add(ecp, current_power, p))\n", + " # Classical update for next iteration: current_power = 2 * current_power\n", + " if i < n - 1: # Don't double after the last iteration\n", + " curve = EllipticCurve(p=p, a=a, b=b)\n", + " current_power = ell_double_classical(current_power, curve)" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "### Classical Point Doubling (`ell_double_classical`)\n", + "\n", + "The doubling of the classically known generator is implemented as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "def ell_double_classical(P, curve):\n", + " \"\"\"\n", + " Classical elliptic curve point doubling for updating powers in ec_scalar_mult_add.\n", + " Returns 2P for a point P on the elliptic curve.\n", + " \"\"\"\n", + " p = curve.p\n", + " x, y = P\n", + " # Slope calculation: s = (3*x² + a) / (2*y) mod p\n", + " numerator = (3 * (x * x % p) + curve.a) % p\n", + " denominator = (2 * y) % p\n", + " s = (numerator * pow(denominator, -1, p)) % p\n", + " # x-coordinate of the result\n", + " xr = (s * s - 2 * x) % p\n", + " # y-coordinate of the result\n", + " yr = (y - s * ((x - xr) % p)) % p\n", + " # Return the result, with y in the standard form\n", + " return [xr, (p - yr) % p]" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "### Quantum Point Addition (`ec_point_add`)\n", + "\n", + "The `ec_point_add` function performs the fundamental operation of adding two elliptic curve points in a quantum-reversible manner.\n", + "\n", + "\n", + "**Our Implementation Simplification**:\n", + "\n", + "Following the approach in [[1]](#Roetteler), our implementation focuses only on the **generic case** where the two points are distinct, not inverses of each other, and neither is the neutral element. These exceptional cases are rare (for large $p$) , except when the accumulation register is initialized to the neutral element.\n", + "\n", + "**Note:** *To avoid edge cases (as described above) through the entire quantum scalar multiplication, we chose to initialize the accumulation elliptic curve point with a non-zero point ($P_0 = [4, 2]$) rather than the neutral element. This strategy does not affect the measurement statistics after the Quantum Fourier Transform, since it only adds a global phase to the final quantum state. We deliberately selected a starting point that lies on the elliptic curve but is not within the subgroup generated by $G$. Additionally, we exploited the fact that the order of $G$ is not a power of 2*\n", + "\n", + "*Disclaimer: This is an engineered example chosen to allow simulation of a small model where edge cases effect the results. In general, such an initialization is not always possible.* \n", + "\n", + "**Algorithm Overview** (Generic Case Only):\n", + "1. **Input**: Quantum point $(x_1, y_1)$ and classical point $G = (G_x, G_y)$\n", + "2. **Slope calculation**: Compute $\\lambda = \\frac{y_1 - G_y}{x_1 - G_x} \\pmod{p}$ using quantum modular inverse\n", + "3. **Result coordinates**: \n", + " - $x_{result}:= x_3 = \\lambda^2 - x_1 - G_x \\pmod{p}$\n", + " - $y_{result}:= y_3 = \\lambda(x_1 - x_3) - y_1 \\pmod{p}$\n", + "4. **Cleanup**: Uncompute auxiliary values to maintain reversibility\n", + "\n", + "\n", + "**Reference**: The `ec_point_add` function is based on **Algorithm 1** from *\"Quantum resource estimates for computing elliptic curve discrete logarithms\"* by Roetteler et al. (2017) [[1]](#Roetteler)\n", + "\n", + "To implement `ec_point_add`, we need a complete library of reversible modular arithmetic operations which we import directly from the Classiq open library.\n", + "\n", + "#### Modular Arithmetic Functions imported from Classiq Open Library - Organized by Category:\n", + "\n", + "*Basic Modular Operations*:\n", + "- `modular_add_inplace` - Modular addition\n", + "- `modular_negate_inplace` - Modular negation\n", + "- `modular_subtract_inplace` - Modular subtraction\n", + "\n", + "*Constant Operations*:\n", + "- `modular_add_constant_inplace` - Modular addition of a classical constant\n", + "\n", + "*Multiplication Operations*:\n", + "- `modular_multiply` - Modular multiplication\n", + "- `modular_square` - Modular squaring\n", + "\n", + "\n", + "\n", + "*In our example, we will first implement modular inversion using a mock quantum function `mock_modular_inverse` based on a classical lookup table specifically for modulo 7, instead of implementing the full quantum modular inversion function `modular_inverse_inplace` (based on \"Kaliski's algorithm\"), which requires significant qubit resources.*\n", + "\n", + "*The above choice is driven by the practical desire to first fully simulate the entire elliptic curve discrete logarithm (ECDLP) algorithm flow within the limits of currently available quantum simulators. The full ECDLP synthesized circuit (including the full `modular_inverse_inplace` implementation) is presented at the end of this notebook.*\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "\n", + "from classiq.qmod.symbolic import subscript\n", + "\n", + "\n", + "@qperm\n", + "def mock_modular_inverse(x: Const[QNum], result: QNum, modulus: int) -> None:\n", + " \"\"\"\n", + " Performs the transformation |x>|0> → |x>|x^(-1) mod modulus> for x in 1..modulus-1.\n", + " This is a mock implementation using controlled operations based on lookup table values.\n", + " If the modular inverse does not exist, sets the 2nd register to 0.\n", + " \"\"\"\n", + " # Generate the lookup table for modular inverses\n", + " inverse_table = lookup_table(\n", + " lambda _x: pow(_x, -1, modulus) if math.gcd(_x, modulus) == 1 else 0, x\n", + " )\n", + " result ^= subscript(inverse_table, x)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "@qperm\n", + "def ec_point_add(\n", + " ecp: EllipticCurvePoint,\n", + " G: list[int], # Classical point coordinates [Gx, Gy] on the curve\n", + " p: int, # Prime modulus\n", + ") -> None:\n", + " \"\"\"\n", + " Performs in-place elliptic curve point addition of a point whose coordinates are\n", + " stored in quantum struct object and a classically known point.\n", + " \"\"\"\n", + "\n", + " n = CURVE.p.bit_length()\n", + " slope = QNum() # aux quantum register for lambda (slope)\n", + " allocate(n, slope)\n", + " t0 = QNum() # aux quantum register for internal arithmetic\n", + " allocate(n, t0)\n", + "\n", + " # Extract classical coordinates\n", + " Gx = G[0] # x2\n", + " Gy = G[1] # y2\n", + "\n", + " # Step 1: # y <-- (y1 - y2) mod p\n", + " modular_add_constant_inplace(p, (-Gy) % p, ecp.y)\n", + "\n", + " # Step 2: # x <-- (x1 - x2) mod p\n", + " modular_add_constant_inplace(p, (-Gx) % p, ecp.x)\n", + "\n", + " # Step 3: # λ <-- (y1 - y2) / (x1 - x2) mod p\n", + " within_apply(\n", + " lambda: mock_modular_inverse(ecp.x, t0, p), # t0 <-- (x1 - x2)^(-1) mod p\n", + " lambda: modular_multiply(p, t0, ecp.y, slope), # λ <-- t0 * (y1 -y2)\n", + " )\n", + "\n", + " # Step 4: y <-- 0\n", + " within_apply(\n", + " lambda: modular_multiply(\n", + " p, slope, ecp.x, t0\n", + " ), # t0 <-- λ * (x1 -x2) = (y1 - y2) = y\n", + " lambda: inplace_xor(t0, ecp.y), # y <-- 0\n", + " )\n", + "\n", + " # Step 5: x <-- x2 - x3\n", + " within_apply(\n", + " lambda: modular_square(p, slope, t0), # t0 = λ²\n", + " lambda: (\n", + " modular_subtract_inplace(p, t0, ecp.x), # x <-- t0 - x = λ² - (x1 - x2)\n", + " modular_negate_inplace(p, ecp.x), # x <-- -x = x1 - x2 - λ²\n", + " modular_add_constant_inplace(\n", + " p, (3 * Gx) % p, ecp.x\n", + " ), # x <-- x1 - x2 - λ² + 3*x2 = x2 - x3\n", + " ),\n", + " )\n", + "\n", + " # Step 6: y <-- y3 + y2\n", + " modular_multiply(p, slope, ecp.x, ecp.y) # y = λ * (x2 - x3) = y3 + y2\n", + "\n", + " # Step 7: λ <-- 0\n", + " t1 = QNum() # aux quantum register for manually uncomputing the slope\n", + " within_apply(\n", + " lambda: mock_modular_inverse(ecp.x, t0, p), # t0 <-- (x2 - x3)^(-1)\n", + " lambda: within_apply(\n", + " lambda: (\n", + " allocate(CURVE.p.bit_length(), t1),\n", + " modular_multiply(p, t0, ecp.y, t1), # t1 <-- (y3 + y2)/(x2 - x3) = λ\n", + " ),\n", + " lambda: inplace_xor(t1, slope), # λ <-- 0\n", + " ),\n", + " )\n", + " free(slope)\n", + "\n", + " # Step 8: Final coordinate adjustments\n", + " modular_add_constant_inplace(p, (-Gy) % p, ecp.y) # y <-- y3 + y2 - y2 = y3!\n", + " modular_negate_inplace(p, ecp.x) # x <-- x3 - x2\n", + " modular_add_constant_inplace(p, Gx, ecp.x) # x <-- x3 - x2 + x2 = x3!" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "**Note:** *To enable reversible in-place point addition, the slope* $\\lambda$ *can be recomputed (as can be observed from step 6 in `ec_point_add`) from the output point* $P_3$ *and the known input* $P_2$ *using the identity:*\n", + "\n", + "$$\n", + "\\frac{y_1 - y_2}{x_1 - x_2} = -\\frac{y_3 + y_2}{x_3 - x_2}\n", + "$$\n", + "\n", + "*This ensures that* $P_1$ *can be safely overwritten with* $P_3$ *while still allowing recovery of* $\\lambda$ *for uncomputing it.*" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "#### Step-by-Step Point Addition Example\n", + "\n", + "Let's see how elliptic curve point addition actually works by computing $[4, 2] + [0, 5]$:\n", + "\n", + "**Step 1: Calculate the slope**\n", + "For two distinct points $P_1 = (x_1, y_1) = (4, 2)$ and $P_2 = (x_2, y_2) = (0, 5)$:\n", + "\n", + "$$\\lambda = \\frac{y_2 - y_1}{x_2 - x_1} = \\frac{5 - 2}{0 - 4} = \\frac{3}{-4} = \\frac{3}{3} = 1 \\pmod{7}$$\n", + "\n", + "Note: $-4 \\equiv 3 \\pmod{7}$, and $3^{-1} \\equiv 5 \\pmod{7}$ since $3 \\times 5 = 15 \\equiv 1 \\pmod{7}$\n", + "\n", + "Actually: $\\lambda = 3 \\times 5 = 15 \\equiv 1 \\pmod{7}$\n", + "\n", + "**Step 2: Calculate the x-coordinate of the result**\n", + "$$x_3 = \\lambda^2 - x_1 - x_2 = 1^2 - 4 - 0 = 1 - 4 = -3 \\equiv 4 \\pmod{7}$$\n", + "\n", + "**Step 3: Calculate the y-coordinate of the result**\n", + "$$y_3 = \\lambda(x_1 - x_3) - y_1 = 1 \\times (4 - 4) - 2 = 0 - 2 = -2 \\equiv 5 \\pmod{7}$$\n", + "\n", + "**Result**: $[4, 2] + [0, 5] = [4, 5]$\n", + "\n", + "\n", + "Lets apply `ec_point_add` to add $P_1$ and $P_2$:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "17", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Synthesizing quantum circuit for ec_point_add...\n", + "Number of qubits: 17\n", + "Program depth: 10928\n", + "Quantum program link: https://platform.classiq.io/circuit/37HaGh1CUEPeQILCn6CknGWMziX\n", + "Executing quantum circuit...\n", + "Execution complete.\n" + ] + } + ], + "source": [ + "# Define the specific points from the notebook example\n", + "P1 = [4, 2] # Starting quantum point (will be modified in-place)\n", + "P2 = [0, 5] # Classical point to add\n", + "expected_result = [4, 5]\n", + "\n", + "\n", + "@qfunc\n", + "def main(\n", + " ecp: Output[EllipticCurvePoint],\n", + ") -> None:\n", + " \"\"\"Main quantum function for testing ec_point_add\"\"\"\n", + " # Initialize elliptic curve point with P1 coordinates\n", + " allocate(ecp)\n", + " ecp.x ^= P1[0] # x = 4\n", + " ecp.y ^= P1[1] # y = 2\n", + "\n", + " # Perform point addition: ecp = P1 + P2 = [4, 2] + [0, 5]\n", + " ec_point_add(ecp, P2, CURVE.p)\n", + "\n", + "\n", + "# Set up optimization constraints\n", + "constraints = Constraints(optimization_parameter=\"width\")\n", + "preferences = Preferences(optimization_level=1, qasm3=True)\n", + "\n", + "# Create and synthesize quantum model\n", + "qmod = create_model(main, constraints=constraints, preferences=preferences)\n", + "\n", + "print(\"Synthesizing quantum circuit for ec_point_add...\")\n", + "qprog_point_add = synthesize(qmod)\n", + "\n", + "# Display circuit information\n", + "print(f\"Number of qubits: {qprog_point_add.data.width}\")\n", + "print(f\"Program depth: {qprog_point_add.transpiled_circuit.depth}\")\n", + "show(qprog_point_add)\n", + "\n", + "# Execute the quantum circuit\n", + "print(\"Executing quantum circuit...\")\n", + "result = execute(qprog_point_add).result()\n", + "print(\"Execution complete.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "18", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Quantum result matches expected manual calculation!\n", + "Verified: [4, 2] + [0, 5] = [4, 5] on curve y² = x³ + 5x + 4 (mod 7)\n" + ] + } + ], + "source": [ + "# Extract quantum results\n", + "quantum_results = result[0].value.parsed_counts[0].state\n", + "ec_point_result = [quantum_results[\"ecp\"][\"x\"], quantum_results[\"ecp\"][\"y\"]]\n", + "# Verify the result\n", + "assert len(quantum_results)\n", + "assert ec_point_result == expected_result\n", + "print(f\"Quantum result matches expected manual calculation!\")\n", + "print(f\"Verified: [4, 2] + [0, 5] = [4, 5] on curve y² = x³ + 5x + 4 (mod 7)\")" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## 4. Execute the Entire Algorithm Flow\n", + "\n", + "We are now ready to synthesize and execute the complete Shor's ECDLP algorithm implemented with the `shor_ecdlp` function. \n" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "#### Main function " + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "@qfunc\n", + "def main(x1: Output[QNum], x2: Output[QNum], ecp: Output[EllipticCurvePoint]) -> None:\n", + " # Call shor_ecdlp with the required parameters\n", + " shor_ecdlp(x1, x2, ecp, INITIAL_POINT, GENERATOR_G, TARGET_POINT)" + ] + }, + { + "attachments": { + "511eb99b-41ff-412e-a8fc-f028c27a8934.png": { + "image/png": "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" + } + }, + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "![Screenshot 2025-08-21 at 17.31.09.png](attachment:511eb99b-41ff-412e-a8fc-f028c27a8934.png)" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "#### Model creation and synthesis" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "24", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating model for Shor's ECDLP algorithm...\n", + "Synthesizing quantum program...\n", + "Number of qubits: 23\n", + "Quantum program link: https://platform.classiq.io/circuit/37HabpslsbIKZQA1k1zxki4eiGL\n" + ] + } + ], + "source": [ + "print(\"Creating model for Shor's ECDLP algorithm...\")\n", + "\n", + "constraints = Constraints(optimization_parameter=\"width\")\n", + "preferences = Preferences(timeout_seconds=3600, optimization_level=1, qasm3=True)\n", + "qmod = create_model(main, constraints=constraints, preferences=preferences)\n", + "write_qmod(qmod, \"elliptic_curve_discrete_log\", symbolic_only=False)\n", + "\n", + "print(\"Synthesizing quantum program...\")\n", + "qprog_shor_ecdlp = synthesize(qmod)\n", + "\n", + "# Display circuit metrics\n", + "num_qubits = qprog_shor_ecdlp.data.width\n", + "print(f\"Number of qubits: {num_qubits}\")\n", + "show(qprog_shor_ecdlp)" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "#### Quantum Program Analysis\n", + "\n", + "We can analyze the transpiled circuit characteristics from the quantum program object:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "26", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cx': 60828,\n", + " 'p': 44368,\n", + " 'u': 19619,\n", + " 'rz': 4104,\n", + " 'h': 3301,\n", + " 'tdg': 2844,\n", + " 't': 2844,\n", + " 'u1': 792,\n", + " 'x': 458}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qprog_shor_ecdlp.transpiled_circuit.count_ops" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "27", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "80093" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qprog_shor_ecdlp.transpiled_circuit.depth" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "#### Execution " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "res = execute(qprog_shor_ecdlp).result_value()" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "#### Results Collection" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "31", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Quantum Execution Results:\n", + "Total distinct pairs: 221, Total measurements: 2048\n", + "\n", + "Top 10 results:\n", + " x1 x2 ecp.x ecp.y count probability bitstring\n", + "0 0.000 0.000 3 2 100 0.048828 010011000000\n", + "1 0.000 0.000 5 0 95 0.046387 000101000000\n", + "2 0.375 0.375 3 2 87 0.042480 010011011011\n", + "3 0.625 0.625 3 2 83 0.040527 010011101101\n", + "4 0.000 0.000 4 5 82 0.040039 101100000000\n", + "5 0.625 0.625 5 0 77 0.037598 000101101101\n", + "6 0.375 0.375 5 0 71 0.034668 000101011011\n", + "7 0.000 0.000 4 2 71 0.034668 010100000000\n", + "8 0.000 0.000 3 5 69 0.033691 101011000000\n", + "9 0.625 0.625 3 5 61 0.029785 101011101101\n" + ] + } + ], + "source": [ + "df = res.dataframe\n", + "df_sorted = df.sort_values(\"count\", ascending=False)\n", + "df_sorted[\"probability\"] = df_sorted[\"count\"] / df[\"count\"].sum()\n", + "\n", + "print(f\"\\nQuantum Execution Results:\")\n", + "print(f\"Total distinct pairs: {len(df)}, Total measurements: {df['count'].sum()}\")\n", + "print(\"\\nTop 10 results:\")\n", + "print(df_sorted.head(10))" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "## 5. Post-Processing Results\n", + "\n", + "For post-processing our results, we will follow the same logic as explained in the [\"discrete log\" notebook](https://github.com/Classiq/classiq-library/blob/main/algorithms/number_theory_and_cryptography/discrete_log/discrete_log.ipynb). \n", + "\n", + "We translate each result to the closest fraction with a denominator, which is the order:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "33", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
x1x2ecp.xecp.ycountprobabilitybitstringx1_roundedx2_rounded
00.0000.000321000.0488280100110000000.00.0
10.0000.00050950.0463870001010000000.00.0
20.3750.37532870.0424800100110110112.02.0
30.6250.62532830.0405270100111011013.03.0
40.0000.00045820.0400391011000000000.00.0
50.6250.62550770.0375980001011011013.03.0
60.3750.37550710.0346680001010110112.02.0
70.0000.00042710.0346680101000000000.00.0
80.0000.00035690.0336911010110000000.00.0
90.6250.62535610.0297851010111011013.03.0
\n", + "
" + ], + "text/plain": [ + " x1 x2 ecp.x ecp.y count probability bitstring x1_rounded \\\n", + "0 0.000 0.000 3 2 100 0.048828 010011000000 0.0 \n", + "1 0.000 0.000 5 0 95 0.046387 000101000000 0.0 \n", + "2 0.375 0.375 3 2 87 0.042480 010011011011 2.0 \n", + "3 0.625 0.625 3 2 83 0.040527 010011101101 3.0 \n", + "4 0.000 0.000 4 5 82 0.040039 101100000000 0.0 \n", + "5 0.625 0.625 5 0 77 0.037598 000101101101 3.0 \n", + "6 0.375 0.375 5 0 71 0.034668 000101011011 2.0 \n", + "7 0.000 0.000 4 2 71 0.034668 010100000000 0.0 \n", + "8 0.000 0.000 3 5 69 0.033691 101011000000 0.0 \n", + "9 0.625 0.625 3 5 61 0.029785 101011101101 3.0 \n", + "\n", + " x2_rounded \n", + "0 0.0 \n", + "1 0.0 \n", + "2 2.0 \n", + "3 3.0 \n", + "4 0.0 \n", + "5 3.0 \n", + "6 2.0 \n", + "7 0.0 \n", + "8 0.0 \n", + "9 3.0 " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def closest_fraction(x, denominator):\n", + " return round(x * denominator)\n", + "\n", + "\n", + "df_sorted[\"x1_rounded\"] = closest_fraction(df_sorted.x1, GENERATOR_ORDER)\n", + "df_sorted[\"x2_rounded\"] = closest_fraction(df_sorted.x2, GENERATOR_ORDER)\n", + "df_sorted.head(10)" + ] + }, + { + "cell_type": "markdown", + "id": "34", + "metadata": {}, + "source": [ + "Now, we take a sample where `x2` is co-prime to the order, such that we can get the logarithm by multiplying `x1` by the modular inverse. If the `x1`, `x2` registers are large enough, we are guaranteed to sample it with a good probability.\n", + "\n", + "From the valid solutions, we solve for the discrete logarithm using:\n", + "$$\\text{logarithm} = - x_1 \\cdot x_2^{-1} \\bmod r$$" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "35", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
x1x2ecp.xecp.ycountprobabilitybitstringx1_roundedx2_roundedx2_inverselogarithm
20.3750.37532870.0424800100110110112.02.034.0
30.6250.62532830.0405270100111011013.03.024.0
50.6250.62550770.0375980001011011013.03.024.0
60.3750.37550710.0346680001010110112.02.034.0
90.6250.62535610.0297851010111011013.03.024.0
100.3750.37545590.0288091011000110112.02.034.0
110.6250.62545590.0288091011001011013.03.024.0
120.3750.37535580.0283201010110110112.02.034.0
130.3750.37542560.0273440101000110112.02.034.0
140.6250.62542520.0253910101001011013.03.024.0
\n", + "
" + ], + "text/plain": [ + " x1 x2 ecp.x ecp.y count probability bitstring x1_rounded \\\n", + "2 0.375 0.375 3 2 87 0.042480 010011011011 2.0 \n", + "3 0.625 0.625 3 2 83 0.040527 010011101101 3.0 \n", + "5 0.625 0.625 5 0 77 0.037598 000101101101 3.0 \n", + "6 0.375 0.375 5 0 71 0.034668 000101011011 2.0 \n", + "9 0.625 0.625 3 5 61 0.029785 101011101101 3.0 \n", + "10 0.375 0.375 4 5 59 0.028809 101100011011 2.0 \n", + "11 0.625 0.625 4 5 59 0.028809 101100101101 3.0 \n", + "12 0.375 0.375 3 5 58 0.028320 101011011011 2.0 \n", + "13 0.375 0.375 4 2 56 0.027344 010100011011 2.0 \n", + "14 0.625 0.625 4 2 52 0.025391 010100101101 3.0 \n", + "\n", + " x2_rounded x2_inverse logarithm \n", + "2 2.0 3 4.0 \n", + "3 3.0 2 4.0 \n", + "5 3.0 2 4.0 \n", + "6 2.0 3 4.0 \n", + "9 3.0 2 4.0 \n", + "10 2.0 3 4.0 \n", + "11 3.0 2 4.0 \n", + "12 2.0 3 4.0 \n", + "13 2.0 3 4.0 \n", + "14 3.0 2 4.0 " + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "\n", + "def modular_inverse(x):\n", + " return [pow(a, -1, GENERATOR_ORDER) for a in x]\n", + "\n", + "\n", + "df_sorted = df_sorted[\n", + " np.gcd(df_sorted.x2_rounded.astype(int), GENERATOR_ORDER) == 1\n", + "].copy()\n", + "df_sorted[\"x2_inverse\"] = modular_inverse(df_sorted.x2_rounded.astype(\"int\"))\n", + "df_sorted[\"logarithm\"] = -df_sorted.x1_rounded * df_sorted.x2_inverse % GENERATOR_ORDER\n", + "df_sorted.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "assert len(df_sorted.logarithm) > 0\n", + "assert np.allclose(df_sorted.logarithm[:10], 4)" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "#### **Quantum Algorithm Success**\n", + "\n", + "\n", + "*
The fact that multiple valid pairs yield the **same discrete logarithm (l = 4)** confirms the quantum algorithm worked correctly, extracting the hidden period structure from Shor's algorithm.
*" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "## 6. The Full Quantum Elliptic Curve Addition Quantum Program\n", + "\n", + "Now that we successfully verified the full ECDLP flow we can synthesize and analyze the full implementation of `ec_point_add` using the quantum modular inversion function `modular_inverse_inplace` (based on \"Kaliski's algorithm\") - imported directly from the Classiq Open Library." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "39", + "metadata": {}, + "outputs": [], + "source": [ + "@qperm\n", + "def ec_point_add(\n", + " ecp: EllipticCurvePoint,\n", + " G: list[int], # Classical point coordinates [Gx, Gy] on the curve\n", + " p: int, # Prime modulus\n", + ") -> None:\n", + " \"\"\"\n", + " Performs in-place elliptic curve point addition of a point whose coordinates are\n", + " stored in quantum struct object and a classically known point.\n", + " \"\"\"\n", + "\n", + " slope = QNum() # aux quantum register for lambda (slope)\n", + " allocate(CURVE.p.bit_length(), slope)\n", + " t0 = QNum() # aux quantum register for internal arithmetic\n", + " allocate(CURVE.p.bit_length(), t0)\n", + " m = QArray()\n", + "\n", + " # Extract classical coordinates\n", + " Gx = G[0] # x2\n", + " Gy = G[1] # y2\n", + "\n", + " # Step 1: # y <-- (y1 - y2) mod p\n", + " modular_add_constant_inplace(p, (-Gy) % p, ecp.y)\n", + "\n", + " # Step 2: # x <-- (x1 - x2) mod p\n", + " modular_add_constant_inplace(p, (-Gx) % p, ecp.x)\n", + "\n", + " # Step 3: # λ <-- (y1 - y2) / (x1 - x2) mod p\n", + " within_apply(\n", + " lambda: modular_inverse_inplace(p, ecp.x, m), # t0 <-- (x1 - x2)^(-1) mod p\n", + " lambda: modular_multiply(p, ecp.x, ecp.y, slope), # λ <-- t0 * (y1 -y2)\n", + " )\n", + " # free(m)\n", + "\n", + " # Step 4: y <-- 0\n", + " within_apply(\n", + " lambda: modular_multiply(\n", + " p, slope, ecp.x, t0\n", + " ), # t0 <-- λ * (x1 -x2) = (y1 - y2) = y\n", + " lambda: inplace_xor(t0, ecp.y), # y <-- 0\n", + " )\n", + "\n", + " # Step 5: x <-- x2 - x3\n", + " within_apply(\n", + " lambda: modular_square(p, slope, t0), # t0 = λ²\n", + " lambda: (\n", + " modular_subtract_inplace(p, t0, ecp.x), # x <-- t0 - x = λ² - (x1 - x2)\n", + " modular_negate_inplace(p, ecp.x), # x <-- -x = x1 - x2 - λ²\n", + " modular_add_constant_inplace(\n", + " p, (3 * Gx) % p, ecp.x\n", + " ), # x <-- x1 - x2 - λ² + 3*x2 = x2 - x3\n", + " ),\n", + " )\n", + "\n", + " # Step 6: y <-- y3 + y2\n", + " modular_multiply(p, slope, ecp.x, ecp.y) # y = λ * (x2 - x3) = y3 + y2\n", + "\n", + " # Step 7: λ <-- 0\n", + " t1 = QNum() # aux quantum register for manually uncomputing the slope\n", + " within_apply(\n", + " lambda: modular_inverse_inplace(p, ecp.x, m), # t0 <-- (x2 - x3)^(-1)\n", + " lambda: within_apply(\n", + " lambda: (\n", + " allocate(CURVE.p.bit_length(), t1),\n", + " modular_multiply(p, ecp.x, ecp.y, t1), # t1 <-- (y3 + y2)/(x2 - x3) = λ\n", + " ),\n", + " lambda: inplace_xor(t1, slope), # λ <-- 0\n", + " ),\n", + " )\n", + " free(slope)\n", + "\n", + " # Step 8: Final coordinate adjustments\n", + " modular_add_constant_inplace(p, (-Gy) % p, ecp.y) # y <-- y3 + y2 - y2 = y3!\n", + " modular_negate_inplace(p, ecp.x) # x <-- x3 - x2\n", + " modular_add_constant_inplace(p, Gx, ecp.x) # x <-- x3 - x2 + x2 = x3!\n", + "\n", + " free(t0)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "40", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Synthesizing quantum circuit for ec_point_add...\n", + "Number of qubits: 34\n", + "Program depth: 52180\n", + "Quantum program link: https://platform.classiq.io/circuit/37HdFbVHmK50VrFETZnFPpjwiEU\n" + ] + } + ], + "source": [ + "# Define the specific points from the notebook example\n", + "P1 = [4, 2] # Starting quantum point (will be modified in-place)\n", + "P2 = [0, 5] # Classical point to add\n", + "expected_result = [4, 5]\n", + "\n", + "\n", + "@qfunc\n", + "def main(\n", + " ecp: Output[EllipticCurvePoint],\n", + ") -> None:\n", + " \"\"\"Main quantum function for testing ec_point_add\"\"\"\n", + " # Initialize elliptic curve point with P1 coordinates\n", + " allocate(ecp)\n", + " ecp.x ^= P1[0] # x = 4\n", + " ecp.y ^= P1[1] # y = 2\n", + "\n", + " # Perform point addition: ecp = P1 + P2 = [4, 2] + [0, 5]\n", + " ec_point_add(ecp, P2, CURVE.p)\n", + "\n", + "\n", + "# Set up optimization constraints\n", + "constraints = Constraints(optimization_parameter=\"width\")\n", + "preferences = Preferences(timeout_seconds=3600, optimization_level=1, qasm3=True)\n", + "\n", + "# Create and synthesize quantum model\n", + "qmod = create_model(main, constraints=constraints, preferences=preferences)\n", + "\n", + "print(\"Synthesizing quantum circuit for ec_point_add...\")\n", + "qprog_point_add_full = synthesize(qmod)\n", + "\n", + "# Display circuit information\n", + "print(f\"Number of qubits: {qprog_point_add_full.data.width}\")\n", + "print(f\"Program depth: {qprog_point_add_full.transpiled_circuit.depth}\")\n", + "show(qprog_point_add_full)" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "## 7. The Full ECDLP Quantum Program\n", + "\n", + "This section presents the complete synthesis of Shor's ECDLP algorithm using the full quantum modular arithmetic library, demonstrating the end-to-end quantum circuit that solves the elliptic curve discrete logarithm problem with all components implemented using the Classiq Open Library functions." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "@qfunc\n", + "def main(x1: Output[QNum], x2: Output[QNum], ecp: Output[EllipticCurvePoint]) -> None:\n", + " # Call shor_ecdlp with the required parameters\n", + " shor_ecdlp(x1, x2, ecp, INITIAL_POINT, GENERATOR_G, TARGET_POINT)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "43", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating model for Shor's ECDLP algorithm...\n", + "Synthesizing quantum program...\n", + "Number of qubits: 40\n", + "Program depth: 326809\n", + "Quantum program link: https://platform.classiq.io/circuit/37HgZ8JOkfm2KeURploTQnYGt36\n" + ] + } + ], + "source": [ + "print(\"Creating model for Shor's ECDLP algorithm...\")\n", + "\n", + "constraints = Constraints(optimization_parameter=\"width\")\n", + "preferences = Preferences(timeout_seconds=3600, optimization_level=1, qasm3=True)\n", + "\n", + "print(\"Synthesizing quantum program...\")\n", + "qprog_shor_ecdlp_full = synthesize(\n", + " main, constraints=constraints, preferences=preferences\n", + ")\n", + "\n", + "# Display circuit metrics\n", + "print(f\"Number of qubits: {qprog_shor_ecdlp_full.data.width}\")\n", + "print(f\"Program depth: {qprog_shor_ecdlp_full.transpiled_circuit.depth}\")\n", + "\n", + "show(qprog_shor_ecdlp_full)" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "#### Algorithm Complexity and Quantum Advantage\n", + "\n", + "**Classical Complexity**: The best known classical algorithms for ECDLP (such as Pollard's rho algorithm) require $O(\\sqrt{n})$ operations, where $n$ is the order of the elliptic curve group. For cryptographically relevant curves with 256-bit keys, this means approximately $2^{128}$ operations.\n", + "\n", + "**Quantum Complexity**: Shor's algorithm reduces this to $O((\\log n)^3)$ operations using $O(\\log n)$ qubits, providing an exponential speedup. For the same 256-bit curves, this reduces to approximately $2^{24}$ operations." + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1]: [ Martin Roetteler, Michael Naehrig, Krysta M. Svore, and Kristin Lauter. \"Quantum resource estimates for computing elliptic curve discrete logarithms.\" *arXiv preprint arXiv:1706.06752* (2017).](https://arxiv.org/pdf/1706.06752)\n", + "\n", + "[2]: [Peter W. Shor. \"Polynomial-time algorithms for prime factorization and discrete logarithms on a quantum computer.\" *SIAM Journal on Computing*, 26(5):1484-1509 (1997). ](https://arxiv.org/abs/quant-ph/9508027)\n", + "\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 9 +} diff --git a/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.metadata.json b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.metadata.json new file mode 100644 index 000000000..53c1883f2 --- /dev/null +++ b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.metadata.json @@ -0,0 +1,8 @@ +{ + "friendly_name": "Elliptic Curve Discrete Logarithm", + "description": "Solving Elliptic Curve Discrete Logarithm Problem using Shor's Algorithm", + "level": ["advanced"], + "problem_domain_tags": [], + "qmod_type": ["algorithms"], + "vertical_tags": [] +} diff --git a/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.qmod b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.qmod new file mode 100644 index 000000000..9ff5f5d0f --- /dev/null +++ b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.qmod @@ -0,0 +1,302 @@ +qstruct EllipticCurvePoint { + x: qnum<3>; + y: qnum<3>; +} + +qfunc hadamard_transform_expanded___0(target: qbit[3]) { + repeat (index: 3) { + H(target[index]); + } +} + +qperm modular_add_constant_inplace_expanded___0(modulus: int, a: int, x: qnum<3, False, 0>) { + carry: qbit; + allocate(1, carry); + temp: qnum<4, True, 0>; + within { + {x, carry} -> temp; + } apply { + temp += a; + temp += -modulus; + } + control (carry) { + x += modulus; + } + carry ^= x >= a; + free(carry); +} + +qperm mock_modular_inverse_expanded___0(const x: qnum<3, False, 0>, result: qnum<3, False, 0>) { + result ^= [0, 1, 4, 5, 2, 3, 6, 0][x]; +} + +qperm modular_add_inplace_expanded___0(modulus: int, const x: qnum<3, False, 0>, y: qnum<3, False, 0>) { + carry: qbit; + allocate(1, carry); + temp: qnum<4, True, 0>; + within { + {y, carry} -> temp; + } apply { + temp += x; + temp += -modulus; + } + control (carry) { + y += modulus; + } + carry ^= y >= x; + free(carry); +} + +qperm cyclic_shift_left_expanded___0(reg: qbit[4]) { + repeat (i: 3) { + SWAP(reg[(4 - i) - 1], reg[(4 - i) - 2]); + } +} + +qperm modular_double_inplace_expanded___0(modulus: int, x: qnum<3, False, 0>) { + carry: qbit; + allocate(1, carry); + res_and_carry: qnum<4, True, 0>; + within { + {x, carry} -> res_and_carry; + } apply { + cyclic_shift_left_expanded___0(res_and_carry); + res_and_carry += -modulus; + } + control (carry) { + x += modulus; + } + carry ^= (x % 2) == 0; + free(carry); +} + +qperm modular_multiply_expanded___0(modulus: int, const x: qbit[3], const y: qbit[3], z: qbit[3]) { + repeat (idx: 3) { + control (x[(3 - idx) - 1]) { + modular_add_inplace_expanded___0(modulus, y, z); + } + if (idx != 2) { + modular_double_inplace_expanded___0(modulus, z); + } + } +} + +qperm modular_square_expanded___0(modulus: int, const x: qbit[3], z: qbit[3]) { + repeat (i: 2) { + control (x[(3 - i) - 1]) { + modular_add_inplace_expanded___0(modulus, x, z); + } + modular_double_inplace_expanded___0(modulus, z); + } + control (x[0]) { + modular_add_inplace_expanded___0(modulus, x, z); + } +} + +qperm apply_to_all_expanded___0(target: qbit[3]) { + repeat (index: 3) { + X(target[index]); + } +} + +qperm bitwise_negate_expanded___0(x: qbit[3]) { + apply_to_all_expanded___0(x); +} + +qperm modular_negate_inplace_expanded___0(modulus: int, x: qnum<3, False, 0>) { + is_all_zeros: qbit; + allocate(1, is_all_zeros); + is_all_zeros ^= x == 0; + control (is_all_zeros) { + x += modulus; + } + x += 7 - modulus; + is_all_zeros ^= x == 7; + bitwise_negate_expanded___0(x); + free(is_all_zeros); +} + +qperm modular_subtract_inplace_expanded___0(modulus: int, const x: qnum<3, False, 0>, y: qnum<3, False, 0>) { + modular_negate_inplace_expanded___0(modulus, y); + modular_add_inplace_expanded___0(modulus, x, y); +} + +qperm mock_modular_inverse_expanded___1(const x: qnum<3, False, 0>, result: qnum<3, False, 0>) { + result ^= [0, 1, 4, 5, 2, 3, 6, 0][x]; +} + +qperm ec_point_add_expanded___0(ecp: EllipticCurvePoint) { + slope: qnum<3, False, 0>; + allocate(3, slope); + t0: qnum<3, False, 0>; + allocate(3, t0); + modular_add_constant_inplace_expanded___0(7, 2, ecp.y); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); + within { + mock_modular_inverse_expanded___0(ecp.x, t0); + } apply { + modular_multiply_expanded___0(7, t0, ecp.y, slope); + } + within { + modular_multiply_expanded___0(7, slope, ecp.x, t0); + } apply { + ecp.y ^= t0; + } + within { + modular_square_expanded___0(7, slope, t0); + } apply { + modular_subtract_inplace_expanded___0(7, t0, ecp.x); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); + } + modular_multiply_expanded___0(7, slope, ecp.x, ecp.y); + t1: qnum<3, False, 0>; + within { + mock_modular_inverse_expanded___1(ecp.x, t0); + } apply { + within { + allocate(3, t1); + modular_multiply_expanded___0(7, t0, ecp.y, t1); + } apply { + slope ^= t1; + } + } + free(slope); + modular_add_constant_inplace_expanded___0(7, 2, ecp.y); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); +} + +qperm ec_point_add_expanded___1(ecp: EllipticCurvePoint) { + slope: qnum<3, False, 0>; + allocate(3, slope); + t0: qnum<3, False, 0>; + allocate(3, t0); + modular_add_constant_inplace_expanded___0(7, 6, ecp.y); + modular_add_constant_inplace_expanded___0(7, 5, ecp.x); + within { + mock_modular_inverse_expanded___0(ecp.x, t0); + } apply { + modular_multiply_expanded___0(7, t0, ecp.y, slope); + } + within { + modular_multiply_expanded___0(7, slope, ecp.x, t0); + } apply { + ecp.y ^= t0; + } + within { + modular_square_expanded___0(7, slope, t0); + } apply { + modular_subtract_inplace_expanded___0(7, t0, ecp.x); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 6, ecp.x); + } + modular_multiply_expanded___0(7, slope, ecp.x, ecp.y); + t1: qnum<3, False, 0>; + within { + mock_modular_inverse_expanded___1(ecp.x, t0); + } apply { + within { + allocate(3, t1); + modular_multiply_expanded___0(7, t0, ecp.y, t1); + } apply { + slope ^= t1; + } + } + free(slope); + modular_add_constant_inplace_expanded___0(7, 6, ecp.y); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 2, ecp.x); +} + +qperm ec_point_add_expanded___2(ecp: EllipticCurvePoint) { + slope: qnum<3, False, 0>; + allocate(3, slope); + t0: qnum<3, False, 0>; + allocate(3, t0); + modular_add_constant_inplace_expanded___0(7, 5, ecp.y); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); + within { + mock_modular_inverse_expanded___0(ecp.x, t0); + } apply { + modular_multiply_expanded___0(7, t0, ecp.y, slope); + } + within { + modular_multiply_expanded___0(7, slope, ecp.x, t0); + } apply { + ecp.y ^= t0; + } + within { + modular_square_expanded___0(7, slope, t0); + } apply { + modular_subtract_inplace_expanded___0(7, t0, ecp.x); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); + } + modular_multiply_expanded___0(7, slope, ecp.x, ecp.y); + t1: qnum<3, False, 0>; + within { + mock_modular_inverse_expanded___1(ecp.x, t0); + } apply { + within { + allocate(3, t1); + modular_multiply_expanded___0(7, t0, ecp.y, t1); + } apply { + slope ^= t1; + } + } + free(slope); + modular_add_constant_inplace_expanded___0(7, 5, ecp.y); + modular_negate_inplace_expanded___0(7, ecp.x); + modular_add_constant_inplace_expanded___0(7, 0, ecp.x); +} + +qperm ec_scalar_mult_add_expanded___0(ecp: EllipticCurvePoint, k: qbit[3]) { + control (k[0]) { + ec_point_add_expanded___0(ecp); + } + control (k[1]) { + ec_point_add_expanded___1(ecp); + } + control (k[2]) { + ec_point_add_expanded___2(ecp); + } +} + +qfunc qft_no_swap_expanded___0(qbv: qbit[3]) { + repeat (i: 3) { + H(qbv[i]); + repeat (j: (3 - i) - 1) { + CPHASE(pi / (2 ** (j + 1)), qbv[(i + j) + 1], qbv[i]); + } + } +} + +qfunc qft_expanded___0(target: qbit[3]) { + repeat (index: 1.5) { + SWAP(target[index], target[2 - index]); + } + qft_no_swap_expanded___0(target); +} + +qfunc shor_ecdlp_expanded___0(output x1: qnum<3, False, 3>, output x2: qnum<3, False, 3>, output ecp: EllipticCurvePoint) { + allocate(3, False, 3, x1); + allocate(3, False, 3, x2); + allocate(6, ecp); + ecp.x ^= 4; + ecp.y ^= 2; + hadamard_transform_expanded___0(x1); + hadamard_transform_expanded___0(x2); + ec_scalar_mult_add_expanded___0(ecp, x1); + ec_scalar_mult_add_expanded___0(ecp, x2); + invert { + qft_expanded___0(x1); + } + invert { + qft_expanded___0(x2); + } +} + +qfunc main(output x1: qnum<3, False, 3>, output x2: qnum<3, False, 3>, output ecp: EllipticCurvePoint) { + shor_ecdlp_expanded___0(x1, x2, ecp); +} diff --git a/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.synthesis_options.json b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.synthesis_options.json new file mode 100644 index 000000000..7b5f68924 --- /dev/null +++ b/algorithms/number_theory_and_cryptography/ecdlp/elliptic_curve_discrete_log.synthesis_options.json @@ -0,0 +1,45 @@ +{ + "constraints": { + "max_gate_count": {}, + "optimization_parameter": "width" + }, + "preferences": { + "custom_hardware_settings": { + "basis_gates": [ + "rx", + "h", + "r", + "tdg", + "cz", + "s", + "sdg", + "z", + "u", + "cx", + "y", + "sx", + "x", + "id", + "sxdg", + "ry", + "cy", + "t", + "p", + "rz", + "u2", + "u1" + ], + "is_symmetric_connectivity": true + }, + "debug_mode": true, + "machine_precision": 8, + "optimization_level": 1, + "output_format": ["qasm"], + "pretty_qasm": true, + "qasm3": true, + "random_seed": 3794060243, + "synthesize_all_separately": false, + "timeout_seconds": 3600, + "transpilation_option": "auto optimize" + } +} diff --git a/tests/notebooks/test_elliptic_curve_discrete_log.py b/tests/notebooks/test_elliptic_curve_discrete_log.py new file mode 100644 index 000000000..48777b289 --- /dev/null +++ b/tests/notebooks/test_elliptic_curve_discrete_log.py @@ -0,0 +1,21 @@ +from tests.utils_for_testbook import ( + validate_quantum_program_size, + validate_quantum_model, + wrap_testbook, +) +from testbook.client import TestbookNotebookClient + + +@wrap_testbook("elliptic_curve_discrete_log", timeout_seconds=1800) +def test_notebook(tb: TestbookNotebookClient) -> None: + # test models + validate_quantum_model(tb.ref("qmod")) + # test quantum programs + validate_quantum_program_size( + tb.ref_pydantic("qprog_shor_ecdlp"), + expected_width=25, + expected_depth=100000, + ) + + # test notebook content + pass # Todo diff --git a/tests/resources/timeouts.yaml b/tests/resources/timeouts.yaml index 28bb9bdc6..7978e513c 100644 --- a/tests/resources/timeouts.yaml +++ b/tests/resources/timeouts.yaml @@ -96,6 +96,8 @@ dqi_max_xorsat.qmod: 200 dt_quantumwalk_complex_network.qmod: 1799 electric_grid_optimization.ipynb: 996 electric_grid_optimization.qmod: 1152 +elliptic_curve_discrete_log.ipynb: 1800 +elliptic_curve_discrete_log.qmod: 1800 encode_in_angle.qmod: 10 encode_on_bloch.qmod: 10 entanglement.ipynb: 20