diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8ebdc17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +dist/ +build/ +*.egg-info/ diff --git a/build/lib/qrng/__init__.py b/build/lib/qrng/__init__.py deleted file mode 100644 index 4383601..0000000 --- a/build/lib/qrng/__init__.py +++ /dev/null @@ -1,122 +0,0 @@ -name = "qrng" - -import qiskit -from qiskit import IBMQ -import math -import struct - -_circuit = None -_bitCache = '' -def set_provider_as_IBMQ(token): - global provider - if token == '': - provider = qiskit.BasicAer - else: - IBMQ.save_account(token) - IBMQ.load_account() - 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. - -_set_qubits(8) # Default Circuit is 8 Qubits - -def set_backend(b = 'qasm_simulator'): - global _backend - global provider - if b == 'ibmq_london' or b == 'ibmq_burlington' or b == 'ibmq_essex'\ - or b == 'ibmq_ourense' or b == 'ibmq_vigo' or b == 'ibmqx2' : - _backend = provider.get_backend(b) - _set_qubits(5) - elif b == 'ibmq_16_melbourne': - _backend = provider.get_backend(b) - _set_qubits(15) - elif b == 'ibmq_armonk': - _backend = provider.get_backend(b) - _set_qubits(1) - elif b == 'ibmq_qasm_simulator': - _backend = provider.get_backend(b) - _set_qubits(32) - else: - _backend = qiskit.BasicAer.get_backend('qasm_simulator') - _set_qubits(8) - -# 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()) - -# Returns a random n-bit string by popping n bits from bitCache. -def get_bit_string(n): - global _bitCache - if len(_bitCache) < n: - _request_bits(n-len(_bitCache)) - bitString = _bitCache[0:n] - _bitCache = _bitCache[n:] - return bitString - -# Returns a random integer between and including [min, max]. -# Running time is probabalistic but complexity is still O(n) -def get_random_int(min,max): - 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) - return result+min - -# def getRandomIntEntaglement(min,max): - -# Returns a random 32 bit integer -def get_random_int32(): - return int(get_bit_string(32),2) - -# Returns a random 64 bit integer -def get_random_int64(): - return int(get_bit_string(64),2) - -# Returns a random float from a uniform distribution in the range [min, max). -def get_random_float(min,max): - # Get random float from [0,1) - 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 - -# Returns a random double from a uniform distribution in the range [min, max). -def get_random_double(min,max): - # Get random double from [0,1) - 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 - -# 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(r1,r2,i1=None,i2=None): - re = get_random_float(r1,r2) - if i1 == None or i2 == None: - im = get_random_float(r1,r2) - else: - im = get_random_float(i1,i2) - return re+im*1j - -# Returns a random complex in rectangular form from a given polar range. -# If no max angle given, [0,2pi) used. -def get_random_complex_polar(r,theta=2*math.pi): - 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 diff --git a/dist/qrng-0.1.0-py3-none-any.whl b/dist/qrng-0.1.0-py3-none-any.whl deleted file mode 100644 index 5d4f0f8..0000000 Binary files a/dist/qrng-0.1.0-py3-none-any.whl and /dev/null differ diff --git a/dist/qrng-0.1.0.tar.gz b/dist/qrng-0.1.0.tar.gz deleted file mode 100644 index 8f6acf5..0000000 Binary files a/dist/qrng-0.1.0.tar.gz and /dev/null differ diff --git a/dist/qrng-0.1.1-py3-none-any.whl b/dist/qrng-0.1.1-py3-none-any.whl deleted file mode 100644 index ec4f302..0000000 Binary files a/dist/qrng-0.1.1-py3-none-any.whl and /dev/null differ diff --git a/dist/qrng-0.1.1.1-py3-none-any.whl b/dist/qrng-0.1.1.1-py3-none-any.whl deleted file mode 100644 index 540f672..0000000 Binary files a/dist/qrng-0.1.1.1-py3-none-any.whl and /dev/null differ diff --git a/dist/qrng-0.1.1.1.tar.gz b/dist/qrng-0.1.1.1.tar.gz deleted file mode 100644 index 2fe9f7d..0000000 Binary files a/dist/qrng-0.1.1.1.tar.gz and /dev/null differ diff --git a/dist/qrng-0.1.1.tar.gz b/dist/qrng-0.1.1.tar.gz deleted file mode 100644 index 975ca0e..0000000 Binary files a/dist/qrng-0.1.1.tar.gz and /dev/null differ diff --git a/dist/qrng-0.1.2-py3-none-any.whl b/dist/qrng-0.1.2-py3-none-any.whl deleted file mode 100644 index ae23d40..0000000 Binary files a/dist/qrng-0.1.2-py3-none-any.whl and /dev/null differ diff --git a/dist/qrng-0.1.2.tar.gz b/dist/qrng-0.1.2.tar.gz deleted file mode 100644 index 38f238c..0000000 Binary files a/dist/qrng-0.1.2.tar.gz and /dev/null differ diff --git a/qrng.egg-info/PKG-INFO b/qrng.egg-info/PKG-INFO deleted file mode 100644 index 845eb6f..0000000 --- a/qrng.egg-info/PKG-INFO +++ /dev/null @@ -1,72 +0,0 @@ -Metadata-Version: 2.1 -Name: qrng -Version: 0.1.2 -Summary: A Quantum Random Number Generator using IBM's Qiskit -Home-page: https://github.com/ozanerhansha/qRNG -Author: Ozaner Hansha -Author-email: ozanerhansha@gmail.com -License: UNKNOWN -Description:

- -

- - ----------------- - - **qRNG** is an open-source quantum random number generator written in python. It achieves this by using IBM's [QISKit](https://qiskit.org/) API to communicate with any one of their 3 publicly accessible quantum computers: - - - `ibmqx4` - - `ibmqx5` - - `ibmqx_16_melborne` - - ## Installation - You can use the pip package manager to install the [current release](https://pypi.org/project/qrng/) of qRNG (along with its dependencies): - ``` - pip install qrng - ``` - - Upgrading is as simple as: - ``` - pip install qrng -U - ``` - ## Tutorial - Now you can try generating your first random number. First open python in the shell or use an IDE: - ```shell - $ python - ``` - Now try generating a random 32-bit integer (note that until a particular quantum computer has been specified, qRNG uses a simulator rather than a real QPC): - ```python - >>> import qrng - >>> qrng.get_random_int32() - 3408681298 - ``` - - - - ## What is Random Number Generation? - There are a variety of applications that require a source of random data in order to work effectively (e.g. simulations and cryptography). To that end, we make use of random number generators (RNGs) to generate sequences of numbers that are, ideally, indistinguishable from random noise. - - There are two types of RNGs: Pseudo-RNGs (PRNGs) and True RNGs (TRNGs). Pseudo-RNGs, while not truly and statistically random, are used in a variety of applications as their output is 'random enough' for many purposes. - - For a True RNG, however, an actual piece of hardware is required to measure some random process in the real world as no computer program could suffice due to being deterministic in nature. These devices vary from apparatuses that measure atmospheric noise to pieces of radioactive material connected via USB. - - ## Why Quantum? - Modern physics has shown us that there are really only two types of events that can happen in the universe: the unitary transformation of a quantum system, and quantum wavefunction collapse (i.e. **measurement**). The former being a totally deterministic process and the latter being a random one. - - Indeed, all randomness in the universe (as far we know) is the result of the collapse of quantum systems upon measurement. In a sense, this is randomness in its purest form and the underlying source of it in any TRNG. - - The point of this package then, besides it being a fun side project, is to cut out the middle man entirely, whether it be a radioactive isotope or the thermal noise in your PC, and simply measure an actual quantum system. For example, we can prepare the following state in a quantum computer: - -

- -

- - There is a 50-50 chance of measuring the above state as a 0 or 1 and we can continually iterate this process for as many random bits as we require. Note that while such a simple algorithm doesn't require a full-blown quantum computer, there are some random algorithms that do. - - ## Practicality - Of course, while the numbers generated from a quantum computer are amongst the most random, the practicality of connecting to one of IBM's quantum computers to generate a large amount of these numbers is nonexistent. For most real world use cases that require such high-caliber random numbers, an off the shelf hardware RNG would suffice. The purpose of this package is thus to provide a working example of how a real cloud based quantum random number generator may operate. - -Platform: UNKNOWN -Classifier: Programming Language :: Python :: 3 -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Description-Content-Type: text/markdown diff --git a/qrng.egg-info/SOURCES.txt b/qrng.egg-info/SOURCES.txt deleted file mode 100644 index 9b753ae..0000000 --- a/qrng.egg-info/SOURCES.txt +++ /dev/null @@ -1,7 +0,0 @@ -README.md -setup.py -qrng/__init__.py -qrng.egg-info/PKG-INFO -qrng.egg-info/SOURCES.txt -qrng.egg-info/dependency_links.txt -qrng.egg-info/top_level.txt \ No newline at end of file diff --git a/qrng.egg-info/dependency_links.txt b/qrng.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/qrng.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/qrng.egg-info/top_level.txt b/qrng.egg-info/top_level.txt deleted file mode 100644 index a3eb737..0000000 --- a/qrng.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -qrng diff --git a/qrng/__init__.py b/qrng/__init__.py index 835bdeb..331747b 100644 --- a/qrng/__init__.py +++ b/qrng/__init__.py @@ -1,5 +1,3 @@ -name = "qrng" - import qiskit from qiskit import IBMQ import math @@ -7,14 +5,20 @@ _circuit = None _bitCache = '' -def set_provider_as_IBMQ(token): - global provider - if token == '': - provider = qiskit.BasicAer - else: - IBMQ.save_account(token) - IBMQ.load_account() - provider = IBMQ.get_provider('ibm-q') +def set_provider_as_IBMQ(token: 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. + + Parameters: + 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: + IBMQ.save_account(token) + IBMQ.load_account() + provider = IBMQ.get_provider('ibm-q') def _set_qubits(n): global _circuit @@ -26,14 +30,20 @@ def _set_qubits(n): _set_qubits(8) # Default Circuit is 8 Qubits -def set_backend(b = 'qasm_simulator'): +def set_backend(backend: str = 'qasm_simulator'): + """ + Sets the backend to one of the provider's available backends (quantum computers/simulators). + + Parameters: + backend (string): Codename for the backend. If no backend is given, a default (simulated) backend will be used. + """ global _backend global provider - available_backends = provider.backends(b, filters = lambda x: x.status().operational == True) - if (b is not '') and (b in str(available_backends)): - _backend = provider.get_backend(b) + 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(b)+' is not available. Backend is set to 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) @@ -50,8 +60,13 @@ def _request_bits(n): job = qiskit.execute(_circuit, _backend, shots=1) _bitCache += _bit_from_counts(job.result().get_counts()) -# Returns a random n-bit string by popping n bits from bitCache. -def get_bit_string(n): +def get_bit_string(n: int) -> str: + """ + Returns a random n-bit bitstring. + + Parameters: + n (int): Account token on IBMQ. If no token is given, will fall back to a local provider. + """ global _bitCache if len(_bitCache) < n: _request_bits(n-len(_bitCache)) @@ -59,37 +74,54 @@ def get_bit_string(n): _bitCache = _bitCache[n:] return bitString -# Returns a random integer between and including [min, max]. # Running time is probabalistic but complexity is still O(n) -def get_random_int(min,max): +def get_random_int(min: int, max: int) -> int: + """ + Returns a random int from [min, max] (bounds are inclusive). + + Parameters: + 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) - return result+min + result += min + return result # def getRandomIntEntaglement(min,max): -# Returns a random 32 bit integer -def get_random_int32(): +def get_random_int32() -> int: + """Returns a uniformly random 32 bit integer.""" return int(get_bit_string(32),2) -# Returns a random 64 bit integer -def get_random_int64(): +def get_random_int64() -> int: + """Returns a uniformly random 64 bit integer.""" return int(get_bit_string(64),2) -# Returns a random float from a uniform distribution in the range [min, max). -def get_random_float(min,max): - # Get random float from [0,1) +def get_random_float(min: float = 0, max: float = 1) -> float: + """ + Returns a uniformly random single-precision float from the range [min,max). + + Parameters: + min (float): The minimum possible returned float (inclusive). Default is 0.0 + max (float): The maximum possible returned float (exclusive). Default is 1.0 + """ 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 -# Returns a random double from a uniform distribution in the range [min, max). -def get_random_double(min,max): - # Get random double from [0,1) +def get_random_double(min: float = 0, max: float = 1) -> float: + """ + Returns a uniformly random double-precision float from the range [min,max). + + Parameters: + min (float): The minimum possible returned double (inclusive). Default is 0.0 + max (float): The maximum possible returned double (exclusive). Default is 1.0 + """ unpacked = 0x3FF0000000000000 | get_random_int64() >> 12 packed = struct.pack('Q',unpacked) value = struct.unpack('d',packed)[0] - 1.0 @@ -97,17 +129,34 @@ def get_random_double(min,max): # 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(r1,r2,i1=None,i2=None): - re = get_random_float(r1,r2) - if i1 == None or i2 == None: - im = get_random_float(r1,r2) - else: - im = get_random_float(i1,i2) +def get_random_complex_rect(real_min: float = 0, real_max: float = 0, img_min: float | None = None, img_max: 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). + Imaginary-part: a uniformly sampled single-precision float from the range [img_min,img_max). + + Parameters: + real_min (float): The minimum possible real component (inclusive). Default is 0. + real_max (float): The maximum possible real component (exclusive). Default is 1. + img_min (float): The minimum possible imaginary component (inclusive). If unspecified, will default to real_min. + img_max (float): The maximum possible imaginary component (exclusive). If unspecified, will default to real_max. + """ + if img_min is None: + 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 -# Returns a random complex in rectangular form from a given polar range. -# If no max angle given, [0,2pi) used. -def get_random_complex_polar(r,theta=2*math.pi): +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. + + Parameters: + 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 diff --git a/qrng/__pycache__/__init__.cpython-37.pyc b/qrng/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 0307f75..0000000 Binary files a/qrng/__pycache__/__init__.cpython-37.pyc and /dev/null differ diff --git a/setup.py b/setup.py index ab78d18..0aac1e8 100644 --- a/setup.py +++ b/setup.py @@ -5,13 +5,13 @@ setuptools.setup( name="qrng", - version="0.1.2", + version="0.1.3", author="Ozaner Hansha", author_email="ozanerhansha@gmail.com", description="A Quantum Random Number Generator using IBM's Qiskit", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/ozanerhansha/qRNG", + url="https://github.com/ozaner/qRNG", packages=setuptools.find_packages(), py_modules = ['qrng'], classifiers=[