oqubit 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oqubit/__init__.py +47 -0
- oqubit/algorithms/__init__.py +5 -0
- oqubit/algorithms/bernstein_vazirani.py +60 -0
- oqubit/algorithms/deutsch.py +37 -0
- oqubit/algorithms/deutsch_joza.py +121 -0
- oqubit/algorithms/deutsch_jozsa.py +121 -0
- oqubit/algorithms/qft.py +87 -0
- oqubit/algorithms/superdense_coding.py +28 -0
- oqubit/circuit/__init__.py +3 -0
- oqubit/circuit/circuit.py +278 -0
- oqubit/circuit/instruction.py +64 -0
- oqubit/core/__init__.py +4 -0
- oqubit/core/operators.py +79 -0
- oqubit/core/qubit.py +68 -0
- oqubit/core/statevector.py +151 -0
- oqubit/gates/__init__.py +5 -0
- oqubit/gates/controlled.py +17 -0
- oqubit/gates/multi.py +33 -0
- oqubit/gates/single.py +17 -0
- oqubit/measurement/__init__.py +3 -0
- oqubit/measurement/measurement.py +124 -0
- oqubit/measurement/sampling.py +46 -0
- oqubit-0.1.dist-info/METADATA +489 -0
- oqubit-0.1.dist-info/RECORD +27 -0
- oqubit-0.1.dist-info/WHEEL +5 -0
- oqubit-0.1.dist-info/licenses/LICENSE +21 -0
- oqubit-0.1.dist-info/top_level.txt +1 -0
oqubit/__init__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from .core.qubit import Qubit
|
|
2
|
+
from .core.statevector import StateVector
|
|
3
|
+
from .core.operators import Operator, Gate
|
|
4
|
+
from .gates.single import X,Y,Z,H,S,T,SX
|
|
5
|
+
from .gates.controlled import CX,CY,CZ,CS,CH,CT,CSX
|
|
6
|
+
from .gates.multi import SWAP,ISWAP,SQRT_SWAP,CCX,CSWAP
|
|
7
|
+
from .measurement.measurement import measure
|
|
8
|
+
from .measurement.sampling import sample
|
|
9
|
+
from .circuit.circuit import Circuit
|
|
10
|
+
from .circuit.instruction import Instruction
|
|
11
|
+
from .algorithms.deutsch import deutsch
|
|
12
|
+
from .algorithms.deutsch_jozsa import deutsch_jozsa
|
|
13
|
+
from .algorithms.bernstein_vazirani import bernstein_vazirani
|
|
14
|
+
from .algorithms.superdense_coding import encode,decode
|
|
15
|
+
__all__=["Qubit",
|
|
16
|
+
"StateVector",
|
|
17
|
+
"Operator",
|
|
18
|
+
"Gate",
|
|
19
|
+
"X",
|
|
20
|
+
"Y",
|
|
21
|
+
"Z",
|
|
22
|
+
"H",
|
|
23
|
+
"S",
|
|
24
|
+
"T",
|
|
25
|
+
"SX",
|
|
26
|
+
"CX",
|
|
27
|
+
"CY",
|
|
28
|
+
"CZ",
|
|
29
|
+
"CS",
|
|
30
|
+
"CH",
|
|
31
|
+
"CT",
|
|
32
|
+
"CSX",
|
|
33
|
+
"SWAP",
|
|
34
|
+
"ISWAP",
|
|
35
|
+
"SQRT_SWAP",
|
|
36
|
+
"CCX",
|
|
37
|
+
"CSWAP",
|
|
38
|
+
"measure",
|
|
39
|
+
"sample",
|
|
40
|
+
"Circuit",
|
|
41
|
+
"Instruction",
|
|
42
|
+
"deutsch",
|
|
43
|
+
"deutsch_jozsa",
|
|
44
|
+
"bernstein_vazirani",
|
|
45
|
+
"encode",
|
|
46
|
+
"decode",
|
|
47
|
+
]
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bernstein–Vazirani algorithm.
|
|
3
|
+
|
|
4
|
+
This module implements the Bernstein–Vazirani algorithm for recovering
|
|
5
|
+
a hidden binary string ``s`` from a Boolean function of the form
|
|
6
|
+
|
|
7
|
+
f(x) = s · x (mod 2)
|
|
8
|
+
|
|
9
|
+
where ``s`` and ``x`` are binary strings of equal length.
|
|
10
|
+
|
|
11
|
+
The algorithm determines the complete hidden string using a single
|
|
12
|
+
quantum-oracle evaluation.
|
|
13
|
+
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
The implementation constructs the oracle from elementary X and
|
|
17
|
+
controlled-X gates. The oracle therefore represents the transformation
|
|
18
|
+
|
|
19
|
+
U_f |x>|y> = |x>|y XOR f(x)>.
|
|
20
|
+
|
|
21
|
+
The first ``n`` qubits form the input register and the final qubit
|
|
22
|
+
is the oracle ancilla.
|
|
23
|
+
|
|
24
|
+
For a secret string
|
|
25
|
+
|
|
26
|
+
s = s_0 s_1 ... s_(n-1),
|
|
27
|
+
|
|
28
|
+
the oracle applies a controlled-X gate from input qubit ``i`` to the
|
|
29
|
+
ancilla whenever ``s_i == 1``.
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
from typing import Final
|
|
33
|
+
from ..circuit import Circuit
|
|
34
|
+
__all__ = ["bernstein_vazirani"]
|
|
35
|
+
def _validate_secret(secret: str) -> None:
|
|
36
|
+
if not isinstance(secret, str):
|
|
37
|
+
raise TypeError("secret must be a string.")
|
|
38
|
+
if not secret:
|
|
39
|
+
raise ValueError("secret must not be empty.")
|
|
40
|
+
if any(bit not in "01" for bit in secret):
|
|
41
|
+
raise ValueError("secret must contain only binary digits '0' and '1'.")
|
|
42
|
+
def _apply_oracle(circuit: Circuit,secret: str,) -> None:
|
|
43
|
+
ancilla = len(secret)
|
|
44
|
+
for qubit, bit in enumerate(secret):
|
|
45
|
+
if bit == "1":
|
|
46
|
+
circuit.cx(qubit, ancilla)
|
|
47
|
+
def bernstein_vazirani(secret: str) -> str:
|
|
48
|
+
_validate_secret(secret)
|
|
49
|
+
num_qubits = len(secret)
|
|
50
|
+
ancilla = num_qubits
|
|
51
|
+
circuit = Circuit(num_qubits + 1)
|
|
52
|
+
circuit.x(ancilla)
|
|
53
|
+
for qubit in range(num_qubits + 1):
|
|
54
|
+
circuit.h(qubit)
|
|
55
|
+
_apply_oracle(circuit,secret,)
|
|
56
|
+
for qubit in range(num_qubits):
|
|
57
|
+
circuit.h(qubit)
|
|
58
|
+
state = circuit.run()
|
|
59
|
+
result = circuit.measure(state=state,qubits=tuple(range(num_qubits)),)
|
|
60
|
+
return "".join(str(bit) for bit in result)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from ..circuit import Circuit
|
|
5
|
+
__all__ = ["deutsch"]
|
|
6
|
+
DeutschResult = Literal["constant", "balanced"]
|
|
7
|
+
def _validate_function(function: Callable[[int], int],) -> tuple[int, int]:
|
|
8
|
+
if not callable(function):
|
|
9
|
+
raise TypeError("function must be callable.")
|
|
10
|
+
f0 = function(0)
|
|
11
|
+
f1 = function(1)
|
|
12
|
+
if f0 not in (0, 1) or f1 not in (0, 1):
|
|
13
|
+
raise ValueError("function must return either 0 or 1.")
|
|
14
|
+
return f0, f1
|
|
15
|
+
def _apply_oracle(circuit: Circuit,f0: int,f1: int,) -> None:
|
|
16
|
+
if f0 == 0 and f1 == 0:
|
|
17
|
+
return
|
|
18
|
+
if f0 == 1 and f1 == 1:
|
|
19
|
+
circuit.x(1)
|
|
20
|
+
return
|
|
21
|
+
if f0 == 0 and f1 == 1:
|
|
22
|
+
circuit.cx(0, 1)
|
|
23
|
+
return
|
|
24
|
+
circuit.x(1)
|
|
25
|
+
circuit.cx(0, 1)
|
|
26
|
+
circuit.x(1)
|
|
27
|
+
def deutsch(function: Callable[[int], int],) -> DeutschResult:
|
|
28
|
+
f0, f1 = _validate_function(function)
|
|
29
|
+
circuit = Circuit(2)
|
|
30
|
+
circuit.x(1)
|
|
31
|
+
circuit.h(0)
|
|
32
|
+
circuit.h(1)
|
|
33
|
+
_apply_oracle(circuit, f0, f1)
|
|
34
|
+
circuit.h(0)
|
|
35
|
+
state = circuit.run()
|
|
36
|
+
result = circuit.measure(state=state,qubits=0,)
|
|
37
|
+
return "constant" if result == 0 else "balanced"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deutsch–Jozsa algorithm.
|
|
3
|
+
|
|
4
|
+
This module implements the Deutsch–Jozsa algorithm for Boolean
|
|
5
|
+
functions of the form
|
|
6
|
+
|
|
7
|
+
f : {0, 1}^n -> {0, 1}
|
|
8
|
+
|
|
9
|
+
under the promise that ``f`` is either constant or balanced.
|
|
10
|
+
|
|
11
|
+
The algorithm determines whether the function is constant or
|
|
12
|
+
balanced using a single quantum-oracle evaluation.
|
|
13
|
+
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
The implementation constructs the oracle from elementary X and
|
|
17
|
+
controlled-X gates. The oracle therefore represents the transformation
|
|
18
|
+
|
|
19
|
+
U_f |x>|y> = |x>|y XOR f(x)>.
|
|
20
|
+
|
|
21
|
+
The first ``n`` qubits form the input register and the final qubit
|
|
22
|
+
is the oracle ancilla.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
from collections.abc import Callable, Iterable
|
|
26
|
+
from itertools import product
|
|
27
|
+
from typing import Literal
|
|
28
|
+
from ..circuit import Circuit
|
|
29
|
+
__all__ = ["deutsch_jozsa"]
|
|
30
|
+
DeutschJozsaResult = Literal["constant", "balanced"]
|
|
31
|
+
def _validate_num_qubits(num_qubits: int) -> None:
|
|
32
|
+
if not isinstance(num_qubits, int):
|
|
33
|
+
raise TypeError("num_qubits must be an integer.")
|
|
34
|
+
if isinstance(num_qubits, bool):
|
|
35
|
+
raise TypeError("num_qubits must be an integer.")
|
|
36
|
+
if num_qubits < 1:
|
|
37
|
+
raise ValueError("num_qubits must be at least 1.")
|
|
38
|
+
def _validate_function(function: Callable[[tuple[int, ...]], int],num_qubits: int,) -> dict[tuple[int, ...], int]:
|
|
39
|
+
if not callable(function):
|
|
40
|
+
raise TypeError("function must be callable.")
|
|
41
|
+
truth_table: dict[tuple[int, ...], int] = {}
|
|
42
|
+
for bits in product((0, 1), repeat=num_qubits):
|
|
43
|
+
value = function(bits)
|
|
44
|
+
if not isinstance(value, int) or isinstance(value, bool):
|
|
45
|
+
raise TypeError("function must return an integer value of 0 or 1.")
|
|
46
|
+
if value not in (0, 1):
|
|
47
|
+
raise ValueError("function must return either 0 or 1.")
|
|
48
|
+
truth_table[bits] = value
|
|
49
|
+
return truth_table
|
|
50
|
+
def _classify_truth_table(truth_table: dict[tuple[int, ...], int],) -> DeutschJozsaResult:
|
|
51
|
+
values = tuple(truth_table.values())
|
|
52
|
+
total = len(values)
|
|
53
|
+
ones = sum(values)
|
|
54
|
+
if ones == 0 or ones == total:
|
|
55
|
+
return "constant"
|
|
56
|
+
if ones * 2 == total:
|
|
57
|
+
return "balanced"
|
|
58
|
+
raise ValueError("function must be either constant or balanced.")
|
|
59
|
+
def _apply_oracle(circuit: Circuit,truth_table: dict[tuple[int, ...], int],num_qubits: int,) -> None:
|
|
60
|
+
ancilla = num_qubits
|
|
61
|
+
for bits, value in truth_table.items():
|
|
62
|
+
if value == 0:
|
|
63
|
+
continue
|
|
64
|
+
if num_qubits == 1:
|
|
65
|
+
if bits[0] == 1:
|
|
66
|
+
circuit.cx(0, ancilla)
|
|
67
|
+
else:
|
|
68
|
+
circuit.x(0)
|
|
69
|
+
circuit.cx(0, ancilla)
|
|
70
|
+
circuit.x(0)
|
|
71
|
+
continue
|
|
72
|
+
if num_qubits == 2:
|
|
73
|
+
_apply_two_controlled_x(
|
|
74
|
+
circuit,
|
|
75
|
+
bits,
|
|
76
|
+
ancilla,
|
|
77
|
+
)
|
|
78
|
+
continue
|
|
79
|
+
raise NotImplementedError(
|
|
80
|
+
"Deutsch–Jozsa oracles with more than 2 input qubits "
|
|
81
|
+
"require multi-controlled gates, which are not yet "
|
|
82
|
+
"exposed by the current OQubit Circuit API."
|
|
83
|
+
)
|
|
84
|
+
def _apply_two_controlled_x(circuit: Circuit,bits: tuple[int, ...],target: int,) -> None:
|
|
85
|
+
control0 = 0
|
|
86
|
+
control1 = 1
|
|
87
|
+
zero_controls = [
|
|
88
|
+
index
|
|
89
|
+
for index, bit in enumerate(bits)
|
|
90
|
+
if bit == 0
|
|
91
|
+
]
|
|
92
|
+
for qubit in zero_controls:
|
|
93
|
+
circuit.x(qubit)
|
|
94
|
+
circuit.ccx(control0,control1,target,)
|
|
95
|
+
for qubit in reversed(zero_controls):
|
|
96
|
+
circuit.x(qubit)
|
|
97
|
+
def deutsch_jozsa(function: Callable[[tuple[int, ...]], int],num_qubits: int,) -> DeutschJozsaResult:
|
|
98
|
+
_validate_num_qubits(num_qubits)
|
|
99
|
+
truth_table = _validate_function(function,num_qubits,)
|
|
100
|
+
_classify_truth_table(truth_table)
|
|
101
|
+
circuit = Circuit(num_qubits + 1)
|
|
102
|
+
ancilla = num_qubits
|
|
103
|
+
circuit.x(ancilla)
|
|
104
|
+
for qubit in range(num_qubits + 1):
|
|
105
|
+
circuit.h(qubit)
|
|
106
|
+
_apply_oracle(
|
|
107
|
+
circuit,
|
|
108
|
+
truth_table,
|
|
109
|
+
num_qubits,
|
|
110
|
+
)
|
|
111
|
+
for qubit in range(num_qubits):
|
|
112
|
+
circuit.h(qubit)
|
|
113
|
+
state = circuit.run()
|
|
114
|
+
result = circuit.measure(
|
|
115
|
+
state=state,
|
|
116
|
+
qubits=tuple(range(num_qubits)),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if all(bit == 0 for bit in result):
|
|
120
|
+
return "constant"
|
|
121
|
+
return "balanced"
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Deutsch–Jozsa algorithm.
|
|
3
|
+
|
|
4
|
+
This module implements the Deutsch–Jozsa algorithm for Boolean
|
|
5
|
+
functions of the form
|
|
6
|
+
|
|
7
|
+
f : {0, 1}^n -> {0, 1}
|
|
8
|
+
|
|
9
|
+
under the promise that ``f`` is either constant or balanced.
|
|
10
|
+
|
|
11
|
+
The algorithm determines whether the function is constant or
|
|
12
|
+
balanced using a single quantum-oracle evaluation.
|
|
13
|
+
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
The implementation constructs the oracle from elementary X and
|
|
17
|
+
controlled-X gates. The oracle therefore represents the transformation
|
|
18
|
+
|
|
19
|
+
U_f |x>|y> = |x>|y XOR f(x)>.
|
|
20
|
+
|
|
21
|
+
The first ``n`` qubits form the input register and the final qubit
|
|
22
|
+
is the oracle ancilla.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
from collections.abc import Callable, Iterable
|
|
26
|
+
from itertools import product
|
|
27
|
+
from typing import Literal
|
|
28
|
+
from ..circuit import Circuit
|
|
29
|
+
__all__ = ["deutsch_jozsa"]
|
|
30
|
+
DeutschJozsaResult = Literal["constant", "balanced"]
|
|
31
|
+
def _validate_num_qubits(num_qubits: int) -> None:
|
|
32
|
+
if not isinstance(num_qubits, int):
|
|
33
|
+
raise TypeError("num_qubits must be an integer.")
|
|
34
|
+
if isinstance(num_qubits, bool):
|
|
35
|
+
raise TypeError("num_qubits must be an integer.")
|
|
36
|
+
if num_qubits < 1:
|
|
37
|
+
raise ValueError("num_qubits must be at least 1.")
|
|
38
|
+
def _validate_function(function: Callable[[tuple[int, ...]], int],num_qubits: int,) -> dict[tuple[int, ...], int]:
|
|
39
|
+
if not callable(function):
|
|
40
|
+
raise TypeError("function must be callable.")
|
|
41
|
+
truth_table: dict[tuple[int, ...], int] = {}
|
|
42
|
+
for bits in product((0, 1), repeat=num_qubits):
|
|
43
|
+
value = function(bits)
|
|
44
|
+
if not isinstance(value, int) or isinstance(value, bool):
|
|
45
|
+
raise TypeError("function must return an integer value of 0 or 1.")
|
|
46
|
+
if value not in (0, 1):
|
|
47
|
+
raise ValueError("function must return either 0 or 1.")
|
|
48
|
+
truth_table[bits] = value
|
|
49
|
+
return truth_table
|
|
50
|
+
def _classify_truth_table(truth_table: dict[tuple[int, ...], int],) -> DeutschJozsaResult:
|
|
51
|
+
values = tuple(truth_table.values())
|
|
52
|
+
total = len(values)
|
|
53
|
+
ones = sum(values)
|
|
54
|
+
if ones == 0 or ones == total:
|
|
55
|
+
return "constant"
|
|
56
|
+
if ones * 2 == total:
|
|
57
|
+
return "balanced"
|
|
58
|
+
raise ValueError("function must be either constant or balanced.")
|
|
59
|
+
def _apply_oracle(circuit: Circuit,truth_table: dict[tuple[int, ...], int],num_qubits: int,) -> None:
|
|
60
|
+
ancilla = num_qubits
|
|
61
|
+
for bits, value in truth_table.items():
|
|
62
|
+
if value == 0:
|
|
63
|
+
continue
|
|
64
|
+
if num_qubits == 1:
|
|
65
|
+
if bits[0] == 1:
|
|
66
|
+
circuit.cx(0, ancilla)
|
|
67
|
+
else:
|
|
68
|
+
circuit.x(0)
|
|
69
|
+
circuit.cx(0, ancilla)
|
|
70
|
+
circuit.x(0)
|
|
71
|
+
continue
|
|
72
|
+
if num_qubits == 2:
|
|
73
|
+
_apply_two_controlled_x(
|
|
74
|
+
circuit,
|
|
75
|
+
bits,
|
|
76
|
+
ancilla,
|
|
77
|
+
)
|
|
78
|
+
continue
|
|
79
|
+
raise NotImplementedError(
|
|
80
|
+
"Deutsch–Jozsa oracles with more than 2 input qubits "
|
|
81
|
+
"require multi-controlled gates, which are not yet "
|
|
82
|
+
"exposed by the current OQubit Circuit API."
|
|
83
|
+
)
|
|
84
|
+
def _apply_two_controlled_x(circuit: Circuit,bits: tuple[int, ...],target: int,) -> None:
|
|
85
|
+
control0 = 0
|
|
86
|
+
control1 = 1
|
|
87
|
+
zero_controls = [
|
|
88
|
+
index
|
|
89
|
+
for index, bit in enumerate(bits)
|
|
90
|
+
if bit == 0
|
|
91
|
+
]
|
|
92
|
+
for qubit in zero_controls:
|
|
93
|
+
circuit.x(qubit)
|
|
94
|
+
circuit.ccx(control0,control1,target,)
|
|
95
|
+
for qubit in reversed(zero_controls):
|
|
96
|
+
circuit.x(qubit)
|
|
97
|
+
def deutsch_jozsa(function: Callable[[tuple[int, ...]], int],num_qubits: int,) -> DeutschJozsaResult:
|
|
98
|
+
_validate_num_qubits(num_qubits)
|
|
99
|
+
truth_table = _validate_function(function,num_qubits,)
|
|
100
|
+
_classify_truth_table(truth_table)
|
|
101
|
+
circuit = Circuit(num_qubits + 1)
|
|
102
|
+
ancilla = num_qubits
|
|
103
|
+
circuit.x(ancilla)
|
|
104
|
+
for qubit in range(num_qubits + 1):
|
|
105
|
+
circuit.h(qubit)
|
|
106
|
+
_apply_oracle(
|
|
107
|
+
circuit,
|
|
108
|
+
truth_table,
|
|
109
|
+
num_qubits,
|
|
110
|
+
)
|
|
111
|
+
for qubit in range(num_qubits):
|
|
112
|
+
circuit.h(qubit)
|
|
113
|
+
state = circuit.run()
|
|
114
|
+
result = circuit.measure(
|
|
115
|
+
state=state,
|
|
116
|
+
qubits=tuple(range(num_qubits)),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if result==0:
|
|
120
|
+
return "constant"
|
|
121
|
+
return "balanced"
|
oqubit/algorithms/qft.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Fourier Transform (QFT).
|
|
3
|
+
|
|
4
|
+
This module provides a circuit-level implementation of the Quantum
|
|
5
|
+
Fourier Transform.
|
|
6
|
+
|
|
7
|
+
The QFT on n qubits is defined as:
|
|
8
|
+
|
|
9
|
+
|x> -> 1 / sqrt(2^n) * sum_y exp(2πixy / 2^n) |y>
|
|
10
|
+
|
|
11
|
+
The implementation uses:
|
|
12
|
+
- Hadamard gates
|
|
13
|
+
- Controlled phase rotations
|
|
14
|
+
- SWAP gates for final bit reversal
|
|
15
|
+
|
|
16
|
+
Notes
|
|
17
|
+
-----
|
|
18
|
+
The QFT is implemented as a circuit transformation. It does not
|
|
19
|
+
directly manipulate state vectors or simulator internals.
|
|
20
|
+
|
|
21
|
+
Examples
|
|
22
|
+
--------
|
|
23
|
+
>>> from oqubit.circuit import Circuit
|
|
24
|
+
>>> from oqubit.algorithms.qft import qft
|
|
25
|
+
|
|
26
|
+
>>> circuit = Circuit(3)
|
|
27
|
+
>>> qft(circuit)
|
|
28
|
+
|
|
29
|
+
The resulting circuit contains the QFT operation on all three qubits.
|
|
30
|
+
|
|
31
|
+
A subset of qubits can also be transformed:
|
|
32
|
+
|
|
33
|
+
>>> circuit = Circuit(5)
|
|
34
|
+
>>> qft(circuit, qubits=[1, 2, 3])
|
|
35
|
+
"""
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
import math
|
|
38
|
+
from collections.abc import Sequence
|
|
39
|
+
from oqubit.circuit import Circuit
|
|
40
|
+
from oqubit.gates import H, SWAP
|
|
41
|
+
from oqubit.gates.controlled import ControlledPhase
|
|
42
|
+
__all__ = ["qft"]
|
|
43
|
+
|
|
44
|
+
def qft(
|
|
45
|
+
circuit: Circuit,
|
|
46
|
+
qubits: Sequence[int] | None = None,
|
|
47
|
+
) -> Circuit:
|
|
48
|
+
if not isinstance(circuit, Circuit):
|
|
49
|
+
raise TypeError("circuit must be an instance of Circuit.")
|
|
50
|
+
if qubits is None:
|
|
51
|
+
qubit_indices = list(range(circuit.num_qubits))
|
|
52
|
+
else:
|
|
53
|
+
if isinstance(qubits, (str, bytes)):
|
|
54
|
+
raise TypeError("qubits must be a sequence of integers.")
|
|
55
|
+
qubit_indices = list(qubits)
|
|
56
|
+
if not all(isinstance(q, int) for q in qubit_indices):
|
|
57
|
+
raise TypeError("qubits must contain only integers.")
|
|
58
|
+
if not qubit_indices:
|
|
59
|
+
raise ValueError("QFT requires at least one qubit.")
|
|
60
|
+
if len(set(qubit_indices)) != len(qubit_indices):
|
|
61
|
+
raise ValueError("QFT qubit indices must be unique.")
|
|
62
|
+
for qubit in qubit_indices:
|
|
63
|
+
if qubit < 0 or qubit >= circuit.num_qubits:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"Qubit index {qubit} is outside the circuit."
|
|
66
|
+
)
|
|
67
|
+
n = len(qubit_indices)
|
|
68
|
+
for j in range(n):
|
|
69
|
+
target = qubit_indices[j]
|
|
70
|
+
circuit.add(H, target)
|
|
71
|
+
for k in range(2, n - j + 1):
|
|
72
|
+
control = qubit_indices[j + k - 1]
|
|
73
|
+
angle = 2.0 * math.pi / (2**k)
|
|
74
|
+
circuit.add(
|
|
75
|
+
ControlledPhase(angle),
|
|
76
|
+
control,
|
|
77
|
+
target,
|
|
78
|
+
)
|
|
79
|
+
for i in range(n // 2):
|
|
80
|
+
circuit.add(
|
|
81
|
+
SWAP,
|
|
82
|
+
qubit_indices[i],
|
|
83
|
+
qubit_indices[n - i - 1],
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return circuit
|
|
87
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Superdense Coding algorithm.
|
|
3
|
+
|
|
4
|
+
Superdense coding transmits two classical bits using one qubit
|
|
5
|
+
and a previously shared entangled Bell pair.
|
|
6
|
+
"""
|
|
7
|
+
from ..circuit import Circuit
|
|
8
|
+
__all__ = ["encode", "decode"]
|
|
9
|
+
def encode(bit0, bit1):
|
|
10
|
+
if bit0 not in (0, 1):
|
|
11
|
+
raise ValueError("bit0 must be 0 or 1.")
|
|
12
|
+
if bit1 not in (0, 1):
|
|
13
|
+
raise ValueError("bit1 must be 0 or 1.")
|
|
14
|
+
circuit = Circuit(2)
|
|
15
|
+
circuit.h(0)
|
|
16
|
+
circuit.cx(0, 1)
|
|
17
|
+
if bit1:
|
|
18
|
+
circuit.x(0)
|
|
19
|
+
if bit0:
|
|
20
|
+
circuit.z(0)
|
|
21
|
+
return circuit
|
|
22
|
+
def decode(circuit):
|
|
23
|
+
if not isinstance(circuit, Circuit):
|
|
24
|
+
raise TypeError("circuit must be an OQubit Circuit.")
|
|
25
|
+
circuit.cx(0, 1)
|
|
26
|
+
circuit.h(0)
|
|
27
|
+
state = circuit.run()
|
|
28
|
+
return circuit.measure(state=state,qubits=(0, 1),)
|