From e839ac4006fc568c0c1e49af2b1a25b98fc79728 Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Tue, 24 Sep 2019 22:41:16 +0200 Subject: [PATCH] Quantum computer, quantum key distribution algos --- .gitignore | 2 + computer.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++ qkd.py | 105 ++++++++++++++++++++++++++++++++++++++++++ test_generator.py | 24 ++++++++++ 4 files changed, 244 insertions(+) create mode 100644 .gitignore create mode 100644 computer.py create mode 100644 qkd.py create mode 100644 test_generator.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82195aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +venv +.idea \ No newline at end of file diff --git a/computer.py b/computer.py new file mode 100644 index 0000000..00f9c8c --- /dev/null +++ b/computer.py @@ -0,0 +1,113 @@ +import random +from math import sqrt, log + + +class Gate(list): + # Matrix multiplication + def __mul__(self, other): + rv = Gate() + for kol in range(len(other[0])): + for _ in range(len(self)): + rv.append([0]) + # iterate through rows of a + for i in range(len(self)): + # iterate through columns of b + for j in range(len(other[0])): + # iterate through rows of b + for k in range(len(other)): + rv[i][j] += self[i][k] * other[k][j] + return rv + + +class Qubit(list): + # tensor multiplication + def __mul__(self, other): + rv = Qubit() + for i in self: + for j in other: + rv.append([i[0] * j[0]]) + return rv + + +def measure(qubits): + """ + Measure result from computation + """ + probabilites = [q[0] ** 2 for q in qubits] + return bin(random.choices( + population=range(len(qubits)), + weights=probabilites + )[0]) + + +_0 = Qubit([[1], [0]]) +_1 = Qubit([[0], [1]]) + +I = Gate([ + [1, 0], + [0, 1], +]) +X = Gate([ + [0, 1], + [1, 0], +]) +Y = Gate([ + [0, complex(0, -1)], + [complex(0, 1), 0], +]) +Z = Gate([ + [1, 0], + [0, -1], +]) +CNOT = Gate([ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], +]) +H = Gate([ + [1 / sqrt(2), 1 / sqrt(2)], + [1 / sqrt(2), -1 / sqrt(2)], +]) + + +def main(): + # TEST IDENTITY + assert _1 == I * _1 + assert _0 == I * _0 + + # TEST BIT-FLIP + assert _1 == X * _0 + assert _0 == X * _1 + + # TEST CNOT GATE - Control * Target + # Control is FALSE, so target doesn't change + assert CNOT * (_0 * _0) == (_0 * _0) + assert CNOT * (_0 * _1) == (_0 * _1) + # Control is TRUE, so target MUST change + assert CNOT * (_1 * _0) == (_1 * _1) + assert CNOT * (_1 * _1) == (_1 * _0) + + # TEST HADAMARD + superposition = Qubit(H * _0) * Qubit(H * _0) + print(superposition) + CNOT_superposition = Qubit(CNOT * superposition) + print(CNOT_superposition) + + print(measure(CNOT_superposition)) + # print(INVERTED_CNOT) + + # TEST MY MEMORY + # rv = _0 + # QUBITS = 23 + # print(2**QUBITS) + # print("{:.1f} KB".format(2**QUBITS / 2**10)) + # print("{:.1f} MB".format(2**QUBITS / 2**20)) + # print("{:.1f} GB".format(2**QUBITS / 2**30)) + # for i in range(QUBITS): + # rv *= _0 + # print(len(rv)) + + +if __name__ == "__main__": + main() diff --git a/qkd.py b/qkd.py new file mode 100644 index 0000000..0d326c4 --- /dev/null +++ b/qkd.py @@ -0,0 +1,105 @@ +from dataclasses import dataclass, field + +import random + +STREAM_LENGTH = 100 + + +@dataclass +class Photon(object): + basis: int + value: int + _measured: bool = False + _measured_value: bool = None + + def measure(self, basis): + self._measured = True + if self.basis == basis: + return self.value + else: + if not self._measured: + self._measured_value = random.randint(0, 1) + return self._measured_value + + def __repr__(self): + return '{}:{}'.format(self.basis, self.value) + + +@dataclass +class Alice: + _otp: list = None + bases: list = field(default_factory=list) + _values: list = field(default_factory=list) + + def encode_qbit(self, value): + basis = random.randint(0, 1) + self.bases.append(basis) + return Photon(basis, value) + + def generate_stream(self, stream_size=10): + for _ in range(stream_size): + qbit = random.randint(0, 1) + self._values.append(qbit) + yield self.encode_qbit(qbit) + + def set_otp(self, other_correct): + self._otp = [v for c, v in zip(other_correct, self._values) if c] + + def get_otp_length(self): + return len(self._otp) + + +@dataclass +class Bob: + correct: list = None + _otp: list = None + _bases: list = field(default_factory=list) + _values: list = field(default_factory=list) + + def ingest_stream(self, stream): + for photon in stream: + basis = random.randint(0, 1) + self._bases.append(basis) + qbit_value = photon.measure(basis) + self._values.append(qbit_value) + + def insecure_get_bases(self, other_bases): + self.correct = [not (a ^ b) for a, b in zip(self._bases, other_bases)] + self._otp = [v for c, v in zip(self.correct, self._values) if c] + + +@dataclass +class Mallory: + _bases: list = field(default_factory=list) + _values: list = field(default_factory=list) + + def ingest_stream(self, stream): + for photon in stream: + basis = random.randint(0, 1) + self._bases.append(basis) + qbit_value = photon.measure(basis) + self._values.append(qbit_value) + yield photon + + +def main(): + alice, bob, mallory = Alice(), Bob(), Mallory() + + # Step 1. Generate a stream of random photons polarizations in a random basis + stream = alice.generate_stream(STREAM_LENGTH) + + # Step 1.5 MALLORY ALSO INGESTS AND COPIES THE PHOTONS + stream = mallory.ingest_stream(stream) + + # Step 2. Bob ingests the stream of photons + bob.ingest_stream(stream) + + bob.insecure_get_bases(alice.bases) + alice.set_otp(bob.correct) + + assert alice._otp == bob._otp + print("OTP Length: {}".format(alice.get_otp_length())) + + +if __name__ == "__main__": + main() diff --git a/test_generator.py b/test_generator.py new file mode 100644 index 0000000..a5faaee --- /dev/null +++ b/test_generator.py @@ -0,0 +1,24 @@ +def gen(): + print("start gen") + for i in range(30): + print('gen {}'.format(i)) + yield i + + +def ingest(stream): + print("start ingest+") + for number in stream: + print("injest {}".format(number)) + yield number + + +def main(): + print("main") + stream = gen() + something = ingest(stream) + for i in something: + print(i) + + +if __name__ == "__main__": + main()