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.
Files changed (62) hide show
  1. cubie-0.0.2/PKG-INFO +71 -0
  2. cubie-0.0.2/pyproject.toml +99 -0
  3. cubie-0.0.2/readme.md +41 -0
  4. cubie-0.0.2/setup.cfg +4 -0
  5. cubie-0.0.2/src/LICENSE +21 -0
  6. cubie-0.0.2/src/cubie/CUDAFactory.py +227 -0
  7. cubie-0.0.2/src/cubie/__init__.py +21 -0
  8. cubie-0.0.2/src/cubie/_utils.py +352 -0
  9. cubie-0.0.2/src/cubie/batchsolving/BatchGridBuilder.py +664 -0
  10. cubie-0.0.2/src/cubie/batchsolving/BatchSolverConfig.py +15 -0
  11. cubie-0.0.2/src/cubie/batchsolving/BatchSolverKernel.py +594 -0
  12. cubie-0.0.2/src/cubie/batchsolving/SystemInterface.py +251 -0
  13. cubie-0.0.2/src/cubie/batchsolving/__init__.py +19 -0
  14. cubie-0.0.2/src/cubie/batchsolving/_utils.py +54 -0
  15. cubie-0.0.2/src/cubie/batchsolving/arrays/BaseArrayManager.py +456 -0
  16. cubie-0.0.2/src/cubie/batchsolving/arrays/BatchInputArrays.py +180 -0
  17. cubie-0.0.2/src/cubie/batchsolving/arrays/BatchOutputArrays.py +193 -0
  18. cubie-0.0.2/src/cubie/batchsolving/arrays/__init__.py +0 -0
  19. cubie-0.0.2/src/cubie/batchsolving/solver.py +551 -0
  20. cubie-0.0.2/src/cubie/batchsolving/solveresult.py +398 -0
  21. cubie-0.0.2/src/cubie/cudasim_utils.py +75 -0
  22. cubie-0.0.2/src/cubie/integrators/IntegratorRunSettings.py +143 -0
  23. cubie-0.0.2/src/cubie/integrators/SingleIntegratorRun.py +377 -0
  24. cubie-0.0.2/src/cubie/integrators/__init__.py +3 -0
  25. cubie-0.0.2/src/cubie/integrators/algorithms/IntegratorLoopSettings.py +84 -0
  26. cubie-0.0.2/src/cubie/integrators/algorithms/LoopStepConfig.py +39 -0
  27. cubie-0.0.2/src/cubie/integrators/algorithms/__init__.py +25 -0
  28. cubie-0.0.2/src/cubie/integrators/algorithms/euler.py +153 -0
  29. cubie-0.0.2/src/cubie/integrators/algorithms/genericIntegratorAlgorithm.py +186 -0
  30. cubie-0.0.2/src/cubie/memory/__init__.py +10 -0
  31. cubie-0.0.2/src/cubie/memory/array_requests.py +55 -0
  32. cubie-0.0.2/src/cubie/memory/cupy_emm.py +205 -0
  33. cubie-0.0.2/src/cubie/memory/mem_manager.py +772 -0
  34. cubie-0.0.2/src/cubie/memory/stream_groups.py +85 -0
  35. cubie-0.0.2/src/cubie/outputhandling/__init__.py +9 -0
  36. cubie-0.0.2/src/cubie/outputhandling/_utils.py +20 -0
  37. cubie-0.0.2/src/cubie/outputhandling/output_config.py +437 -0
  38. cubie-0.0.2/src/cubie/outputhandling/output_functions.py +232 -0
  39. cubie-0.0.2/src/cubie/outputhandling/output_sizes.py +279 -0
  40. cubie-0.0.2/src/cubie/outputhandling/save_state.py +44 -0
  41. cubie-0.0.2/src/cubie/outputhandling/save_summaries.py +131 -0
  42. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/__init__.py +12 -0
  43. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/max.py +66 -0
  44. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/mean.py +65 -0
  45. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/metrics.py +346 -0
  46. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/peaks.py +94 -0
  47. cubie-0.0.2/src/cubie/outputhandling/summarymetrics/rms.py +70 -0
  48. cubie-0.0.2/src/cubie/outputhandling/update_summaries.py +110 -0
  49. cubie-0.0.2/src/cubie/systemmodels/SystemValues.py +397 -0
  50. cubie-0.0.2/src/cubie/systemmodels/__init__.py +3 -0
  51. cubie-0.0.2/src/cubie/systemmodels/systems/GenericODE.py +282 -0
  52. cubie-0.0.2/src/cubie/systemmodels/systems/ODEData.py +93 -0
  53. cubie-0.0.2/src/cubie/systemmodels/systems/__init__.py +5 -0
  54. cubie-0.0.2/src/cubie/systemmodels/systems/decays.py +82 -0
  55. cubie-0.0.2/src/cubie/systemmodels/systems/threeCM.py +170 -0
  56. cubie-0.0.2/src/cubie.egg-info/PKG-INFO +71 -0
  57. cubie-0.0.2/src/cubie.egg-info/SOURCES.txt +60 -0
  58. cubie-0.0.2/src/cubie.egg-info/dependency_links.txt +1 -0
  59. cubie-0.0.2/src/cubie.egg-info/requires.txt +21 -0
  60. cubie-0.0.2/src/cubie.egg-info/top_level.txt +1 -0
  61. cubie-0.0.2/tests/test_CUDAFactory.py +148 -0
  62. 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
+ [![docs](https://github.com/ccam80/smc/actions/workflows/documentation.yml/badge.svg)](https://github.com/ccam80/smc/actions/workflows/documentation.yml) [![CUDA tests](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml/badge.svg)](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml) [![Python Tests](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml/badge.svg)](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml) [![test build](https://github.com/ccam80/cubie/actions/workflows/test_pypi.yml/badge.svg)](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
+ [![docs](https://github.com/ccam80/smc/actions/workflows/documentation.yml/badge.svg)](https://github.com/ccam80/smc/actions/workflows/documentation.yml) [![CUDA tests](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml/badge.svg)](https://github.com/ccam80/cubie/actions/workflows/ci_cuda_tests.yml) [![Python Tests](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml/badge.svg)](https://github.com/ccam80/cubie/actions/workflows/ci_nocuda_tests.yml) [![test build](https://github.com/ccam80/cubie/actions/workflows/test_pypi.yml/badge.svg)](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
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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"