pedrorrivero / qrand Goto Github PK
View Code? Open in Web Editor NEWA multiprotocol and multiplatform quantum random number generation framework
Home Page: https://pypi.org/project/qrand/
License: Apache License 2.0
A multiprotocol and multiplatform quantum random number generation framework
Home Page: https://pypi.org/project/qrand/
License: Apache License 2.0
QRAND will make requests to quantum backends in order to randomly generate numbers. These requests (i.e. jobs) will have a unique id associated to each of them given by the backend; however, it is very difficult to tell apart which of these requests were made from QRAND when looking at the information stored in the provider.
By given QRAND's jobs a particular, easily distinguishable, friendly name we will be able to tell them apart in the provider (e.g. IBM-Q website). Ideally this name will contain:
BareQuantumProtocol
being run.E.g.: QRAND by Pedro Rivero - EntanglementProtocol | 2021-06-06T20:14:00Z
If we want to include the name of the protocol, we will need to add a name
str parameter to create_job
in QuantumFactory
. The easiest way to implement this is by defining a property in QuantumProtocol
called job_name
. This will be inherited by all protocols, and produce an updated name every time it is called (i.e. updating the protocol name through self.__class__.__name__
or type(self).__name__
, as well as the timestamp). Since we are using a decorator structure, the name will have to be produce by BareQuantumProtocol
and tunneled all the way up through the ValidationDecorator
class.
The alternative would be to hardcode the name inside every platform's QuantumJob
, but this will not be able to display the name of the protocol.
Qiskit: Qiskit/qiskit-ibmq-provider#118, and job_name
Q#: azure.execute
documentation.
Unable to install qrand
using pip
in Windows, Colab and IBM Quantum Experience portal.
Steps to reproduce the behavior:
pip install qrand
Install qrand library
ERROR: Could not find a version that satisfies the requirement qrand
ERROR: No matching distribution found for qrand
Add any other context about the problem here.
The following classes need to be developed implementing the corresponding interfaces:
QsharpPlatform
← QuantumPlatform
QsharpBackend
← QuantumBackend
QsharpJob
← QuantumJob
QsharpCircuit
← QuantumCircuit
The first of these classes follows the Facade/Abstract Factory pattern, while the other two are Class Adapters. For an example check the Qiskit variants.
TestBitCache.test_get_random_complex_polar
test case failing
Steps to reproduce the behavior:
All the test cases passing and the commit being successful.
The entire error message -
isort....................................................................Passed
mypy.....................................................................Passed
black....................................................................Passed
flake8...................................................................Passed
pytest...................................................................Failed
- hook id: pytest
- exit code: 1
============================= test session starts =============================
platform win32 -- Python 3.8.3, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: F:\QuantumProgrammes\unitaryHack\qrand
plugins: cov-2.11.1
collected 32 items
tests\test_qiskit_bit_generator.py ..................... [ 65%]
tests\test_qrand.py .. [ 71%]
tests\test_qrng.py .F....... [100%]
================================== FAILURES ===================================
_________________ TestBitCache.test_get_random_complex_polar __________________
self = <tests.test_qrng.TestBitCache object at 0x000001BCCF8B1E20>
def test_get_random_complex_polar(self):
bitgen = QiskitBitGenerator()
qrng = Qrng(bitgen)
cache = "100" * 1000
bitgen.load_cache(cache)
assert (
qrng.get_random_complex_polar()
== 0.4713139887723277 + 0.5910090485061033j
)
assert (
qrng.get_random_complex_polar(4)
== -1.9263524684802522 - 0.9276824556973191j
)
> assert (
qrng.get_random_complex_polar(4, 3.14)
== 0.9431657500378959 + 1.1815890375548252j
)
E assert (0.9431657500378958+1.1815890375548252j) == (0.9431657500378959 + 1.1815890375548252j)
E + where (0.9431657500378958+1.1815890375548252j) = <bound method Qrng.get_random_complex_polar of <qrand.qrng.Qrng object at 0x000001BCCF8B1AF0>>(4, 3.14)
E + where <bound method Qrng.get_random_complex_polar of <qrand.qrng.Qrng object at 0x000001BCCF8B1AF0>> = <qrand.qrng.Qrng object at 0x000001BCCF8B1AF0>.get_random_complex_polar
tests\test_qrng.py:57: AssertionError
============================== warnings summary ===============================
tests/test_qiskit_bit_generator.py: 15 warnings
tests/test_qrand.py: 1 warning
tests/test_qrng.py: 9 warnings
F:\QuantumProgrammes\unitaryHack\qrand\qrand\_qiskit_bit_generator.py:119: FutureWarning: QiskitBitGenerator will be deprecated in version 1.0.0. Use QuantumBitGenerator and QiskitPlatform instead.
warn(WARNING_MESSAGE, FutureWarning)
tests/test_qrng.py::TestBitCache::test_state
F:\QuantumProgrammes\unitaryHack\qrand\qrand\qrng.py:308: FutureWarning: state() will be deprecated in version 1.0.0.
warn(WARNING_MESSAGE, FutureWarning)
-- Docs: https://docs.pytest.org/en/stable/warnings.html
----------- coverage: platform win32, python 3.8.3-final-0 -----------
Coverage HTML written to dir coverage-html
=========================== short test summary info ===========================
FAILED tests/test_qrng.py::TestBitCache::test_get_random_complex_polar - asse...
================== 1 failed, 31 passed, 26 warnings in 2.91s ==================
Currently, QRAND can only be installed for Python >= 3.9.
QRAND should work with Python versions >=:
The reason why Python 3.9 was used initially is that previous version seemed to be unstable when interfaced with Qiskit.
Missing documentation.
Build a Read The Docs website.
Project already created: https://qrand.readthedocs.io/
The QiskitCircuit
class contains 21 occurrences of super(QuantumCircuit, self)
.
With a restrictive linter, this generates 21 error-level messages Bad first argument 'QuantumCircuit' given to super()
.
PEP 3135 introduced a new syntax for super()
, where the child class calling super()
should not be mentioned any longer. See here and here.
Unless there is a reason for using this ancient syntax?
Steps to reproduce the behavior:
pylint
.No error messages thrown by the linter (that is no use of the ancient syntax).
Not appllicable.
Not applicable.
There is no functionality for retrieving bitstrings in a more compact base:
Add random hex, base32, and base64 functionality to QRNG
The user can translate the output bitstring manually
Users need to go through several steps in order to instantiate a Qrng object:
QuantumPlatform
.QuantumProtocol
.QuantumBitGenerator
from the previous two objects.Qrng
from the bit generator.By transforming Qrng
from a QuantumBitGenerator
object wrapper to a class wrapper (i.e. through inheritance), the user can disregard QuantumBitGenerator
altogether (i.e. step 3) and jump straight to using Qrng
. Furtheremore, Qrng
will then also be able to interface with NumPy.
Build facades to simplify the four-step process described above.
Qrng
we can set a default protocol (e.g. Hadamard) to further simplify the process, or we can set the default on the QuantumBirGenerator
class directly.QantumBitGenerator
and Qrng
is to segregate/isolate responsibilities. All the critical functionality will be implemented in QuantumBitGenerator
(e.g. NumPy interface, caching), while the Qrng
child will simply add extra functionality.Cache is hold in memory and gets erased when the python job holding it finishes.
It should be possible to choose between holding the cache in memory or storage.
It is currently possible for the user to save and load the cache manually. However, if two programs are running at the same time and one uses bits in the cache, this will not be reflected on the other one. Leading to possible security issues and statistical biases.
Concurrent access to the local file holding the cache has to be handled. This suggests using a singleton pattern along with semaphores in order to make it thread-safe. Performance has to be taken into account, since accessing a local file is always slower than simply reading from memory. Also, this feature should be available for all major OS distributions (i.e. Windows, MacOS, and Linux/Unix).
New content.
We need to design QRAND's graphic identity. All files should be in SVG format.
Checkout Qiskit for inspiration.
Hello there. I have cited your library in a paper currently under peer review. I am trying to validate use of Qrand with simulator backend on qiskit client. The sampling does not appear to return random values, although it works with a live circuit backend.
from qrand import QuantumBitGenerator
from qrand.platforms import QiskitPlatform
from qrand.protocols import HadamardProtocol
from qiskit import IBMQ
provider = IBMQ.load_account()
backend = provider.get_backend('ibmq_qasm_simulator')
#I added a backend assignment here and it allowed it to compile the cell
#it worked with a live circuit
#so I am assuming that backend assignment is supported functionality
platform = QiskitPlatform(provider, backend)
protocol = HadamardProtocol()
bitgen = QuantumBitGenerator(platform, protocol)
#and then to validate tried sampling 5 points from numpy's standard_normal
import numpy as np
from numpy.random import Generator
gen = Generator(bitgen)
gen.standard_normal(5)
#the array returned 5 equal values
array([-7.42778876e-09, -7.42778876e-09, -7.42778876e-09, -7.42778876e-09,
-7.42778876e-09])
It also generated a bunch of qiskit printouts as follows (repeated for each sampled number):
Exception ignored on calling ctypes callback function: <function QuantumBitGenerator._next_64.<locals>.next_64 at 0x7f79a8f8eee0>
Traceback (most recent call last):
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/quantum_bit_generator.py", line 311, in next_64
return uint64(self.random_uint(64))
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/quantum_bit_generator.py", line 259, in random_uint
return int(self.random_bitstring(num_bits), base=2)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/quantum_bit_generator.py", line 207, in random_bitstring
self._refill_cache()
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/quantum_bit_generator.py", line 277, in _refill_cache
bitstring: str = self.platform.fetch_random_bits(self.protocol)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/platforms/qiskit/platform.py", line 117, in fetch_random_bits
result: ProtocolResult = protocol.run(self)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/protocols/hadamard.py", line 88, in run
circuit: QuantumCircuit = factory.create_circuit(num_qubits)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/platforms/qiskit/platform.py", line 105, in create_circuit
return QiskitCircuit(num_qubits)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qrand/platforms/qiskit/circuit.py", line 33, in __init__
super(QuantumCircuit, self).__init__(num_qubits, num_qubits)
File "/Users/nicholasteague/opt/anaconda3/envs/qiskit/lib/python3.9/site-packages/qiskit/circuit/quantumcircuit.py", line 226, in __init__
raise CircuitError(
qiskit.circuit.exceptions.CircuitError: "Circuit args must be Registers or integers. (['str', 'str'] '('32', '32')' was provided)"
I expected the sampling to generate 5 unique numbers when running the simulator backend
This might be related. I was running some code from a qiskit book and it appears that some kind of convention may have changed for their library in a recent update with respect to representing qubit counts .configuration().n_qubits
for various backends as strings instead of integers. Wondering if a simple type conversion somewhere could resolve .
QiskitPlatform
splits job repetitions
into shots
and experiments
. Therefore, in order to build a QiskitJob
, we need to factor any user input number of repetitions into a number of shots and a number of experiments such that shots * experiments = repetitions
. Also, we need to take into account the fact that these two amounts are bounded by max_shots
and max_experiments
respectively.
Because of this last restriction, said task can be impossible to perform: for instance, if repetitions
is a prime number larger than both bounds. To address this issue, we would like to find the factorization which is closest to the actual number of repetitions requested without exceeding it.
Representing the solution algorithm as a function, this problem can be mathematically modeled as follows:
Find f: ℕ³ → D ⊆ ℕ² such that f(n, A, B) = (a, b) minimizes n-a•b, where:
Notice how we require A < n otherwise the trivial solution (a=n, b=1) would be available. The same applies to B < n. Additionally, we require n < A•B to scape the trivial solution (a=A, b=B).
The last (stability) condition is needed to guarantee performance (i.e. running the algorithm iteratively does not worsen the approximation). Notice that this condition is automatically satisfied if the function always returns an optimal solution. However, since (a, b) ∈ D —as opposed to being general natural numbers— this condition is, in principle, less restrictive than asking for perfect factoring of any given composite number.
By convention, we assume zero to not be contained in the natural numbers (ℕ).
Due to symmetry considerations, we can assume the first bound to be lower than the second without loss of generality. Otherwise, we would only need to swap them.
A naive approach to solving this problem would be as follows:
def g(
n: int, bound_A: int, bound_B: int
) -> Tuple[int, int]:
if not bound_A * bound_B > n:
return bound_A, bound_B
swapped: bool = bound_A > bound_B
bound_A, bound_B = sorted([bound_A, bound_B])
a = min(bound_A, n // bound_B + 1)
b = min(bound_B, n // a)
return (b, a) if swapped else (a, b)
However, this violates (3): as displayed for n=11, A=4, B=5 which returns (a=3, b=3). Updating n=3•3 it follows (a'=2, b'=4), so we have 11 > 3•3 > 2•4. Nonetheless, this process seems to converge, so a workaround would be to repeat the process until the output stabilizes.
def f(
n: int, bound_A: int, bound_B: int
) -> Tuple[int, int]:
last_a, last_b = g(n, bound_A, bound_B)
a, b = g(last_a * last_b, bound_A, bound_B)
while a * b != last_a * last_b:
last_a, last_b = a, b
a, b = g(n, bound_A, bound_B)
return a, b
In order to implement this approach we would need:
The algorithm currently used finds the optimal solution through exhaustive search:
def compute_bounded_factorization(
n: int, bound_A: int, bound_B: int
) -> Tuple[int, int]:
if bound_A * bound_B < n:
return bound_A, bound_B
swapped: bool = bound_A > bound_B
bound_A, bound_B = sorted([bound_A, bound_B])
final_delta: int = n
b: int = bound_B
a: int = n // b
delta: int = n - a * b
while a <= bound_A and a <= b and final_delta != 0:
if delta < final_delta:
final_a, final_b, final_delta = a, b, delta
a += 1
b = n // a
delta = n - a * b
return (final_b, final_a) if swapped else (final_a, final_b)
Due to the nature of the qiskit backends experiments are more expensive than shots, so we would ideally want to keep the number of experiments as low as possible if several equally good, or even optimal, solutions exist.
Although we do not provide a formal proof, we believe this problem to be beyond NP. This is so because it reduces to the factoring problem for A = B = n-1, when n is the product of only two primes. Additionally, verifying an answer does not seem to be efficient either, since we do not know a way of finding the optimal gap n-a•b without going through the same process required for solving the problem to begin with.
*Therefore, we are open to considering suboptimal solutions that are capable of improving performance (i.e. speed) significantly.
qrand\platforms\qiskit\backend.py:45: error: Returning Any from function declared to return "Dict[Any, Any]"
qrand\platforms\qiskit\backend.py:49: error: Returning Any from function declared to return "int"
qrand\platforms\qiskit\backend.py:62: error: Returning Any from function declared to return "int"
qrand\platforms\qiskit\backend.py:71: error: Returning Any from function declared to return "int"
qrand\platforms\qiskit\backend.py:81: error: Returning Any from function declared to return "bool"
qrand\platforms\qiskit\circuit.py:37: error: Returning Any from function declared to return "int"
qrand\_qiskit_bit_generator.py:397: error: Returning Any from function declared to return "Dict[Any, Any]"
qrand\platforms\qsharp\job.py:76: error: unused 'type: ignore' comment
qrand\platforms\qsharp\job.py:99: error: Returning Any from function declared to return "List[str]"
qrand\platforms\qsharp\job.py:103: error: Returning Any from function declared to return "List[str]"
Found 10 errors in 4 files (checked 46 source files)
Steps to reproduce the behavior:
mypy
on the moduleNo type-check error thrown by mypy
.
Not applicable.
Not applicable.
The package qrand
depends on qiskit-ibmq-provider (==0.12.3)
https://www.wheelodex.org/projects/qrand/ which is not maintained any more and it was replaced by qiskit-ibm-runtime
.
See the migration guide here: https://docs.quantum.ibm.com/api/migration-guides/qiskit-runtime
As of May 2021, QRAND depends on NumPy
and randomgen
, but it only needs certain parts of these packages. Particularly, it only needs a number of data types (float64, uint32, uint64
) from the former, and the UserBitGenerator
class from the latter.
Removing this dependencies would make QRAND faster, lighter, and less fragile. We would need:
NumPy
. These should not change very often and so they will be easy to maintain.UserBitGenerator
that can be implemented by QuantumBitGenerator
.Apparently, randomgen
is slowly merging into NumPy
, so maybe in some time from now we will be able to naturally reduce the dependencies to only the latter with no additional effort. Nonetheless, NumPy
is a rather large library and we would only make use of a very limited portion of it.
The randomgen
package depends on NumPy
itself, so it would be necessary to assess wether this dependency could be entirely removed.
Even if impractical, removing the explicit NumPy
dependency would help mitigating conflicts. Luckily, this should be easy to do. A possible solution would be building data-type stubs through Python's own ctypes
.
Regardless of the above, since QRAND is meant to be able to interface with NumPy
we would still need to keep it as an extra dependency.
qrand
depends on qiskit-terra (<0.18.0,>=0.17.2)
https://www.wheelodex.org/projects/qrand/ which is reaching EoL:
Important
The package qiskit-terra
is not going to be updated after August 15th, 2024. Since Qiskit 0.44 (released on July 27th, 2023), the qiskit
meta-package only contains qiskit-terra
. In Qiskit 1.0 and beyond, the meta-package architecture is removed.
If you are installing or depending on qiskit-terra
, consider changing that to qiskit
: Either qiskit>=0.x,<1
(if you did not transition to Qiskit 1.0 yet) or qiskit>=0.x,<2
(to also include Qiskit 1.*).
Read more.
The following classes need to be developed implementing the corresponding interfaces:
CirqPlatform
← QuantumPlatform
CirqBackend
← QuantumBackend
CirqJob
← QuantumJob
CirqCircuit
← QuantumCircuit
The first of these classes follows the Facade/Abstract Factory pattern, while the other two are Class Adapters. For an example check the Qiskit variants.
New content
Build an interactive and up-to-date Jupyter Notebook tutorial for QRAND v1.0.0
The tutorial should at least include:
Generator
interface → Different probability distributionsBitCache
persistence explanationThis has to be targeted for v1.0.0 → QiskitBitGenerator
deprecated in favor of QuantumBitGenerator
The internal structure of BitCache
is very similar to some common data structures. However, it does not implement any of their interfaces, making it incompatible with many of the already existing (and very useful) libraries and functions.
BitCache
should be refactored so that it adheres to all relevant and applicable data structures from the collections.abc
module.
The alternative consists on relying on duck-typing alone.
This might require changing the signature of some of the methods already defined, nonetheless, as of June 2021, this would not mean a breaking change; since BitCache
and its specific implementations are only used internally.
Currently, calls to the quantum backends are not made until the BitCache
does not hold enough bits to satisfy the next request.
Calls should be made automatically once the BitCache
reaches a certain minimum amount of bits to have stored. Since the task of fetching new random bits from online quantum backends is I/O bound, these calls can be performed on a new thread (i.e. concurrent, non-blocking). This functionality can be implemented by means of the observer or state patters.
When using a local quantum simulator the process will no longer be I/O bound, but CPU bound instead. Multiprocessing (i.e. parallelism) should be a better alternative in this case, so it can be useful to choose from one or the other depending on the type of backend being used at runtime.
Develop an initial set of classes implementing the ValidationStrategy
interface.
These classes will need to expose a validate method which takes in a bitstring (i.e. a string of only zeros and ones) and returns either True
or False
depending on whether it passes a particular entropy/randomness validation test or not.
Please, develop each test class on a separate file/module inside the validation subpackage, and use descriptive names. Also, remember to include a precise reference to the test documentation in the docstring (e.g. document, website, pages...).
References:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.