Cryptology Algorithms
Overview
unitarylab_algorithms.cryptology contains three quantum cryptography algorithms that demonstrate the advantages of quantum computing over classical methods for number-theoretic and hidden-period problems:
| Algorithm | Class | Problem Solved |
|---|---|---|
| Shor’s Factoring Algorithm | ShorAlgorithm | Prime factorization of a composite number |
| Discrete Logarithm Algorithm | DiscreteLogAlgorithm | Solve |
| Simon’s Periodic Mask Algorithm | SimonAlgorithm | Find the hidden mask in |
All three algorithms inherit from unitarylab_algorithms.algo_base.BaseAlgorithm, and the return value of .run() follows a unified structure:
{
'status': 'ok' | 'failed', # Whether it succeeded, determined by the algorithm's own validation logic
'circuit_path': '/path/to/xxx_circuit.svg', # Full path to the circuit diagram; None for branches of some algorithms that do not need to build a circuit
'plot': [{'format': 'txt', 'filename': 'xxx_algorithm_result.txt'}], # Result text file, filename is the **filename without the directory**
'circuit': <Circuit object or None>,
# ... The following are algorithm-specific fields appended via update_output() (see each section)
}Shor’s Factoring Algorithm
Background
Shor’s algorithm factors a composite number into its prime factors in polynomial time. The best classical algorithms (such as the general number field sieve) require sub-exponential time, while Shor’s algorithm can complete the task in time on a quantum computer.
The algorithm combines:
- Quantum period finding based on the Quantum Fourier Transform (QFT)
- Classical post-processing to extract the prime factors from the period
Import
from unitarylab_algorithms import ShorAlgorithm.run() Parameters
def run(self, N: int, method: str = "matrix", max_retries: int = 15,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
N | int | — (required) | Composite number to factor (e.g. 15, 21) |
method | str | 'matrix' | Solving method: 'matrix' or 'operator'; passing any other value raises ValueError |
max_retries | int | 15 | Maximum number of times, within a single .run() call, that the base is resampled and retried |
parameters.json(the web-side parameter panel) only exposes the two parametersN(range 2–300) andmethod, it does not provide a UI input formax_retries; to customize the retry count you must call the Python API directly.
Execution Flow
- Classical pre-check: if
Nis even, the factors[2, N//2]are returned directly; if the randomly sampled baseashares a common factor withN, classical factorization is done directly — in both of these cases no quantum circuit is built, andcircuit_pathisNone. - Otherwise, a phase estimation circuit is built (
method='matrix'uses a controlled permutation matrix;method='operator'uses a modular multiplier circuit, which uses more qubits but is a more “physical” circuit), and it is executed and measured. - Continued-fraction expansion is applied to the measurement result to extract the period ; if is even and , then is computed to obtain the factors.
- If a single attempt fails to extract a valid factor, the base is resampled and the process repeats, up to
max_retriestimes.
Return Value
Consistent with other BaseAlgorithm subclasses, status truly reflects whether the algorithm succeeded in finding factors (both the classical shortcut branch and a successful quantum branch return 'ok'; if max_retries is exhausted without success it is 'failed'). The fields appended by update_output() vary by branch:
# Classical shortcut branch (N is even / the random base shares a common factor with N)
{'factors': [p, q], 'period': None, 'Selected base': a_or_None}
# Quantum branch succeeds
{
'factors': [p, q], 'period': r, 'Selected base': a,
'Computation time (s)': 0.1234, 'Measurement': 42, 'Total qubits': 12,
}
# Retries exhausted, still failed
{
'factors': None, 'period': None, 'Selected base': a,
'Computation time (s)': 0.1234, 'Measurement': 42, 'Total qubits': 12,
}Example
from unitarylab_algorithms import ShorAlgorithm
algo = ShorAlgorithm()
result = algo.run(N=15)
print(result['status']) # 'ok'
print(result['factors']) # e.g. [3, 5]Quick Demo
from unitarylab_algorithms.cryptology.shor.algorithm import test
test(N=15, method='matrix', max_retries=15)Notes
Nmust be a composite number with at least two distinct prime factors. Passing1will, on the very first attempt, cause the internal callrandom.randint(2, N - 1)(i.e.random.randint(2, 0)) to directly raiseValueError: empty range for randrange(), rather than entering the retry loop; passing2(or any other even number) will be short-circuited directly by theN % 2 == 0branch, immediately returningstatus='ok'withfactors=[2, N//2], and likewise will not enter the retry loop. Only passing an odd prime (such as7) will actually enter the retry loop, ending withstatus='failed'aftermax_retriesattempts are exhausted.- The
'matrix'method directly constructs a controlled permutation matrix, using fewer qubits, suitable for small ; the'operator'method uses a modular multiplier circuit, which uses more qubits. - When
Nis even, or the random base happens to share a common factor withN, the algorithm takes the classical shortcut and returns directly, without building/saving a quantum circuit (circuit_path=None); in this case the return value cannot be used to judge “whether the quantum algorithm succeeded.” statustruly reflects whether the factorization succeeded, and can be safely used for business logic decisions (this differs from the case infundamental_algorithmwhere some algorithms havestatusthat is “always'ok'”).
Discrete Logarithm Algorithm
Background
The discrete logarithm problem: given , , and , find such that . This is the security foundation of many classical cryptographic systems (such as Diffie–Hellman and DSA). The quantum algorithm uses quantum phase estimation and continued-fraction post-processing to solve it efficiently, and it executes the quantum circuit only once (unlike Shor’s algorithm, it has no retry mechanism).
Import
from unitarylab_algorithms import DiscreteLogAlgorithm.run() Parameters
def run(self, g: int, y: int, P: int,
backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
g | int | — (required) | Base of the exponentiation, must be coprime with P |
y | int | — (required) | Target value, must be coprime with P |
P | int | — (required) | Modulus (typically prime) |
g, y, and P have no default values in .run() and must be passed explicitly. If or , a ValueError("g and y must be coprime with P") is raised directly.
The module-level
test()function and the default example on the web-sideparameters.jsonare inconsistent:test()defaults tog=3, y=6, P=7(corresponding to , answer ); while the default values inparameters.jsonareg=3, y=13, P=17. The example below usesg=3, y=6, P=7, consistent withtest().
Return Value
status reflects true success/failure (is_success = found_x is not None):
{
'status': 'ok',
'circuit_path': '/path/to/discrete_logarithm_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'discrete_logarithm_algorithm_result.txt'}],
'circuit': <Circuit>,
'Computation time (s)': 0.0456,
'Detected period r': 3,
'Found x': 3,
}If the continued-fraction post-processing fails to find a solution satisfying , status='failed' and Found x' is None.
Example
from unitarylab_algorithms import DiscreteLogAlgorithm
# Solve 3^x ≡ 6 (mod 7), the answer is x = 3
algo = DiscreteLogAlgorithm()
result = algo.run(g=3, y=6, P=7)
print(result['status']) # 'ok'
print(result['Found x']) # 3Quick Demo
from unitarylab_algorithms.cryptology.discrete_log.algorithm import test
test(g=3, y=6, P=7)Notes
gandymust both be coprime withP, otherwise aValueErroris raised directly during parameter preparation, and no circuit is built.- Unlike Shor’s algorithm, this algorithm has no retry mechanism — if a single quantum measurement fails to find a solution via continued-fraction post-processing,
status='failed'is returned directly. - The default values in the web-side parameter panel (
g=3, y=13, P=17) differ from the default values in the source code’stest()/__main__(g=3, y=6, P=7); when calling the Python API, go by the arguments you actually pass.
Simon’s Periodic Mask Algorithm
Background
Simon’s problem: given a function , known to have a hidden mask satisfying , find . Classical algorithms require an exponential number of queries, while Simon’s algorithm only needs quantum queries plus classical Gaussian elimination to solve it.
Import
from unitarylab_algorithms import SimonAlgorithm.run() Parameters
def run(self, s: str = "1010", backend='torch', device='cpu', dtype=np.complex128) -> Dict[str, Any]| Parameter | Type | Default | Description |
|---|---|---|---|
s | str | '1010' | Binary string representing the hidden mask to be found; its length is the number of bits |
The default value
'1010'of.run()itself is inconsistent with the default value'1101'used by the module-leveltest()function and the web-sideparameters.json. CallingSimonAlgorithm().run()directly (without passings) uses'1010'; callingtest()or running via the web side defaults to'1101'.
Return Value
{
'status': 'ok',
'circuit_path': '/path/to/simon_algorithm_circuit.svg',
'plot': [{'format': 'txt', 'filename': 'simon_algorithm_result.txt'}],
'circuit': <Circuit>,
'Computed s': '1101',
'Valid states': 8,
'computation time (s)': 0.0321, # Note: the key name is lowercase computation, and it is not rounded (unlike 'Computation time (s)' in the other two algorithms)
'Register size': 4,
'Equations': 3,
}Example
from unitarylab_algorithms import SimonAlgorithm
algo = SimonAlgorithm()
result = algo.run(s='1101')
print(result['status']) # 'ok'
print(result['Computed s']) # '1101'Quick Demo
from unitarylab_algorithms.cryptology.simon.algorithm import test
test(s='1101')Notes
smust be a binary string of length (e.g.,'1010'corresponds to ), and must contain at least one'1'— an all-zero string (such as'0000') will directly triggerValueError("Secret string s cannot be all zeros"), and the algorithm will not run to completion.- The computation-time field in the output dictionary is named in lowercase,
'computation time (s)', and is a raw, unrounded floating-point number, differing from the capitalized, 4-decimal-rounded'Computation time (s)'used in the Shor / Discrete Logarithm algorithms — be careful of the case when reading it. statusis determined by the actual comparison result offound_s == s, and can be used to judge whether this particular measurement-and-solve run successfully recovered the mask .