cubie 0.0.2__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.
- cubie-0.0.2/PKG-INFO +71 -0
- cubie-0.0.2/pyproject.toml +99 -0
- cubie-0.0.2/readme.md +41 -0
- cubie-0.0.2/setup.cfg +4 -0
- cubie-0.0.2/src/LICENSE +21 -0
- cubie-0.0.2/src/cubie/CUDAFactory.py +227 -0
- cubie-0.0.2/src/cubie/__init__.py +21 -0
- cubie-0.0.2/src/cubie/_utils.py +352 -0
- cubie-0.0.2/src/cubie/batchsolving/BatchGridBuilder.py +664 -0
- cubie-0.0.2/src/cubie/batchsolving/BatchSolverConfig.py +15 -0
- cubie-0.0.2/src/cubie/batchsolving/BatchSolverKernel.py +594 -0
- cubie-0.0.2/src/cubie/batchsolving/SystemInterface.py +251 -0
- cubie-0.0.2/src/cubie/batchsolving/__init__.py +19 -0
- cubie-0.0.2/src/cubie/batchsolving/_utils.py +54 -0
- cubie-0.0.2/src/cubie/batchsolving/arrays/BaseArrayManager.py +456 -0
- cubie-0.0.2/src/cubie/batchsolving/arrays/BatchInputArrays.py +180 -0
- cubie-0.0.2/src/cubie/batchsolving/arrays/BatchOutputArrays.py +193 -0
- cubie-0.0.2/src/cubie/batchsolving/arrays/__init__.py +0 -0
- cubie-0.0.2/src/cubie/batchsolving/solver.py +551 -0
- cubie-0.0.2/src/cubie/batchsolving/solveresult.py +398 -0
- cubie-0.0.2/src/cubie/cudasim_utils.py +75 -0
- cubie-0.0.2/src/cubie/integrators/IntegratorRunSettings.py +143 -0
- cubie-0.0.2/src/cubie/integrators/SingleIntegratorRun.py +377 -0
- cubie-0.0.2/src/cubie/integrators/__init__.py +3 -0
- cubie-0.0.2/src/cubie/integrators/algorithms/IntegratorLoopSettings.py +84 -0
- cubie-0.0.2/src/cubie/integrators/algorithms/LoopStepConfig.py +39 -0
- cubie-0.0.2/src/cubie/integrators/algorithms/__init__.py +25 -0
- cubie-0.0.2/src/cubie/integrators/algorithms/euler.py +153 -0
- cubie-0.0.2/src/cubie/integrators/algorithms/genericIntegratorAlgorithm.py +186 -0
- cubie-0.0.2/src/cubie/memory/__init__.py +10 -0
- cubie-0.0.2/src/cubie/memory/array_requests.py +55 -0
- cubie-0.0.2/src/cubie/memory/cupy_emm.py +205 -0
- cubie-0.0.2/src/cubie/memory/mem_manager.py +772 -0
- cubie-0.0.2/src/cubie/memory/stream_groups.py +85 -0
- cubie-0.0.2/src/cubie/outputhandling/__init__.py +9 -0
- cubie-0.0.2/src/cubie/outputhandling/_utils.py +20 -0
- cubie-0.0.2/src/cubie/outputhandling/output_config.py +437 -0
- cubie-0.0.2/src/cubie/outputhandling/output_functions.py +232 -0
- cubie-0.0.2/src/cubie/outputhandling/output_sizes.py +279 -0
- cubie-0.0.2/src/cubie/outputhandling/save_state.py +44 -0
- cubie-0.0.2/src/cubie/outputhandling/save_summaries.py +131 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/__init__.py +12 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/max.py +66 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/mean.py +65 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/metrics.py +346 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/peaks.py +94 -0
- cubie-0.0.2/src/cubie/outputhandling/summarymetrics/rms.py +70 -0
- cubie-0.0.2/src/cubie/outputhandling/update_summaries.py +110 -0
- cubie-0.0.2/src/cubie/systemmodels/SystemValues.py +397 -0
- cubie-0.0.2/src/cubie/systemmodels/__init__.py +3 -0
- cubie-0.0.2/src/cubie/systemmodels/systems/GenericODE.py +282 -0
- cubie-0.0.2/src/cubie/systemmodels/systems/ODEData.py +93 -0
- cubie-0.0.2/src/cubie/systemmodels/systems/__init__.py +5 -0
- cubie-0.0.2/src/cubie/systemmodels/systems/decays.py +82 -0
- cubie-0.0.2/src/cubie/systemmodels/systems/threeCM.py +170 -0
- cubie-0.0.2/src/cubie.egg-info/PKG-INFO +71 -0
- cubie-0.0.2/src/cubie.egg-info/SOURCES.txt +60 -0
- cubie-0.0.2/src/cubie.egg-info/dependency_links.txt +1 -0
- cubie-0.0.2/src/cubie.egg-info/requires.txt +21 -0
- cubie-0.0.2/src/cubie.egg-info/top_level.txt +1 -0
- cubie-0.0.2/tests/test_CUDAFactory.py +148 -0
- cubie-0.0.2/tests/test_utils.py +0 -0
cubie-0.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cubie
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: CUDA Batch Integration Engine - for doing a lot at once.
|
|
5
|
+
Author: Chris Cameron
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ccam80/cubie.git
|
|
8
|
+
Project-URL: Issues, https://github.com/https://github.com/ccam80/cubie/issues
|
|
9
|
+
Requires-Python: >=3.8
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: src/LICENSE
|
|
12
|
+
Requires-Dist: numpy==1.26.4
|
|
13
|
+
Requires-Dist: numba
|
|
14
|
+
Requires-Dist: numba-cuda[cu12]
|
|
15
|
+
Requires-Dist: attrs
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-durations; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-json-report; extra == "dev"
|
|
21
|
+
Requires-Dist: flake8; extra == "dev"
|
|
22
|
+
Requires-Dist: cupy-cuda12x; extra == "dev"
|
|
23
|
+
Requires-Dist: pandas; extra == "dev"
|
|
24
|
+
Provides-Extra: cupy
|
|
25
|
+
Requires-Dist: cupy-cuda12x; extra == "cupy"
|
|
26
|
+
Provides-Extra: pandas
|
|
27
|
+
Requires-Dist: pandas; extra == "pandas"
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# CuBIE
|
|
32
|
+
## CUDA batch integration engine for python
|
|
33
|
+
|
|
34
|
+
[](https://github.com/ccam80/smc/actions/workflows/documentation.yml) [](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml) [](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml) [](https://github.com/ccam80/cubie/actions/workflows/test_pypi.yml)
|
|
35
|
+
|
|
36
|
+
A batch integration system for systems of ODEs and SDEs, for when elegant solutions fail and you would like to simulate
|
|
37
|
+
1,000,000 systems, fast. This package was designed to simulate a large electrophysiological model as part of a
|
|
38
|
+
likelihood-free inference method (eventually, package [cubism]), but the machinery is domain-agnostic.
|
|
39
|
+
|
|
40
|
+
The most basic use case is to define a system of ODEs or SDEs, and then call cubie.solve(system, inits, params, duration) with a description of the "batch" in the form of initial conditions and system parameters. There are a few seconds of overhead in the first call to Solve - cubie really shines when dealing with large problems or repeated calls with a similarly sized batch.
|
|
41
|
+
|
|
42
|
+
Defining a system of ODEs is the most cumbersome part of using this library. Like in MATLAB or SciPy, we need to create a dxdt function that takes the current state and parameters, and returns the rate of change of the state. Unlike MATLAB and SciPy, this function needs to be CUDA-compatible, which means it cannot use some of the features of Python and numpy. Creating a system is done by subclassing cubie.SystemModel.GenericODE, and implementing the dxdt method. See ThreeCM.py for an example of a small system. Fabbri_linder.py for an example of a large system.
|
|
43
|
+
|
|
44
|
+
## Installation:
|
|
45
|
+
pip install cubie
|
|
46
|
+
|
|
47
|
+
## System Requirements:
|
|
48
|
+
- Python 3.8 or later
|
|
49
|
+
- CUDA Toolkit 12.9 or later
|
|
50
|
+
- NVIDIA GPU with compute capability 6.0 or higher (i.e. GTX10-series or newer)
|
|
51
|
+
|
|
52
|
+
I am using this library as a way to experiment with and learn about some better software practice than I have used in
|
|
53
|
+
past, including testing, CI/CD, and other helpful tactics I stumble upon. As such, while it's in development, there will
|
|
54
|
+
be some clunky bits.
|
|
55
|
+
|
|
56
|
+
The interface is not yet stable, and the documentation is currently non-working AI-generated slop, but the library now works roughly as you might expect, and can get up and running quickly by reading docstrings. Documentation and SymPy integration (as a means to get Jacobians to use implicit algorithms) are on the hit list for v0.1.0.
|
|
57
|
+
## Project Goals:
|
|
58
|
+
|
|
59
|
+
- Make an engine and interface for batch integration that is close enough to MATLAB or SciPy that a Python beginner can
|
|
60
|
+
get integrating with the documentation alone in an hour or two. This also means staying Windows-compatible.
|
|
61
|
+
- Perform integrations of 10 or more parallel systems faster than MATLAB or SciPy can
|
|
62
|
+
- Enable extraction of summary variables only (rather than saving time-domain outputs) to facilitate use in algorithms
|
|
63
|
+
like likelihood-free inference.
|
|
64
|
+
- Be extensible enough that users can add their own systems and algorithms without needing to go near the core machinery.
|
|
65
|
+
- Don't be greedy - allow the user to control VRAM usage so that cubie can run alongside other applications.
|
|
66
|
+
|
|
67
|
+
## Non-Goals:
|
|
68
|
+
- Have the full set of integration algorithms that SciPy and MATLAB have.
|
|
69
|
+
The full set of known and trusted algorithms is long, and it includes many wrappers for old Fortran libraries that the Numba compiler can't touch. If a problem requires a specific algorithm, we can add it as a feature request, but we won't set out to implement them all.
|
|
70
|
+
- Have a GUI.
|
|
71
|
+
MATLABs toolboxes are excellent, but from previous projects (specifically CuNODE, the precursor to cubie), GUI development becomes all-consuming and distracts from the purpose of the project.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools.packages.find]
|
|
6
|
+
where = ["src"]
|
|
7
|
+
exclude = ["./tests/*"]
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "cubie"
|
|
11
|
+
version = "0.0.2"
|
|
12
|
+
description = "CUDA Batch Integration Engine - for doing a lot at once."
|
|
13
|
+
authors = [
|
|
14
|
+
{ name="Chris Cameron" }
|
|
15
|
+
]
|
|
16
|
+
readme = "readme.md"
|
|
17
|
+
requires-python = ">=3.8"
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["src/LICEN[CS]E*"]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"numpy==1.26.4",
|
|
22
|
+
"numba",
|
|
23
|
+
"numba-cuda[cu12]",
|
|
24
|
+
"attrs"
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Homepage = "https://github.com/ccam80/cubie.git"
|
|
29
|
+
Issues = "https://github.com/https://github.com/ccam80/cubie/issues"
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
dev = [
|
|
33
|
+
"pytest",
|
|
34
|
+
"pytest-cov",
|
|
35
|
+
"pytest-durations",
|
|
36
|
+
"pytest-json-report",
|
|
37
|
+
"flake8",
|
|
38
|
+
"cupy-cuda12x",
|
|
39
|
+
"pandas"
|
|
40
|
+
|
|
41
|
+
]
|
|
42
|
+
cupy = [
|
|
43
|
+
"cupy-cuda12x",
|
|
44
|
+
]
|
|
45
|
+
pandas = [
|
|
46
|
+
"pandas"
|
|
47
|
+
]
|
|
48
|
+
all = []
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
minversion = "6.0"
|
|
52
|
+
testpaths = [
|
|
53
|
+
"tests"
|
|
54
|
+
]
|
|
55
|
+
markers = [
|
|
56
|
+
"nocudasim: Will fail if run on the GPU simulator, only test on real GPU",
|
|
57
|
+
"cupy: Requires optional dependency CuPy, will fail if not installed",
|
|
58
|
+
"slow: Marks tests that are slow to run, can be skipped with -m 'not slow'",
|
|
59
|
+
]
|
|
60
|
+
addopts = [
|
|
61
|
+
"--import-mode=importlib",
|
|
62
|
+
"-ra",
|
|
63
|
+
"--cov=cubie",
|
|
64
|
+
"--cov-report=xml:coverage.xml",
|
|
65
|
+
"--cov-report=term-missing",
|
|
66
|
+
"--json-report",
|
|
67
|
+
"--json-report-file=tests/pytest_report.json",
|
|
68
|
+
]
|
|
69
|
+
filterwarnings = [
|
|
70
|
+
"ignore::numba.core.errors.NumbaPerformanceWarning",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[tool.coverage.run]
|
|
74
|
+
#source = ["cubie"]
|
|
75
|
+
omit = [
|
|
76
|
+
"*/tests/*",
|
|
77
|
+
"*/__pycache__/*"
|
|
78
|
+
]
|
|
79
|
+
relative_files = true
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
[tool.coverage.report]
|
|
83
|
+
exclude_lines = [
|
|
84
|
+
"pragma: no cover",
|
|
85
|
+
"def __repr__",
|
|
86
|
+
"if self.debug:",
|
|
87
|
+
"if settings.DEBUG",
|
|
88
|
+
"raise AssertionError",
|
|
89
|
+
"raise NotImplementedError",
|
|
90
|
+
"if 0:",
|
|
91
|
+
"if __name__ == .__main__.:",
|
|
92
|
+
"class .*\\bProtocol\\):",
|
|
93
|
+
"@(abc\\.)?abstractmethod",
|
|
94
|
+
]
|
|
95
|
+
ignore_errors = true
|
|
96
|
+
show_missing = true
|
|
97
|
+
|
|
98
|
+
[tool.coverage.html]
|
|
99
|
+
directory = "htmlReport"
|
cubie-0.0.2/readme.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# CuBIE
|
|
2
|
+
## CUDA batch integration engine for python
|
|
3
|
+
|
|
4
|
+
[](https://github.com/ccam80/smc/actions/workflows/documentation.yml) [](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml) [](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml) [](https://github.com/ccam80/cubie/actions/workflows/test_pypi.yml)
|
|
5
|
+
|
|
6
|
+
A batch integration system for systems of ODEs and SDEs, for when elegant solutions fail and you would like to simulate
|
|
7
|
+
1,000,000 systems, fast. This package was designed to simulate a large electrophysiological model as part of a
|
|
8
|
+
likelihood-free inference method (eventually, package [cubism]), but the machinery is domain-agnostic.
|
|
9
|
+
|
|
10
|
+
The most basic use case is to define a system of ODEs or SDEs, and then call cubie.solve(system, inits, params, duration) with a description of the "batch" in the form of initial conditions and system parameters. There are a few seconds of overhead in the first call to Solve - cubie really shines when dealing with large problems or repeated calls with a similarly sized batch.
|
|
11
|
+
|
|
12
|
+
Defining a system of ODEs is the most cumbersome part of using this library. Like in MATLAB or SciPy, we need to create a dxdt function that takes the current state and parameters, and returns the rate of change of the state. Unlike MATLAB and SciPy, this function needs to be CUDA-compatible, which means it cannot use some of the features of Python and numpy. Creating a system is done by subclassing cubie.SystemModel.GenericODE, and implementing the dxdt method. See ThreeCM.py for an example of a small system. Fabbri_linder.py for an example of a large system.
|
|
13
|
+
|
|
14
|
+
## Installation:
|
|
15
|
+
pip install cubie
|
|
16
|
+
|
|
17
|
+
## System Requirements:
|
|
18
|
+
- Python 3.8 or later
|
|
19
|
+
- CUDA Toolkit 12.9 or later
|
|
20
|
+
- NVIDIA GPU with compute capability 6.0 or higher (i.e. GTX10-series or newer)
|
|
21
|
+
|
|
22
|
+
I am using this library as a way to experiment with and learn about some better software practice than I have used in
|
|
23
|
+
past, including testing, CI/CD, and other helpful tactics I stumble upon. As such, while it's in development, there will
|
|
24
|
+
be some clunky bits.
|
|
25
|
+
|
|
26
|
+
The interface is not yet stable, and the documentation is currently non-working AI-generated slop, but the library now works roughly as you might expect, and can get up and running quickly by reading docstrings. Documentation and SymPy integration (as a means to get Jacobians to use implicit algorithms) are on the hit list for v0.1.0.
|
|
27
|
+
## Project Goals:
|
|
28
|
+
|
|
29
|
+
- Make an engine and interface for batch integration that is close enough to MATLAB or SciPy that a Python beginner can
|
|
30
|
+
get integrating with the documentation alone in an hour or two. This also means staying Windows-compatible.
|
|
31
|
+
- Perform integrations of 10 or more parallel systems faster than MATLAB or SciPy can
|
|
32
|
+
- Enable extraction of summary variables only (rather than saving time-domain outputs) to facilitate use in algorithms
|
|
33
|
+
like likelihood-free inference.
|
|
34
|
+
- Be extensible enough that users can add their own systems and algorithms without needing to go near the core machinery.
|
|
35
|
+
- Don't be greedy - allow the user to control VRAM usage so that cubie can run alongside other applications.
|
|
36
|
+
|
|
37
|
+
## Non-Goals:
|
|
38
|
+
- Have the full set of integration algorithms that SciPy and MATLAB have.
|
|
39
|
+
The full set of known and trusted algorithms is long, and it includes many wrappers for old Fortran libraries that the Numba compiler can't touch. If a problem requires a specific algorithm, we can add it as a feature request, but we won't set out to implement them all.
|
|
40
|
+
- Have a GUI.
|
|
41
|
+
MATLABs toolboxes are excellent, but from previous projects (specifically CuNODE, the precursor to cubie), GUI development becomes all-consuming and distracts from the purpose of the project.
|
cubie-0.0.2/setup.cfg
ADDED
cubie-0.0.2/src/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chris Cameron
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Base classes for constructing cached CUDA device functions with Numba."""
|
|
2
|
+
from typing import Set
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
import attrs
|
|
5
|
+
|
|
6
|
+
from cubie._utils import in_attr, is_attrs_class
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CUDAFactory(ABC):
|
|
10
|
+
"""Factory for creating and caching CUDA device functions.
|
|
11
|
+
|
|
12
|
+
Subclasses implement :meth:`build` to construct Numba CUDA device functions
|
|
13
|
+
or other cached outputs. Compile settings are stored as attrs classes and
|
|
14
|
+
any change invalidates the cache to ensure functions are rebuilt when
|
|
15
|
+
needed.
|
|
16
|
+
|
|
17
|
+
Attributes
|
|
18
|
+
----------
|
|
19
|
+
_compile_settings : attrs class or None
|
|
20
|
+
Current compile settings.
|
|
21
|
+
_cache_valid : bool
|
|
22
|
+
Indicates whether cached outputs are valid.
|
|
23
|
+
_device_function : callable or None
|
|
24
|
+
Cached CUDA device function.
|
|
25
|
+
_cache : attrs class or None
|
|
26
|
+
Container for additional cached outputs.
|
|
27
|
+
|
|
28
|
+
Notes
|
|
29
|
+
-----
|
|
30
|
+
There is potential for a cache mismatch when doing the following:
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
device_function = self.device_function # calls build if settings updated
|
|
34
|
+
self.update_compile_settings(new_setting=value) # updates settings but
|
|
35
|
+
does not rebuild
|
|
36
|
+
|
|
37
|
+
device_function(argument_derived_from_new_setting) # this will use the
|
|
38
|
+
old device function, not the new one
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The lesson is: Always use CUDAFactory.device_function at the point of
|
|
42
|
+
use, otherwise you'll defeat the cache invalidation logic.
|
|
43
|
+
|
|
44
|
+
If your build function returns multiple cached items, create a cache
|
|
45
|
+
class decorated with @attrs.define. For example:
|
|
46
|
+
```python
|
|
47
|
+
@attrs.define
|
|
48
|
+
class MyCache:
|
|
49
|
+
device_function: callable
|
|
50
|
+
other_output: int
|
|
51
|
+
```
|
|
52
|
+
Then, in your build method, return an instance of this class:
|
|
53
|
+
```python
|
|
54
|
+
|
|
55
|
+
def build(self):
|
|
56
|
+
return MyCache(device_function=my_device_function,
|
|
57
|
+
other_output=42)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The current cache validity can be checked using the `cache_valid` property,
|
|
61
|
+
which will return True if the cache
|
|
62
|
+
is valid and False otherwise.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self._compile_settings = None
|
|
67
|
+
self._cache_valid = True
|
|
68
|
+
self._device_function = None
|
|
69
|
+
self._cache = None
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def build(self):
|
|
73
|
+
"""Build and return the CUDA device function.
|
|
74
|
+
|
|
75
|
+
This method must be overridden by subclasses.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
callable or attrs class
|
|
80
|
+
Compiled CUDA function or container of cached outputs.
|
|
81
|
+
"""
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
def setup_compile_settings(self, compile_settings):
|
|
85
|
+
"""Attach a container of compile-critical settings to the object.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
compile_settings : attrs class
|
|
90
|
+
Settings object used to configure the CUDA function.
|
|
91
|
+
|
|
92
|
+
Notes
|
|
93
|
+
-----
|
|
94
|
+
Any existing settings are replaced.
|
|
95
|
+
"""
|
|
96
|
+
if not attrs.has(compile_settings):
|
|
97
|
+
raise TypeError(
|
|
98
|
+
"Compile settings must be an attrs class instance.")
|
|
99
|
+
self._compile_settings = compile_settings
|
|
100
|
+
self._invalidate_cache()
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def cache_valid(self):
|
|
104
|
+
"""bool: ``True`` if cached outputs are up to date."""
|
|
105
|
+
|
|
106
|
+
return self._cache_valid
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def device_function(self):
|
|
110
|
+
"""Return the compiled CUDA device function.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
callable
|
|
115
|
+
Compiled CUDA device function.
|
|
116
|
+
"""
|
|
117
|
+
if not self._cache_valid:
|
|
118
|
+
self._build()
|
|
119
|
+
return self._device_function
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def compile_settings(self):
|
|
123
|
+
"""Return the current compile settings object."""
|
|
124
|
+
return self._compile_settings
|
|
125
|
+
|
|
126
|
+
def update_compile_settings(
|
|
127
|
+
self, updates_dict=None, silent=False, **kwargs
|
|
128
|
+
) -> Set[str]:
|
|
129
|
+
"""Update compile settings with new values.
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
updates_dict : dict, optional
|
|
134
|
+
Mapping of setting names to new values.
|
|
135
|
+
silent : bool, default=False
|
|
136
|
+
Suppress errors for unrecognised parameters.
|
|
137
|
+
**kwargs
|
|
138
|
+
Additional settings to update.
|
|
139
|
+
|
|
140
|
+
Returns
|
|
141
|
+
-------
|
|
142
|
+
set[str]
|
|
143
|
+
Names of settings that were successfully updated.
|
|
144
|
+
|
|
145
|
+
Raises
|
|
146
|
+
------
|
|
147
|
+
ValueError
|
|
148
|
+
If compile settings have not been set up.
|
|
149
|
+
KeyError
|
|
150
|
+
If an unrecognised parameter is supplied and ``silent`` is ``False``.
|
|
151
|
+
"""
|
|
152
|
+
if updates_dict is None:
|
|
153
|
+
updates_dict = {}
|
|
154
|
+
if kwargs:
|
|
155
|
+
updates_dict.update(kwargs)
|
|
156
|
+
if updates_dict == {}:
|
|
157
|
+
return set()
|
|
158
|
+
|
|
159
|
+
if self._compile_settings is None:
|
|
160
|
+
raise ValueError(
|
|
161
|
+
"Compile settings must be set up using self.setup_compile_settings before updating.")
|
|
162
|
+
|
|
163
|
+
recognized_params = []
|
|
164
|
+
|
|
165
|
+
for key, value in updates_dict.items():
|
|
166
|
+
if in_attr(key, self._compile_settings):
|
|
167
|
+
setattr(self._compile_settings, key, value)
|
|
168
|
+
recognized_params.append(key)
|
|
169
|
+
|
|
170
|
+
unrecognised_params = set(updates_dict.keys()) - set(recognized_params)
|
|
171
|
+
|
|
172
|
+
if unrecognised_params and not silent:
|
|
173
|
+
invalid = ", ".join(sorted(unrecognised_params))
|
|
174
|
+
raise KeyError(
|
|
175
|
+
f"'{invalid}' is not a valid compile setting for this "
|
|
176
|
+
"object, and so was not updated.", )
|
|
177
|
+
if recognized_params:
|
|
178
|
+
self._invalidate_cache()
|
|
179
|
+
|
|
180
|
+
return set(recognized_params)
|
|
181
|
+
|
|
182
|
+
def _invalidate_cache(self):
|
|
183
|
+
"""Mark cached outputs as invalid."""
|
|
184
|
+
self._cache_valid = False
|
|
185
|
+
|
|
186
|
+
def _build(self):
|
|
187
|
+
"""Rebuild cached outputs if they are invalid."""
|
|
188
|
+
build_result = self.build()
|
|
189
|
+
|
|
190
|
+
# Multi-output case
|
|
191
|
+
if is_attrs_class(build_result):
|
|
192
|
+
self._cache = build_result
|
|
193
|
+
# If 'device_function' is in the dict, make it an attribute
|
|
194
|
+
if in_attr('device_function', build_result):
|
|
195
|
+
self._device_function = build_result.device_function
|
|
196
|
+
else:
|
|
197
|
+
self._device_function = build_result
|
|
198
|
+
|
|
199
|
+
self._cache_valid = True
|
|
200
|
+
|
|
201
|
+
def get_cached_output(self, output_name):
|
|
202
|
+
"""Return a named cached output.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
output_name : str
|
|
207
|
+
Name of the cached item to retrieve.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
Any
|
|
212
|
+
Cached value associated with ``output_name``.
|
|
213
|
+
|
|
214
|
+
Raises
|
|
215
|
+
------
|
|
216
|
+
KeyError
|
|
217
|
+
If ``output_name`` is not present in the cache.
|
|
218
|
+
"""
|
|
219
|
+
if not self.cache_valid:
|
|
220
|
+
self._build()
|
|
221
|
+
if output_name == 'device_function':
|
|
222
|
+
return self._device_function
|
|
223
|
+
else:
|
|
224
|
+
if not in_attr(output_name, self._cache):
|
|
225
|
+
raise KeyError(
|
|
226
|
+
f"Output '{output_name}' not found in cached outputs.")
|
|
227
|
+
return getattr(self._cache, output_name)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cubie: CUDA Batch Integration Engine
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import version
|
|
6
|
+
|
|
7
|
+
from cubie.batchsolving import *
|
|
8
|
+
from cubie.integrators import *
|
|
9
|
+
from cubie.outputhandling import *
|
|
10
|
+
from cubie.memory import *
|
|
11
|
+
import cubie.systemmodels as systems
|
|
12
|
+
from cubie._utils import *
|
|
13
|
+
|
|
14
|
+
__all__ = ["summary_metrics", "default_memmgr", "systems", "ArrayTypes",
|
|
15
|
+
"Solver", "solve_ivp"]
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
__version__ = version("cubie")
|
|
19
|
+
except ImportError:
|
|
20
|
+
# Package is not installed
|
|
21
|
+
__version__ = "unknown"
|