diff --git a/.github/workflows/python-release.yml b/.github/workflows/python-release.yml index 220885109..be09eca8b 100644 --- a/.github/workflows/python-release.yml +++ b/.github/workflows/python-release.yml @@ -198,10 +198,16 @@ jobs: - name: Install pecos-rslib run: pip install ./pecos-rslib-wheel/*.whl - - name: Install build dependencies + - name: Install pecos RNG build dependencies run: | - python -m pip install --upgrade pip - pip install build + pip install build nanobind + + - name: Build PECOS RNG + run: | + cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. + make && cd .. && pip install . + env: + NANOBIND_DIR: python -m nanobind --include_dir - name: Build quantum-pecos SDist run: | @@ -249,10 +255,16 @@ jobs: - name: Install pecos-rslib run: pip install ./pecos-rslib-wheel/*.whl - - name: Install build dependencies + - name: Install pecos rng build dependencies + run: | + pip install build nanobind + + - name: Build PECOS RNG run: | - python -m pip install --upgrade pip - pip install build + cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. + make && cd .. && pip install . + env: + NANOBIND_DIR: python -m nanobind --include_dir - name: Build quantum-pecos wheel run: | diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index d056d495a..b374ea298 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -69,6 +69,18 @@ jobs: uv lock --project . uv sync --project . + - name: Install pecos rng build dependencies + run: | + uv pip install build nanobind scikit-build-core cmake ninja + + - name: Build PECOS RNG + run: | + export NANOBIND_CMAKE_DIR=$(uv run -- python -m nanobind --cmake_dir) + export NANOBIND_DIR=$(uv run -- python -m nanobind --include_dir) + cd clibs/pecos-rng && mkdir build && cd build/ && cmake .. -DCMAKE_PREFIX_PATH=$NANOBIND_CMAKE_DIR + cmake --build . && cd .. + uv pip install . + - name: Install pecos-rslib with maturin run: | cd python/pecos-rslib diff --git a/.gitignore b/.gitignore index 46407a2aa..147a22cfd 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ @@ -177,3 +176,7 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. .idea/ + +# Prevent subdirectory virtual environments +clibs/*/.venv/ +clibs/*/uv.lock diff --git a/Makefile b/Makefile index e140b6c27..d94150ed7 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ # Try to autodetect if python3 or python is the python executable used. PYTHONPATH := $(shell which python 2>/dev/null || which python3 2>/dev/null) - SHELL=bash # Requirements @@ -20,27 +19,37 @@ installreqs: ## Install Python project requirements to root .venv @echo "Installing requirements..." uv sync +.PHONY: buildrng +buildrng: + @echo "Building and installing RNG library..." + uv pip install nanobind + cd clibs/pecos-rng && CC=gcc CXX=g++ uv pip install --python $(shell uv run which python) -e . + # Building development environments # --------------------------------- .PHONY: build build: installreqs ## Compile and install for development @unset CONDA_PREFIX && cd python/pecos-rslib/ && uv run maturin develop --uv + $(MAKE) buildrng @unset CONDA_PREFIX && cd python/quantum-pecos && uv pip install -e .[all] .PHONY: build-basic build-basic: installreqs ## Compile and install for development but do not include install extras @unset CONDA_PREFIX && cd python/pecos-rslib/ && uv run maturin develop --uv + $(MAKE) buildrng @unset CONDA_PREFIX && cd python/quantum-pecos && uv pip install -e . .PHONY: build-release build-release: installreqs ## Build a faster version of binaries @unset CONDA_PREFIX && cd python/pecos-rslib/ && uv run maturin develop --uv --release + $(MAKE) buildrng @unset CONDA_PREFIX && cd python/quantum-pecos && uv pip install -e .[all] .PHONY: build-native build-native: installreqs ## Build a faster version of binaries with native CPU optimization @unset CONDA_PREFIX && cd python/pecos-rslib/ && RUSTFLAGS='-C target-cpu=native' \ && uv run maturin develop --uv --release + $(MAKE) buildrng @unset CONDA_PREFIX && cd python/quantum-pecos && uv pip install -e .[all] # Documentation @@ -146,6 +155,12 @@ clean-unix: @find . -type d -name "junit" -exec rm -rf {} + @find python -name "*.so" -delete @find python -name "*.pyd" -delete + @# Clean clibs build artifacts + @find clibs -type d -name "build" -exec rm -rf {} + + @find clibs -type d -name "dist" -exec rm -rf {} + + @find clibs -type d -name "*.egg-info" -exec rm -rf {} + + @find clibs -type d -name ".venv" -exec rm -rf {} + + @find clibs -name "uv.lock" -delete @# Clean all target directories in crates (in case they were built independently) @find crates -type d -name "target" -exec rm -rf {} + @find python -type d -name "target" -exec rm -rf {} + @@ -167,6 +182,12 @@ clean-windows-ps: @powershell -Command "Get-ChildItem -Path . -Recurse -Directory -Filter '.hypothesis' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path . -Recurse -Directory -Filter 'junit' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path python -Recurse -File -Include '*.so','*.pyd' | Remove-Item -Force -ErrorAction SilentlyContinue" + @# Clean clibs build artifacts + @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter 'build' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter 'dist' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter '*.egg-info' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clibs -Recurse -Directory -Filter '.venv' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" + @powershell -Command "Get-ChildItem -Path clibs -Recurse -File -Filter 'uv.lock' | Remove-Item -Force -ErrorAction SilentlyContinue" @# Clean all target directories in crates @powershell -Command "Get-ChildItem -Path crates -Recurse -Directory -Filter 'target' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @powershell -Command "Get-ChildItem -Path python -Recurse -Directory -Filter 'target' | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue" @@ -187,6 +208,12 @@ clean-windows-cmd: -@for /f "delims=" %%d in ('dir /s /b /ad .hypothesis 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%d in ('dir /s /b /ad junit 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%f in ('dir /s /b python\*.so python\*.pyd 2^>nul') do @del "%%f" 2>nul + -@REM Clean clibs build artifacts + -@for /f "delims=" %%d in ('dir /s /b /ad clibs\build 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clibs\dist 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clibs\*.egg-info 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%d in ('dir /s /b /ad clibs\.venv 2^>nul') do @rd /s /q "%%d" 2>nul + -@for /f "delims=" %%f in ('dir /s /b clibs\uv.lock 2^>nul') do @del "%%f" 2>nul -@REM Clean all target directories in crates -@for /f "delims=" %%d in ('dir /s /b /ad crates\target 2^>nul') do @rd /s /q "%%d" 2>nul -@for /f "delims=" %%d in ('dir /s /b /ad python\target 2^>nul') do @rd /s /q "%%d" 2>nul @@ -201,10 +228,10 @@ pip-install-uv: ## Install uv using pip and create a venv. (Recommended to inst @echo "Creating venv and installing dependencies..." uv sync -.PONY: dev +.PHONY: dev dev: clean build test ## Run the typical sequence of commands to check everything is running correctly -.PONY: devl ## Run the commands to make sure everything runs + lint +.PHONY: devl ## Run the commands to make sure everything runs + lint devl: dev lint # Help diff --git a/clibs/pecos-rng/CMakeLists.txt b/clibs/pecos-rng/CMakeLists.txt new file mode 100644 index 000000000..b2f3f0760 --- /dev/null +++ b/clibs/pecos-rng/CMakeLists.txt @@ -0,0 +1,34 @@ +# CMakeLists.txt +cmake_minimum_required(VERSION 3.15...3.27) +project(pecos_rng LANGUAGES C CXX) +set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + + +if (CMAKE_VERSION VERSION_LESS 3.18) + set(DEV_MODULE Development) +else() + set(DEV_MODULE Development.Module) +endif() + +# Find Python (for Python 3.13) +find_package(Python REQUIRED COMPONENTS Interpreter ${DEV_MODULE}) + +# Detect the installed nanobind package and import it into CMake +# This executes a Python command to get the nanobind CMake directory +execute_process( + COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE nanobind_ROOT +) + +set(CMAKE_PREFIX_PATH "${nanobind_ROOT}" ${CMAKE_PREFIX_PATH}) + +# This sets up nanobind +find_package(nanobind CONFIG REQUIRED) + +# Add your C source file as a library +add_library(rng_pcg STATIC src/rng_pcg.c) # Compile test.c as a static library +nanobind_add_module(pecos_rng src/wrapper.cpp) +target_link_libraries(pecos_rng PRIVATE rng_pcg) +# Install the module so it can be found by pip install +install(TARGETS pecos_rng DESTINATION "pecos_pcg") diff --git a/clibs/pecos-rng/README.md b/clibs/pecos-rng/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/clibs/pecos-rng/pecos_pcg/__init__.py b/clibs/pecos-rng/pecos_pcg/__init__.py new file mode 100644 index 000000000..5580f3ca4 --- /dev/null +++ b/clibs/pecos-rng/pecos_pcg/__init__.py @@ -0,0 +1,10 @@ +"""Python Package responsible for generating random numbers using pcg_rng.""" + +from .pecos_rng import ( + pcg32_boundedrand, + pcg32_frandom, + pcg32_random, + pcg32_srandom, +) + +__all__ = ["pcg32_boundedrand", "pcg32_frandom", "pcg32_random", "pcg32_srandom"] diff --git a/clibs/pecos-rng/pyproject.toml b/clibs/pecos-rng/pyproject.toml new file mode 100644 index 000000000..72a22f680 --- /dev/null +++ b/clibs/pecos-rng/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["scikit-build-core[pyproject]", "nanobind"] +build-backend = "scikit_build_core.build" + +[project] +name = "pecos-pcg" +version = "0.1.0" +description = "A Python package with a nanobind-based C++ extension" +license = { text = "MIT" } +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Python :: 3", + "Programming Language :: C++", + "License :: OSI Approved :: MIT License" +] + +[tool.scikit-build] +wheel.packages = ["pecos_pcg"] +# Optional: set cmake minimum version or other CMake arguments +cmake.minimum-version = "3.18" +cmake.verbose = true diff --git a/clibs/pecos-rng/src/rng_pcg.c b/clibs/pecos-rng/src/rng_pcg.c new file mode 100644 index 000000000..8ddb23d2e --- /dev/null +++ b/clibs/pecos-rng/src/rng_pcg.c @@ -0,0 +1,96 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + */ + +#include +#include "rng_pcg.h" + +// RNG state structure +typedef struct pcg_state_setseq_64 { + uint64_t state; + uint64_t inc; +} pcg32_random_t; + +// global RNG state +static pcg32_random_t pcg32_global = { + 0x853c49e6748fea9bULL, + 0xda3e39cb94b95bdbULL +}; + +// default multi[plier] +#define PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL + +// helper functions +static inline uint32_t pcg_rotr_32(uint32_t value, unsigned int urot) { + int rot = (int)urot; + return (value >> rot) | (value << ((-rot) & 31)); +} + +static inline void pcg_setseq_64_step_r(pcg32_random_t* rng) { + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + rng->inc; +} + +static inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state) { + return pcg_rotr_32(((state >> 18u) ^ state) >> 27u, state >> 59u); +} + +static inline uint32_t pcg32_random_r(pcg32_random_t* rng) { + const uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +static inline uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t ubound) { + int32_t bound = (int32_t)ubound; + uint32_t threshold = -bound % bound; + for (;;) { + const uint32_t r = pcg32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +static inline void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate, uint64_t initseq) { + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_64_step_r(rng); + rng->state += initstate; + pcg_setseq_64_step_r(rng); +} + +// public interface to RNG + +uint32_t pcg32_random() { + return pcg32_random_r(&pcg32_global); +} + +uint32_t pcg32_boundedrand(uint32_t bound) { + return pcg32_boundedrand_r(&pcg32_global, bound); +} + +double pcg32_frandom() { + return ldexp(pcg32_random(), -32); +} + +void pcg32_srandom(uint64_t seq) { + pcg32_srandom_r(&pcg32_global, 42u, seq); +} diff --git a/clibs/pecos-rng/src/rng_pcg.h b/clibs/pecos-rng/src/rng_pcg.h new file mode 100644 index 000000000..5cfb759f5 --- /dev/null +++ b/clibs/pecos-rng/src/rng_pcg.h @@ -0,0 +1,42 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014 Melissa O'Neill + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For additional information about the PCG random number generation scheme, + * including its license and other licensing options, visit + * + * http://www.pcg-random.org + */ + +#pragma once + +#include + +#if __cplusplus +extern "C" { +#endif + +uint32_t pcg32_random(); + +uint32_t pcg32_boundedrand(uint32_t bound); + +double pcg32_frandom(); + +void pcg32_srandom(uint64_t seq); + +#if __cplusplus +} +#endif diff --git a/clibs/pecos-rng/src/wrapper.cpp b/clibs/pecos-rng/src/wrapper.cpp new file mode 100644 index 000000000..94a258808 --- /dev/null +++ b/clibs/pecos-rng/src/wrapper.cpp @@ -0,0 +1,12 @@ +#include +#include "rng_pcg.h" + +namespace nb = nanobind; +using namespace nb::literals; + +NB_MODULE(pecos_rng, m) { + m.def("pcg32_random", &pcg32_random,"generate random numbers"); + m.def("pcg32_frandom", &pcg32_frandom, "Generate random floating point number"); + m.def("pcg32_boundedrand", &pcg32_boundedrand, "Generate bounded random number"); + m.def("pcg32_srandom", &pcg32_srandom, "seeded random"); +} diff --git a/pyproject.toml b/pyproject.toml index 98f668951..81a3479bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pecos-workspace" -version = "0.6.0.dev8" +version = "0.7.0.dev8" [tool.uv.workspace] members = [ @@ -10,6 +10,7 @@ members = [ [dependency-groups] dev = [ + "nanobind", "maturin>=1.2,<2.0", # For building (should match build requirements) "setuptools>=62.6", # Build system "pre-commit", # Git hooks diff --git a/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py new file mode 100644 index 000000000..de760a639 --- /dev/null +++ b/python/quantum-pecos/src/pecos/engines/cvm/rng_model.py @@ -0,0 +1,99 @@ +"""This Module is responsible for keeping track of the state for generating a sequence of random numbers. + +It handles RNG platform function calls that that are handled by the pcg_rng library. + +""" + +from pecos_pcg import pecos_rng + +from pecos.engines.cvm.binarray import BinArray + + +class RNGModel: + """This class is responsible for the functionality of generating a sequence of random numbers.""" + + def __init__( + self, + shot_id: int, + seed: int = 0, + current_bound: int | None = 0, + ) -> None: + """Constructs an RNGModel object.""" + self.shot_id = shot_id + self.current_bound = current_bound + self.count = 0 + self.seed = self.set_seed(seed) + + def __str__(self) -> str: + """Returns the str representation of the model.""" + return f"RNG Model with bound {self.current_bound} with count {self.count}" + + def set_seed(self, seed: int) -> None: + """Setting the seed for generating random numbers.""" + self.seed = seed + pecos_rng.pcg32_srandom(seed) + + def set_bound(self, bound: int) -> None: + """Setting the current bound for generating random numbers.""" + self.current_bound = bound + + def rng_random(self) -> int: + """Generating a random number and keeping track of how many we have generated.""" + if self.current_bound == 0: + rng_num = pecos_rng.pcg32_random() + else: + rng_num = pecos_rng.pcg32_boundedrand(self.current_bound) + self.count += 1 + return rng_num + + def set_index(self, index: int) -> None: + """Setting the index for the random number sequence. + + The number after from the stream will be the idx of interest. + """ + if self.count > index: + error_msg = "rngindex called after specified already generated" + raise BufferError(error_msg) + while self.count < index: + self.rng_random() + + def extract_val(self, param: str, output: dict) -> int: + """Responsible for extracting the value of interest depending on the type of the parameter being passed in.""" + if param.isdigit(): + val = int(param) + elif "[" in param: + idx_creg = param.split("[") + creg = output[idx_creg[0]] + idx = int(idx_creg[-1][:-1]) + val = int(creg[idx]) + elif param == "JOB_shotnum": + val = self.shot_id + else: + reg = output[param] + val = int(reg) + return val + + def eval_func(self, params: dict, output: dict) -> None: + """Calling the appropriate functions dependent on RNG Function call passed in.""" + func_name = params.get("func") + if func_name == "RNGseed": + seed_var = params.get("args")[0] + seed = self.extract_val(seed_var, output) + self.set_seed(seed) + elif func_name == "RNGbound": + bound_var = params.get("args")[0] + bound = self.extract_val(bound_var, output) + self.set_bound(bound) + elif func_name == "RNGindex": + index_var = params.get("args")[0] + index = self.extract_val(index_var, output) + self.set_index(index) + elif func_name == "RNGnum": + creg_name = params.get("assign_vars")[0] + creg = output[creg_name] + rng = self.rng_random() + binary_val = BinArray(creg.size, rng) + creg.set(binary_val) + else: + error_msg = f"RNG function not supported {func_name}" + raise ValueError(error_msg) diff --git a/python/quantum-pecos/src/pecos/engines/hybrid_engine_old.py b/python/quantum-pecos/src/pecos/engines/hybrid_engine_old.py index 26593a0f7..476e449d4 100644 --- a/python/quantum-pecos/src/pecos/engines/hybrid_engine_old.py +++ b/python/quantum-pecos/src/pecos/engines/hybrid_engine_old.py @@ -26,6 +26,7 @@ from pecos.engines.cvm.binarray import BinArray from pecos.engines.cvm.classical import eval_condition, eval_cop, set_output +from pecos.engines.cvm.rng_model import RNGModel from pecos.engines.cvm.wasm import eval_cfunc, get_ccop from pecos.error_models.fake_error_model import FakeErrorModel from pecos.errors import NotSupportedGateError @@ -89,8 +90,11 @@ def __init__( self.seed = None if self.seed: + self.rng_model = RNGModel(self.seed) np.random.seed(self.seed) random.seed(self.seed) + else: + self.rng_model = RNGModel(0) self.ccop = None @@ -100,6 +104,7 @@ def run( self, state: SimulatorProtocol, circuit: QuantumCircuit, + shot_id: int, error_gen: ParentErrorModel | None = None, error_params: dict[str, float | dict[str, float]] | None = None, error_circuits: dict[int, dict[str, QuantumCircuit | set[int]]] | None = None, @@ -112,6 +117,7 @@ def run( Args: state: Quantum simulator state. circuit: Quantum circuit to execute. + shot_id: Integer representing current shot. error_gen: Optional error model. error_params: Parameters for error generation. error_circuits: Pre-generated error circuits. @@ -137,6 +143,8 @@ def run( # -------------------- self.generate_errors = True error_circuits = error_gen.start(circuit, error_params) + self.rng_model.count = 0 + self.rng_model.shot_id = shot_id # run through the circuits... # --------------------------- @@ -150,7 +158,7 @@ def run( error_circuits = error_gen.generate_tick_errors( tick_circuit, time, - output, + # output, **params, ) errors = error_circuits.get(time, {}) @@ -222,7 +230,6 @@ def run_circuit( """ self.state = state - if removed_locations is None: removed_locations = set() @@ -248,7 +255,11 @@ def run_circuit( pass elif params.get("cop_type") == "CFunc": - eval_cfunc(self, params, output) + cop_name = params.get("func") + if "RNG" in cop_name: + self.rng_model.eval_func(params, output) + else: + eval_cfunc(self, params, output) elif params.get("expr"): eval_cop(params.get("expr"), output, width=self.regwidth) diff --git a/python/tests/pecos/unit/test_rng.py b/python/tests/pecos/unit/test_rng.py new file mode 100644 index 000000000..d342adc13 --- /dev/null +++ b/python/tests/pecos/unit/test_rng.py @@ -0,0 +1,53 @@ +"""Testing module for the RNG Model.""" + +import random + +from pecos.engines.cvm.rng_model import RNGModel + + +def test_set_seed() -> None: + """Verifies that a seed is set properly for our RNG model.""" + rng = RNGModel(shot_id=0) + seed = 42 + rng.set_seed(seed) + assert rng.seed == seed + + +def test_random_number() -> None: + """Verifies that the random number generated is an int type.""" + rng = RNGModel(shot_id=0) + random = rng.rng_random() + assert isinstance(random, int) + + +def test_bounded_random() -> None: + """Verifies that a single generated random number is within bounds.""" + rng = RNGModel(shot_id=0) + rng.set_seed(42) + bound = 16 + rng.set_bound(bound) + assert rng.current_bound == bound + + random_number = rng.rng_random() + assert 0 <= random_number < bound + + +def test_set_idx() -> None: + """Verifies that the idx is set properly for our model.""" + rng = RNGModel(shot_id=0) + rng.set_seed(42) + idx = 4 + rng.set_index(idx) + assert rng.count == idx + + +def test_multiple_bounded_rand() -> None: + """For several randomly generated number, with a random bound, verifies that its appropriate.""" + rng = RNGModel(shot_id=0) + rng.set_seed(42) + + for _ in range(100): + random_bound = random.randint(1, 2**32 - 1) # noqa: S311 + rng.set_bound(random_bound) + random_number = rng.rng_random() + assert 0 <= random_number < random_bound diff --git a/ruff.toml b/ruff.toml index cc535ce30..36fffc649 100644 --- a/ruff.toml +++ b/ruff.toml @@ -91,6 +91,7 @@ ignore = [ "ANN", "D", ] +"clibs/pecos-rng/pecos_pcg/__init__.py" = ["TID252"] [lint.pycodestyle] diff --git a/uv.lock b/uv.lock index f07c13aca..08c042d1b 100644 --- a/uv.lock +++ b/uv.lock @@ -886,6 +886,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695, upload-time = "2023-02-04T12:11:25.002Z" }, ] +[[package]] +name = "nanobind" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/7d/f77f2bc2e2a210502a164556f8a742cd0f72f39061b97cb9d73bbd3ff0ab/nanobind-2.7.0.tar.gz", hash = "sha256:f9f1b160580c50dcf37b6495a0fd5ec61dc0d95dae5f8004f87dd9ad7eb46b34", size = 976093, upload-time = "2025-04-18T01:17:37.187Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/14/989883082b395146120d34ca7e484a2b24cb73b0e428576a3a4249bd4082/nanobind-2.7.0-py3-none-any.whl", hash = "sha256:73b12d0e751d140d6c1bf4b215e18818a8debfdb374f08dc3776ad208d808e74", size = 241690, upload-time = "2025-04-18T01:17:34.821Z" }, +] + [[package]] name = "networkx" version = "3.4.2" @@ -1038,7 +1047,7 @@ source = { editable = "python/pecos-rslib" } [[package]] name = "pecos-workspace" -version = "0.6.0.dev8" +version = "0.7.0.dev8" source = { virtual = "." } [package.dev-dependencies] @@ -1049,6 +1058,7 @@ dev = [ { name = "mkdocs" }, { name = "mkdocs-material" }, { name = "mkdocstrings", extra = ["python"] }, + { name = "nanobind" }, { name = "pre-commit" }, { name = "ruff" }, { name = "setuptools" }, @@ -1069,6 +1079,7 @@ dev = [ { name = "mkdocs" }, { name = "mkdocs-material" }, { name = "mkdocstrings", extras = ["python"] }, + { name = "nanobind" }, { name = "pre-commit" }, { name = "ruff" }, { name = "setuptools", specifier = ">=62.6" },