lightrider 0.1.0__tar.gz
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.
- lightrider-0.1.0/MANIFEST.in +1 -0
- lightrider-0.1.0/PKG-INFO +52 -0
- lightrider-0.1.0/README.md +44 -0
- lightrider-0.1.0/lightrider/__init__.py +8 -0
- lightrider-0.1.0/lightrider/_pool.py +123 -0
- lightrider-0.1.0/lightrider/core.py +80 -0
- lightrider-0.1.0/lightrider/data/__init__.py +0 -0
- lightrider-0.1.0/lightrider/data/qubit_0_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_1_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_2_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_3_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_4_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_5_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_6_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_7_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_8_sequence.npy +0 -0
- lightrider-0.1.0/lightrider/data/qubit_9_sequence.npy +0 -0
- lightrider-0.1.0/lightrider.egg-info/PKG-INFO +52 -0
- lightrider-0.1.0/lightrider.egg-info/SOURCES.txt +23 -0
- lightrider-0.1.0/lightrider.egg-info/dependency_links.txt +1 -0
- lightrider-0.1.0/lightrider.egg-info/requires.txt +1 -0
- lightrider-0.1.0/lightrider.egg-info/top_level.txt +2 -0
- lightrider-0.1.0/pyproject.toml +17 -0
- lightrider-0.1.0/setup.cfg +4 -0
- lightrider-0.1.0/setup.py +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
recursive-include lightrider/data *.npy
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lightrider
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Quantum random numbers from IQM hardware-generated bit pools
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: numpy>=1.21
|
|
8
|
+
|
|
9
|
+
# lightrider
|
|
10
|
+
|
|
11
|
+
Quantum random numbers sourced from IQM hardware measurements.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install lightrider
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from lightrider import IQM_sirius
|
|
23
|
+
|
|
24
|
+
# 5 random integers between 1 and 100
|
|
25
|
+
IQM_sirius(5, 1, 100)
|
|
26
|
+
# → [42, 7, 88, 13, 56]
|
|
27
|
+
|
|
28
|
+
# 3 floats between 0.0 and 1.0 in steps of 0.1
|
|
29
|
+
IQM_sirius(3, 0.0, 1.0, step=0.1)
|
|
30
|
+
# → [0.3, 0.8, 0.1]
|
|
31
|
+
|
|
32
|
+
# 4 floats between 10.0 and 99.9 in steps of 0.1
|
|
33
|
+
IQM_sirius(4, 10.0, 99.9, step=0.1)
|
|
34
|
+
# → [23.4, 67.8, 45.1, 88.0]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
### `IQM_sirius(count, min_val, max_val, step=None)`
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Description |
|
|
42
|
+
|-----------|------|-------------|
|
|
43
|
+
| `count` | `int` | Number of values to return |
|
|
44
|
+
| `min_val` | `int` or `float` | Inclusive lower bound |
|
|
45
|
+
| `max_val` | `int` or `float` | Inclusive upper bound |
|
|
46
|
+
| `step` | `float` or `None` | Value granularity; `None` means integer steps |
|
|
47
|
+
|
|
48
|
+
Returns a `list` of quantum-random numbers in `[min_val, max_val]`.
|
|
49
|
+
|
|
50
|
+
## Data source
|
|
51
|
+
|
|
52
|
+
The pool contains ~2 million bits collected from IQM quantum hardware via Hadamard coin-flip circuits across 10 qubits. Numbers are produced using rejection sampling for unbiased output.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# lightrider
|
|
2
|
+
|
|
3
|
+
Quantum random numbers sourced from IQM hardware measurements.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install lightrider
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from lightrider import IQM_sirius
|
|
15
|
+
|
|
16
|
+
# 5 random integers between 1 and 100
|
|
17
|
+
IQM_sirius(5, 1, 100)
|
|
18
|
+
# → [42, 7, 88, 13, 56]
|
|
19
|
+
|
|
20
|
+
# 3 floats between 0.0 and 1.0 in steps of 0.1
|
|
21
|
+
IQM_sirius(3, 0.0, 1.0, step=0.1)
|
|
22
|
+
# → [0.3, 0.8, 0.1]
|
|
23
|
+
|
|
24
|
+
# 4 floats between 10.0 and 99.9 in steps of 0.1
|
|
25
|
+
IQM_sirius(4, 10.0, 99.9, step=0.1)
|
|
26
|
+
# → [23.4, 67.8, 45.1, 88.0]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## API
|
|
30
|
+
|
|
31
|
+
### `IQM_sirius(count, min_val, max_val, step=None)`
|
|
32
|
+
|
|
33
|
+
| Parameter | Type | Description |
|
|
34
|
+
|-----------|------|-------------|
|
|
35
|
+
| `count` | `int` | Number of values to return |
|
|
36
|
+
| `min_val` | `int` or `float` | Inclusive lower bound |
|
|
37
|
+
| `max_val` | `int` or `float` | Inclusive upper bound |
|
|
38
|
+
| `step` | `float` or `None` | Value granularity; `None` means integer steps |
|
|
39
|
+
|
|
40
|
+
Returns a `list` of quantum-random numbers in `[min_val, max_val]`.
|
|
41
|
+
|
|
42
|
+
## Data source
|
|
43
|
+
|
|
44
|
+
The pool contains ~2 million bits collected from IQM quantum hardware via Hadamard coin-flip circuits across 10 qubits. Numbers are produced using rejection sampling for unbiased output.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manages the quantum bit pool loaded from IQM hardware data.
|
|
3
|
+
Bits are consumed sequentially with wrap-around.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
import importlib.resources
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import threading
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_QUBIT_FILES = [f"qubit_{i}_sequence.npy" for i in range(10)]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_bits() -> np.ndarray:
|
|
20
|
+
"""Load and concatenate all qubit bit sequences into a single uint8 array."""
|
|
21
|
+
chunks = []
|
|
22
|
+
try:
|
|
23
|
+
# Python 3.9+ path
|
|
24
|
+
pkg_files = importlib.resources.files("lightrider.data")
|
|
25
|
+
for fname in _QUBIT_FILES:
|
|
26
|
+
data_bytes = (pkg_files / fname).read_bytes()
|
|
27
|
+
arr = np.frombuffer(data_bytes, dtype=np.uint8)
|
|
28
|
+
# npy files: reconstruct via np.load from bytes
|
|
29
|
+
import io
|
|
30
|
+
arr = np.load(io.BytesIO(data_bytes))
|
|
31
|
+
chunks.append(arr.astype(np.uint8).ravel())
|
|
32
|
+
except Exception:
|
|
33
|
+
# Fallback: load from package directory relative path
|
|
34
|
+
data_dir = Path(__file__).parent / "data"
|
|
35
|
+
for fname in _QUBIT_FILES:
|
|
36
|
+
arr = np.load(data_dir / fname)
|
|
37
|
+
chunks.append(arr.astype(np.uint8).ravel())
|
|
38
|
+
return np.concatenate(chunks)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class QuantumPool:
|
|
42
|
+
"""Thread-safe sequential consumer of quantum random bits."""
|
|
43
|
+
|
|
44
|
+
def __init__(self) -> None:
|
|
45
|
+
self._bits: np.ndarray = _load_bits()
|
|
46
|
+
self._pos: int = 0
|
|
47
|
+
self._total: int = len(self._bits)
|
|
48
|
+
self._lock = threading.Lock()
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def total_bits(self) -> int:
|
|
52
|
+
return self._total
|
|
53
|
+
|
|
54
|
+
def consume(self, n: int) -> np.ndarray:
|
|
55
|
+
"""Return the next *n* bits, wrapping around if the pool is exhausted."""
|
|
56
|
+
with self._lock:
|
|
57
|
+
if n > self._total:
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"Requested {n} bits but pool only has {self._total} bits total."
|
|
60
|
+
)
|
|
61
|
+
end = self._pos + n
|
|
62
|
+
if end <= self._total:
|
|
63
|
+
result = self._bits[self._pos : end].copy()
|
|
64
|
+
self._pos = end % self._total
|
|
65
|
+
else:
|
|
66
|
+
# Wrap around
|
|
67
|
+
tail = self._bits[self._pos :].copy()
|
|
68
|
+
head_n = n - len(tail)
|
|
69
|
+
head = self._bits[:head_n].copy()
|
|
70
|
+
result = np.concatenate([tail, head])
|
|
71
|
+
self._pos = head_n
|
|
72
|
+
return result
|
|
73
|
+
|
|
74
|
+
def bits_to_uint(self, bits: np.ndarray) -> int:
|
|
75
|
+
"""Interpret a 1-D bit array (MSB first) as an unsigned integer."""
|
|
76
|
+
result = 0
|
|
77
|
+
for b in bits:
|
|
78
|
+
result = (result << 1) | int(b)
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
def draw_integers(self, count: int, n_values: int) -> list[int]:
|
|
82
|
+
"""
|
|
83
|
+
Draw *count* unbiased integers in [0, n_values) using rejection sampling.
|
|
84
|
+
Uses ceil(log2(n_values)) bits per candidate.
|
|
85
|
+
"""
|
|
86
|
+
if n_values <= 0:
|
|
87
|
+
raise ValueError("n_values must be positive.")
|
|
88
|
+
if n_values == 1:
|
|
89
|
+
return [0] * count
|
|
90
|
+
|
|
91
|
+
bits_per = max(1, math.ceil(math.log2(n_values)))
|
|
92
|
+
# Upper power-of-two threshold for rejection
|
|
93
|
+
threshold = (1 << bits_per) # = 2**bits_per
|
|
94
|
+
|
|
95
|
+
results: list[int] = []
|
|
96
|
+
# Over-sample to reduce rejection loop iterations
|
|
97
|
+
batch = max(count * 2, 64)
|
|
98
|
+
|
|
99
|
+
while len(results) < count:
|
|
100
|
+
raw_bits = self.consume(batch * bits_per)
|
|
101
|
+
for i in range(batch):
|
|
102
|
+
segment = raw_bits[i * bits_per : (i + 1) * bits_per]
|
|
103
|
+
val = self.bits_to_uint(segment)
|
|
104
|
+
if val < n_values:
|
|
105
|
+
results.append(val)
|
|
106
|
+
if len(results) == count:
|
|
107
|
+
break
|
|
108
|
+
|
|
109
|
+
return results[:count]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# Module-level singleton
|
|
113
|
+
_pool: QuantumPool | None = None
|
|
114
|
+
_pool_lock = threading.Lock()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def get_pool() -> QuantumPool:
|
|
118
|
+
global _pool
|
|
119
|
+
if _pool is None:
|
|
120
|
+
with _pool_lock:
|
|
121
|
+
if _pool is None:
|
|
122
|
+
_pool = QuantumPool()
|
|
123
|
+
return _pool
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core API for the lightrider quantum random number generator.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import math
|
|
8
|
+
from typing import Union
|
|
9
|
+
|
|
10
|
+
from ._pool import get_pool
|
|
11
|
+
|
|
12
|
+
Number = Union[int, float]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def IQM_sirius(
|
|
16
|
+
count: int,
|
|
17
|
+
min_val: Number,
|
|
18
|
+
max_val: Number,
|
|
19
|
+
step: float | None = None,
|
|
20
|
+
) -> list[Number]:
|
|
21
|
+
"""
|
|
22
|
+
Return *count* quantum random numbers drawn from a hardware-generated bit pool.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
count : int
|
|
27
|
+
How many random values to return.
|
|
28
|
+
min_val : int or float
|
|
29
|
+
Inclusive lower bound.
|
|
30
|
+
max_val : int or float
|
|
31
|
+
Inclusive upper bound.
|
|
32
|
+
step : float or None
|
|
33
|
+
Granularity of values. ``None`` means integer steps (step=1).
|
|
34
|
+
Example: ``step=0.1`` produces values like 23.4, 67.8.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
list of int or float
|
|
39
|
+
Quantum-random values in [min_val, max_val].
|
|
40
|
+
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
ValueError
|
|
44
|
+
If arguments are invalid.
|
|
45
|
+
|
|
46
|
+
Examples
|
|
47
|
+
--------
|
|
48
|
+
>>> IQM_sirius(5, 1, 100)
|
|
49
|
+
[42, 7, 88, 13, 56]
|
|
50
|
+
|
|
51
|
+
>>> IQM_sirius(3, 0.0, 1.0, step=0.1)
|
|
52
|
+
[0.3, 0.8, 0.1]
|
|
53
|
+
"""
|
|
54
|
+
if count <= 0:
|
|
55
|
+
raise ValueError(f"count must be a positive integer, got {count!r}.")
|
|
56
|
+
if min_val > max_val:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"min_val ({min_val}) must be <= max_val ({max_val})."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
effective_step: float = step if step is not None else 1.0
|
|
62
|
+
if effective_step <= 0:
|
|
63
|
+
raise ValueError(f"step must be positive, got {step!r}.")
|
|
64
|
+
|
|
65
|
+
# Number of discrete values in the range (inclusive on both ends)
|
|
66
|
+
n_values = round((max_val - min_val) / effective_step) + 1
|
|
67
|
+
|
|
68
|
+
pool = get_pool()
|
|
69
|
+
indices = pool.draw_integers(count, n_values)
|
|
70
|
+
|
|
71
|
+
if step is None:
|
|
72
|
+
# Return plain ints when no step is specified and both bounds are int
|
|
73
|
+
if isinstance(min_val, int) and isinstance(max_val, int):
|
|
74
|
+
return [int(min_val) + idx for idx in indices]
|
|
75
|
+
else:
|
|
76
|
+
return [min_val + idx for idx in indices]
|
|
77
|
+
else:
|
|
78
|
+
# Round to avoid floating-point noise (e.g. 0.1+0.2 artifacts)
|
|
79
|
+
decimal_places = max(0, -math.floor(math.log10(step)))
|
|
80
|
+
return [round(min_val + idx * step, decimal_places) for idx in indices]
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lightrider
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Quantum random numbers from IQM hardware-generated bit pools
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: numpy>=1.21
|
|
8
|
+
|
|
9
|
+
# lightrider
|
|
10
|
+
|
|
11
|
+
Quantum random numbers sourced from IQM hardware measurements.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install lightrider
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from lightrider import IQM_sirius
|
|
23
|
+
|
|
24
|
+
# 5 random integers between 1 and 100
|
|
25
|
+
IQM_sirius(5, 1, 100)
|
|
26
|
+
# → [42, 7, 88, 13, 56]
|
|
27
|
+
|
|
28
|
+
# 3 floats between 0.0 and 1.0 in steps of 0.1
|
|
29
|
+
IQM_sirius(3, 0.0, 1.0, step=0.1)
|
|
30
|
+
# → [0.3, 0.8, 0.1]
|
|
31
|
+
|
|
32
|
+
# 4 floats between 10.0 and 99.9 in steps of 0.1
|
|
33
|
+
IQM_sirius(4, 10.0, 99.9, step=0.1)
|
|
34
|
+
# → [23.4, 67.8, 45.1, 88.0]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API
|
|
38
|
+
|
|
39
|
+
### `IQM_sirius(count, min_val, max_val, step=None)`
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Description |
|
|
42
|
+
|-----------|------|-------------|
|
|
43
|
+
| `count` | `int` | Number of values to return |
|
|
44
|
+
| `min_val` | `int` or `float` | Inclusive lower bound |
|
|
45
|
+
| `max_val` | `int` or `float` | Inclusive upper bound |
|
|
46
|
+
| `step` | `float` or `None` | Value granularity; `None` means integer steps |
|
|
47
|
+
|
|
48
|
+
Returns a `list` of quantum-random numbers in `[min_val, max_val]`.
|
|
49
|
+
|
|
50
|
+
## Data source
|
|
51
|
+
|
|
52
|
+
The pool contains ~2 million bits collected from IQM quantum hardware via Hadamard coin-flip circuits across 10 qubits. Numbers are produced using rejection sampling for unbiased output.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
lightrider/__init__.py
|
|
6
|
+
lightrider/_pool.py
|
|
7
|
+
lightrider/core.py
|
|
8
|
+
lightrider.egg-info/PKG-INFO
|
|
9
|
+
lightrider.egg-info/SOURCES.txt
|
|
10
|
+
lightrider.egg-info/dependency_links.txt
|
|
11
|
+
lightrider.egg-info/requires.txt
|
|
12
|
+
lightrider.egg-info/top_level.txt
|
|
13
|
+
lightrider/data/__init__.py
|
|
14
|
+
lightrider/data/qubit_0_sequence.npy
|
|
15
|
+
lightrider/data/qubit_1_sequence.npy
|
|
16
|
+
lightrider/data/qubit_2_sequence.npy
|
|
17
|
+
lightrider/data/qubit_3_sequence.npy
|
|
18
|
+
lightrider/data/qubit_4_sequence.npy
|
|
19
|
+
lightrider/data/qubit_5_sequence.npy
|
|
20
|
+
lightrider/data/qubit_6_sequence.npy
|
|
21
|
+
lightrider/data/qubit_7_sequence.npy
|
|
22
|
+
lightrider/data/qubit_8_sequence.npy
|
|
23
|
+
lightrider/data/qubit_9_sequence.npy
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
numpy>=1.21
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "lightrider"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Quantum random numbers from IQM hardware-generated bit pools"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = ["numpy>=1.21"]
|
|
12
|
+
|
|
13
|
+
[tool.setuptools.packages.find]
|
|
14
|
+
where = ["."]
|
|
15
|
+
|
|
16
|
+
[tool.setuptools.package-data]
|
|
17
|
+
lightrider = ["data/*.npy"]
|