Skip to content

Feat/shor classical implementation #12799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added cryptography/__init__.py
Empty file.
99 changes: 99 additions & 0 deletions cryptography/shor_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
Classical simulation of Shor's Algorithm to factor integers.

Source: https://en.wikipedia.org/wiki/Shor%27s_algorithm
"""

import math
import random
from typing import Any

Check failure on line 9 in cryptography/shor_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F401)

cryptography/shor_algorithm.py:9:20: F401 `typing.Any` imported but unused


def is_prime(n: int) -> bool:

Choose a reason for hiding this comment

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

Please provide descriptive name for the parameter: n

"""
Check if a number is prime.

>>> is_prime(2)
True
>>> is_prime(4)
False
"""
if n < 2:
return False
if n in (2, 3):
return True
if n % 2 == 0:
return False
r = math.isqrt(n)
return all(n % i != 0 for i in range(3, r + 1, 2))


def modexp(a: int, b: int, m: int) -> int:

Choose a reason for hiding this comment

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

Please provide descriptive name for the parameter: a

Please provide descriptive name for the parameter: b

Please provide descriptive name for the parameter: m

"""
Modular exponentiation: (a^b) % m

>>> modexp(2, 5, 13)
6
"""
result = 1
a = a % m
while b > 0:
if b & 1:
result = (result * a) % m
a = (a * a) % m
b >>= 1
return result


def shor_classical(n: int, max_attempts: int = 10) -> str | tuple[int, int]:

Choose a reason for hiding this comment

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

Please provide descriptive name for the parameter: n

"""
Classical approximation of Shor's Algorithm to factor a number.

>>> result = shor_classical(15)
>>> isinstance(result, tuple)
True
>>> sorted(result) == [3, 5]
True

>>> shor_classical(13) # Prime
'No factors: 13 is prime'
"""
if n <= 1:
return "Failure: input must be > 1"
if n % 2 == 0:
return 2, n // 2
if is_prime(n):
return f"No factors: {n} is prime"

for _ in range(max_attempts):
a = random.randrange(2, n - 1)
g = math.gcd(a, n)
if g > 1:
return g, n // g

r = 1
while r < n:
if modexp(a, r, n) == 1:
break
r += 1
else:
continue

if r % 2 != 0:
continue
x = modexp(a, r // 2, n)
if x == n - 1:
continue

factor1 = math.gcd(x - 1, n)
factor2 = math.gcd(x + 1, n)
if factor1 not in (1, n) and factor2 not in (1, n):
return factor1, factor2

return "Failure: try more attempts"


if __name__ == "__main__":
import doctest

doctest.testmod()
Empty file added cryptography/tests/__init__.py
Empty file.
36 changes: 36 additions & 0 deletions cryptography/tests/test_shor_algorithm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

Check failure on line 1 in cryptography/tests/test_shor_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F401)

cryptography/tests/test_shor_algorithm.py:1:8: F401 `pytest` imported but unused
from cryptography.shor_algorithm import shor_classical

Check failure on line 2 in cryptography/tests/test_shor_algorithm.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

cryptography/tests/test_shor_algorithm.py:1:1: I001 Import block is un-sorted or un-formatted


def test_small_composite():
factors = shor_classical(15)
assert set(factors) == {3, 5}


def test_medium_composite():
factors = shor_classical(21)
assert set(factors) == {3, 7}


def test_even_number():
factors = shor_classical(18)
assert set(factors) == {2, 9}


def test_prime_number():
result = shor_classical(13)
assert isinstance(result, str)
assert "prime" in result.lower()


def test_invalid_input():
result = shor_classical(1)
assert isinstance(result, str)
assert "failure" in result.lower()


def test_larger_composite_number():
result = shor_classical(91)
assert isinstance(result, (tuple, str))
if isinstance(result, tuple):
assert all(isinstance(x, int) for x in result)
Loading