diff --git a/qrng/__init__.py b/qrng/__init__.py index 6abdd93..b422bc0 100644 --- a/qrng/__init__.py +++ b/qrng/__init__.py @@ -2,10 +2,13 @@ from qiskit import IBMQ import math import struct +from typing import Union _circuit = None -_bitCache = '' -def set_provider_as_IBMQ(token: str = None): +_bitCache = "" + + +def set_provider_as_IBMQ(token: str = str(None)): """ Sets the backend provider to IBMQ with the given account token. Will fall back to a local (simulated) provider if no token is given. @@ -13,24 +16,27 @@ def set_provider_as_IBMQ(token: str = None): token (string): Account token on IBMQ. If no token is given, will fall back to a local provider. """ global provider - if token == None or '': - provider = qiskit.BasicAer - else: + if token == None or "": + provider = qiskit.BasicAer + else: IBMQ.save_account(token) IBMQ.load_account() - provider = IBMQ.get_provider('ibm-q') - + provider = IBMQ.get_provider("ibm-q") + + def _set_qubits(n): global _circuit qr = qiskit.QuantumRegister(n) cr = qiskit.ClassicalRegister(n) _circuit = qiskit.QuantumCircuit(qr, cr) - _circuit.h(qr) # Apply Hadamard gate to qubits - _circuit.measure(qr,cr) # Collapses qubit to either 1 or 0 w/ equal prob. + _circuit.h(qr) # Apply Hadamard gate to qubits + _circuit.measure(qr, cr) # Collapses qubit to either 1 or 0 w/ equal prob. + -_set_qubits(8) # Default Circuit is 8 Qubits - -def set_backend(backend: str = 'qasm_simulator'): +_set_qubits(8) # Default Circuit is 8 Qubits + + +def set_backend(backend: str = "qasm_simulator"): """ Sets the backend to one of the provider's available backends (quantum computers/simulators). @@ -39,26 +45,34 @@ def set_backend(backend: str = 'qasm_simulator'): """ global _backend global provider - available_backends = provider.backends(backend, filters = lambda x: x.status().operational == True) - if (backend != '') and (backend in str(available_backends)): + available_backends = provider.backends( + backend, filters=lambda x: x.status().operational == True + ) + if (backend != "") and (backend in str(available_backends)): _backend = provider.get_backend(backend) else: - print(str(backend)+' is not available. Backend is set to qasm_simulator.') - _backend = qiskit.BasicAer.get_backend('qasm_simulator') + print(str(backend) + " is not available. Backend is set to qasm_simulator.") + _backend = qiskit.BasicAer.get_backend("qasm_simulator") _set_qubits(_backend.configuration().n_qubits) + # Strips QISKit output to just a bitstring. def _bit_from_counts(counts): return [k for k, v in counts.items() if v == 1][0] + # Populates the bitCache with at least n more bits. def _request_bits(n): global _bitCache - iterations = math.ceil(n/_circuit.width()*2) - for _ in range(iterations): - # Create new job and run the quantum circuit - job = qiskit.execute(_circuit, _backend, shots=1) - _bitCache += _bit_from_counts(job.result().get_counts()) + if _circuit == None: + print("Circuit is None") + else: + iterations = math.ceil(n / _circuit.width() * 2) + for _ in range(iterations): + # Create new job and run the quantum circuit + job = qiskit.execute(_circuit, _backend, shots=1) + _bitCache += _bit_from_counts(job.result().get_counts()) + def get_bit_string(n: int) -> str: """ @@ -69,11 +83,12 @@ def get_bit_string(n: int) -> str: """ global _bitCache if len(_bitCache) < n: - _request_bits(n-len(_bitCache)) + _request_bits(n - len(_bitCache)) bitString = _bitCache[0:n] _bitCache = _bitCache[n:] return bitString + # Running time is probabalistic but complexity is still O(n) def get_random_int(min: int, max: int) -> int: """ @@ -83,23 +98,27 @@ def get_random_int(min: int, max: int) -> int: min (int): The minimum possible returned integer. max (int): The maximum possible returned integer. """ - delta = max-min - n = math.floor(math.log(delta,2))+1 - result = int(get_bit_string(n),2) - while(result > delta): - result = int(get_bit_string(n),2) + delta = max - min + n = math.floor(math.log(delta, 2)) + 1 + result = int(get_bit_string(n), 2) + while result > delta: + result = int(get_bit_string(n), 2) result += min return result + # def getRandomIntEntaglement(min,max): + def get_random_int32() -> int: """Returns a uniformly random 32 bit integer.""" - return int(get_bit_string(32),2) + return int(get_bit_string(32), 2) + def get_random_int64() -> int: """Returns a uniformly random 64 bit integer.""" - return int(get_bit_string(64),2) + return int(get_bit_string(64), 2) + def get_random_float(min: float = 0, max: float = 1) -> float: """ @@ -112,9 +131,10 @@ def get_random_float(min: float = 0, max: float = 1) -> float: Algorithm provided by: https://experilous.com/1/blog/post/perfect-fast-random-floating-point-numbers. """ unpacked = 0x3F800000 | get_random_int32() >> 9 - packed = struct.pack('I',unpacked) - value = struct.unpack('f',packed)[0] - 1.0 - return (max-min)*value+min # Scale float to given range + packed = struct.pack("I", unpacked) + value = struct.unpack("f", packed)[0] - 1.0 + return (max - min) * value + min # Scale float to given range + def get_random_double(min: float = 0, max: float = 1) -> float: """ @@ -127,13 +147,22 @@ def get_random_double(min: float = 0, max: float = 1) -> float: Algorithm provided by: https://experilous.com/1/blog/post/perfect-fast-random-floating-point-numbers. """ unpacked = 0x3FF0000000000000 | get_random_int64() >> 12 - packed = struct.pack('Q',unpacked) - value = struct.unpack('d',packed)[0] - 1.0 - return (max-min)*value+min # Scale double to given range + packed = struct.pack("Q", unpacked) + value = struct.unpack("d", packed)[0] - 1.0 + return (max - min) * value + min # Scale double to given range + # Returns a random complex with both real and imaginary parts # from the given ranges. If no imaginary range specified, real range used. -def get_random_complex_rect(real_min: float = 0, real_max: float = 0, img_min: float | None = None, img_max: float | None = None) -> complex: +from typing import Union + + +def get_random_complex_rect( + real_min: float = 0, + real_max: float = 0, + img_min: Union[float, None] = None, + img_max: Union[float, None] = None, +) -> complex: """ Returns a random complex number with: Real-part: a uniformly sampled single-precision float from the range [real_min,real_max). @@ -149,11 +178,12 @@ def get_random_complex_rect(real_min: float = 0, real_max: float = 0, img_min: f img_min = real_min if img_max is None: img_max = real_max - re = get_random_float(real_min,real_max) - im = get_random_float(img_min,img_min) - return re+im*1j + re = get_random_float(real_min, real_max) + im = get_random_float(img_min, img_max) + return re + im * 1j + -def get_random_complex_polar(r: float = 1, theta: float = 2*math.pi) -> complex: +def get_random_complex_polar(r: float = 1, theta: float = 2 * math.pi) -> complex: """ Returns a random complex uniformly sampled from an arc sweeping across the complex plane, starting at theta = 0. @@ -161,6 +191,6 @@ def get_random_complex_polar(r: float = 1, theta: float = 2*math.pi) -> complex: r (float): The radius of the arc being sampled. Default is 1. theta (float): The ending angle of the arc. """ - r0 = r * math.sqrt(get_random_float(0,1)) - theta0 = get_random_float(0,theta) - return r0*math.cos(theta0)+r0*math.sin(theta0)*1j + r0 = r * math.sqrt(get_random_float(0, 1)) + theta0 = get_random_float(0, theta) + return r0 * math.cos(theta0) + r0 * math.sin(theta0) * 1j