CUDA¶
CUDA is an explicit optional backend. CPU functions retain their existing
defaults; use cgpy.cuda for device operations. Importing this module works
with a CPU-only wheel and does not import PyTorch. cuda.enabled() checks
whether its extension and runtime libraries load; cuda.is_available()
additionally checks the requested GPU. Neither query selects another backend.
Successful runtime-library loads are retained by path. If a library or the
extension fails to load, a later call retries without reopening libraries
already loaded successfully; failures are not permanently cached.
Local build¶
The pinned bootstrap supports Linux x86-64, Python 3.12, CUDA Toolkit 12.8
and an existing compatible host compiler. The preset uses /usr/bin/g++-13
and explicit architecture 75 for the RTX 2060. A compatible NVIDIA driver
must already be installed. A driver’s advertised CUDA API version is separate
from the compiler toolkit version; this package requires Toolkit >=12.8 and
<13 to match its cu12 runtime dependencies.
Keep the GPU toolchain and comparison dependencies in a separate environment:
python3.12 -m venv .venv-cuda
.venv-cuda/bin/python -m pip install --cache-dir .cache/pip \
-r requirements/test.lock -r requirements/cuda.lock
.venv-cuda/bin/python scripts/setup_cuda.py --prefix .deps/cuda-12.8
CG_REQUIRE_CUDA=1 make test PRESET=cuda-release PYTHON="$PWD/.venv-cuda/bin/python"
The bootstrap verifies pinned NVIDIA compiler/cudart/CCCL archive checksums
and assembles them under .deps/cuda-12.8. Public NVIDIA wheels supply the
runtime and cuBLAS headers/libraries. Downloads remain under .cache; no
system toolkit, shell configuration or PyTorch-private library is changed.
Initial downloads require network access. Override CUDA_ROOT,
CUDA_HOST_COMPILER or a semicolon-separated CUDA_ARCHITECTURES list
explicitly when using another compatible local toolchain. CUDA compilation
uses C++20 without fast-math.
CG_REQUIRE_CUDA=1 makes local GPU validation fail if the device or extension
is unavailable. Hosted compile-only CI leaves it unset and reports GPU skips.
Optional Compute Sanitizer checks use the same local toolkit:
.venv-cuda/bin/python scripts/setup_cuda.py --sanitizer
.deps/cuda-12.8/bin/compute-sanitizer --tool memcheck --error-exitcode 1 build/cuda-release/cg_cuda_gtests
.deps/cuda-12.8/bin/compute-sanitizer --tool racecheck --force-blocking-launches --error-exitcode 1 build/cuda-release/cg_cuda_gtests
.deps/cuda-12.8/bin/compute-sanitizer --tool synccheck --error-exitcode 1 build/cuda-release/cg_cuda_gtests
The recorded WSL2 run needed serialized racecheck launches because its default mode stalled on the concurrent-stream test. This setting checks kernel shared memory hazards under serialization; it does not establish race freedom for overlapping launches. Normal execution, memcheck and synccheck also exercise the concurrent-stream test without this flag.
To build a relocatable local CUDA wheel:
make wheel DIST_CUDA=ON DIST_DIR=dist/cuda PYTHON="$PWD/.venv-cuda/bin/python"
make test-dist DIST_CUDA=ON DIST_DIR=dist/cuda PYTHON="$PWD/.venv-cuda/bin/python"
.venv-cuda/bin/python -m pip install --cache-dir .cache/pip \
'dist/cuda/cgpy-0.4.0-cp312-cp312-linux_x86_64.whl[cuda]'
Select the actual wheel filename produced for your Python/platform. The
[cuda] extra installs nvidia-cuda-runtime-cu12==12.8.90 and
nvidia-cublas-cu12==12.8.4.1. The loader opens these public distributions
before importing cgpy._cuda; installed wheels have no toolkit RPATH and need
no source checkout or compiler. The driver remains a host prerequisite. CUDA,
native and BLAS wheels share the package version/tag; keep their output
directories separate. These are local Linux platform wheels, with no
manylinux portability claim. Installing .[cuda] alone installs the extra
dependencies but retains the default CPU build; enable CG_ENABLE_CUDA
explicitly when building the extension.
For a source install with the same local toolkit and public runtime extra:
.venv-cuda/bin/python -m pip install --cache-dir .cache/pip \
-Ccmake.define.CG_ENABLE_CUDA=ON \
-Ccmake.define.CMAKE_TOOLCHAIN_FILE=cmake/CGCudaToolchain.cmake \
-Ccmake.define.CUDAToolkit_ROOT="$PWD/.deps/cuda-12.8" \
'.[cuda]'
Replace .[cuda] with the selected local source archive followed by
[cuda] to install from an sdist. Pip’s isolated build environment supplies
the declared Python build tools; the CUDA toolkit remains the explicit local
path above.
Device data and prepared solves¶
Host transfers use explicitly named methods. Dense input is copied to
row-major float64 device storage. CSR upload accepts the same owning canonical
host cgpy.CsrMatrix used by CPU functions, without dense conversion.
Device CSR snapshots own their index arrays and values. External int32
and int64 indices are validated and stored as int64; ordering and
duplicates are never silently repaired. Uploads complete before temporary
host conversion buffers are released.
>>> A = np.array([[4.0, 1.0], [1.0, 3.0]])
>>> b = cuda.Vector.from_host(np.array([1.0, 2.0]))
>>> plan = cuda.prepare_host(A, preconditioner="jacobi", dense_kernel="native")
>>> workspace = plan.create_workspace()
>>> x = plan.create_vector()
>>> plan.solve_into(b, x, workspace=workspace, rtol=1e-10).converged
True
>>> np.testing.assert_allclose(x.to_host(), [1 / 11, 7 / 11])
>>> x.fill(0.0)
>>> plan.solve_into(b, x, workspace=workspace, fused=False).converged
True
>>> device_A = cuda.DenseMatrix.from_host(A)
>>> answer = cuda.solve(device_A, b, dense_kernel="cublas", algorithm="explicit")
>>> answer.converged
True
>>> np.testing.assert_allclose(answer.x.to_host(), [1 / 11, 7 / 11])
>>> host_csr = cgpy.CsrMatrix([0, 2, 4], [0, 1, 0, 1], [4., 1., 1., 3.], (2, 2))
>>> device_csr = cuda.CsrMatrix.from_host(host_csr)
>>> answer = cuda.solve(device_csr, b, preconditioner="jacobi")
>>> answer.converged
True
>>> np.testing.assert_allclose(answer.x.to_host(), [1 / 11, 7 / 11])
prepare accepts an already-device-resident matrix; prepare_host
normalizes and uploads host input. The plan owns a matrix snapshot and caches
the selected Jacobi inverse. solve allocates its result and workspace;
solve_into treats x as the initial guess/output and requires an
explicit workspace. Reinitialize x before repeated comparisons.
The workspace fixes the device, borrowed stream, dense kernel and execution
mode. Strict cuda.solve_into(A, b, x, workspace=workspace, ...) uses those
settings directly; it has no separate dense_kernel or execution_mode
argument. Choose the kernel when constructing a workspace, session or prepared
solver. A prepared solver rejects a workspace with a different kernel before
writing output. Convenience cuda.solve accepts explicit kernel and mode
choices when it creates its fresh workspace.
dense_kernel="native" selects project CUDA kernels; "cublas" selects
double-precision cuBLAS GEMV for dense operators only. CSR rejects cuBLAS.
fused independently selects supported vector/reduction fusion. Standard
CG supports Jacobi; the explicit algorithm requires no preconditioner. Both
modes retain the true-residual stopping rule and failure statuses described
in Python API. Invalid shapes, options, dtypes or layouts raise ValueError;
CUDA allocation, stream and execution failures raise RuntimeError.
Execution-resource reuse¶
cuda.solve remains a fresh call: it allocates output/workspace and sets up
Jacobi on each invocation. cuda.Session reuses one existing workspace and
its execution resources while still returning a fresh owning output and
recomputing Jacobi each time. It borrows the matrix for that call and retains
no matrix or preconditioner cache. Use prepare when an immutable matrix
snapshot and cached Jacobi are desired instead.
Size, device, borrowed stream, dense kernel and execution mode are readonly
session properties. The stream must outlive the session. One session permits
one active call, including input validation, initialization and setup;
overlapping or reentrant calls raise ValueError. Distinct sessions own
independent resources. Results remain valid after another call or session
release. Each call completes on its selected stream before returning.
>>> matrix = cuda.DenseMatrix.from_host([[4., 1.], [1., 3.]])
>>> rhs = cuda.Vector.from_host([1., 2.])
>>> session = cuda.Session(2, dense_kernel="cublas", execution_mode="device")
>>> first = session.solve(matrix, rhs, preconditioner="jacobi")
>>> second = session.solve(matrix, rhs, preconditioner="jacobi")
>>> first.converged and second.converged and first.x is not second.x
True
>>> del session
>>> np.testing.assert_allclose(first.x.to_host(), [1 / 11, 7 / 11])
>>> np.testing.assert_allclose(second.x.to_host(), [1 / 11, 7 / 11])
Execution mode¶
The default execution_mode="host" uses host-controlled scalar reductions.
Select "device" explicitly to keep recurrence scalars in GPU storage.
The mode belongs to a workspace and is fixed at construction:
workspace = plan.create_workspace(execution_mode="device")
info = plan.solve_into(b, x, workspace=workspace)
# Strict solve_into also uses this workspace's device, stream and kernel.
result = cuda.solve(device_A, b, execution_mode="device")
cuda.Workspace(..., execution_mode="device") provides the same selection.
The readonly workspace.execution_mode reports "host" or "device".
Preparation fixes the operator, preconditioner and dense kernel; the same
immutable plan can serve separate host/device workspaces. There is no
kernel or execution-mode argument on strict or prepared solve_into methods.
Kernel selection, vector fusion, algorithm and execution mode are independent.
Both modes support native/cuBLAS dense and native CSR, standard CG/Jacobi PCG
and the unpreconditioned explicit formulation. Neither choice selects another
mode automatically.
Device execution still reads completed status packets and synchronizes with the host. An ordinary standard-CG iteration reads one packet; the explicit formulation reads two so alpha is validated before updating the solution or launching the true-residual matvec. Candidate convergence, residual refresh, range fallback and numerical breakdown can require additional checks. Matrix-vector counts describe actual executed operations; no speculative matvec is launched to hide an unresolved scalar decision. Both modes preserve the true-residual gate, failure statuses, stream completion and ownership contracts. Their reduction/fusion order can affect rounding and counts; performance must be measured under the same declared numerical policy.
>>> plan = cuda.prepare_host(np.array([[4., 1.], [1., 3.]]))
>>> b = cuda.Vector.from_host([1., 2.])
>>> x = plan.create_vector()
>>> workspace = plan.create_workspace(execution_mode="device")
>>> workspace.execution_mode
'device'
>>> plan.solve_into(b, x, workspace=workspace).converged
True
>>> np.testing.assert_allclose(x.to_host(), [1 / 11, 7 / 11])
>>> plan.create_workspace().execution_mode
'host'
Streams, lifetime and interoperability¶
Each operation uses an explicit device and borrowed integer stream
handle; zero is the legacy default stream. Calls finish on that stream before
returning and restore the caller’s current device. They do not change a
PyTorch current stream. A nondefault stream must belong to the selected
device and outlive every workspace using it.
The strict path accepts compatible CUDA Array Interface arrays, including detached contiguous float64 CUDA tensors. Vector output must be writable and disjoint from the right-hand side and operator. Native device ownership is retained through a solve, and external input owners are retained across GIL release. Keep external storage alive and free from conflicting mutation; retaining an owner does not prevent writes from another stream. Version 3 producer stream dependencies are honored. Version 2 producers have no stream metadata: make their writes ready on the selected stream or synchronize them before passing their arrays. Explicit host/device-copy methods complete before returning; exported native vectors have no pending producer work.
cuda.SupportsCudaArrayInterface describes a readable
__cuda_array_interface__ property returning object. External exporters
need not inherit from it. Solver matrix inputs also accept the owning
DenseMatrix and CsrMatrix types, which expose no array interface;
Vector already satisfies the protocol. Device factories and vector inputs
require the array interface rather than a matrix owner.
This is a static capability declaration. The native parser still requires a
dict descriptor and validates its contents and actual device storage.
The protocol does not prove float64 layout, writable output, stream readiness
or owner lifetime. Torch and CuPy remain optional external producers; incomplete
third-party annotations can weaken checks. A Torch CPU tensor can satisfy
the static declaration even though accessing its interface fails at runtime.
No runtime protocol check or third-party import is added.
Prepared matrix and Jacobi snapshots can be shared, but each overlapping solve needs its own output and workspace. The workspace owns device vectors, reduction buffers and inline host scalar storage. A cuBLAS workspace additionally owns its handle and 4 MiB scratch allocation; overlapping reuse raises an error. It fixes the device, stream, kernel and execution mode used for its solve. No API reinterprets a NumPy host pointer as a device pointer.
All device entry points use the same native CUDA Array Interface parser.
Missing required fields and invalid built-in metadata types, dimensions,
layout or pointers raise ValueError. An absent interface or AttributeError
from its getter is treated as a missing protocol. Other exceptions from custom
descriptor getters, and exceptions from integer conversion callbacks,
propagate unchanged. Convenience and session solves validate b and optional
x0, including matching vector sizes, before allocating or initializing their
fresh output. Host uploads use the same aligned contiguous float64 checks as
CPU borrowing boundaries.
Native C++ vector and matrix views are lightweight borrowing descriptors.
Their pointer, extent, index type and device metadata are private, accessed
through methods such as data(), size() and device(). They neither
allocate nor extend storage lifetime. Accessors are callable from host/device
code; host access to data() does not make a device pointer dereferenceable
on the CPU. Boundary validation still checks device identity and pointer
metadata before operations.
Validation and benchmarks¶
Hosted CI compiles the pinned CUDA toolkit without a GPU and explicitly skips
device tests when unavailable. The optional cuda_gpu_tests workflow input
requires a configured self-hosted runner labeled linux, x64, gpu;
that job requires a real device before executing tests. CPU builds exercise
disabled-extension behavior:
>>> cuda.is_available()
False
>>> try:
... cuda.Vector(2)
... except RuntimeError:
... rejected = True
... else:
... rejected = False
>>> rejected
True
To document the imported CUDA build with executable examples:
.venv-cuda/bin/python -m pip install --cache-dir .cache/pip -r requirements/docs.lock
make docs doctest PRESET=cuda-release PYTHON="$PWD/.venv-cuda/bin/python"
See Benchmarks for explicit resident-GPU comparison commands. Prepared, API and session scopes exclude host upload and correctness-gate download. Reported upload/setup metadata is separate from solve latency; none of these scopes measures host-to-host end-to-end performance. Timings include host dispatch, iteration scalar synchronization and final completion, rather than only GPU kernel events.
API reference¶
CUDA solves return the shared cgpy.SolveResult documented in
Python API; its x owns a device Vector.
Explicit CUDA CG/PCG with device-owned solutions and synchronous calls.
Importing this module works with CPU-only builds. CUDA operations require an
optional extension, its NVIDIA runtime dependencies and a compatible GPU.
Uploads use explicitly named from_host / prepare_host methods.
- class cgpy.cuda.CsrMatrix¶
Own an immutable canonical CSR device snapshot.
Columns must be sorted, unique and in range in each row; pointers must be monotone, start at zero and end at nnz. Setup validates the indices once; it does not silently sort or coalesce them. Use native CUDA CSR kernels.
- classmethod from_host(source, *, device=0, stream=0)¶
Explicitly upload a public CPU cgpy.CsrMatrix snapshot.
- classmethod from_device(indptr, indices, data, shape, *, device=0, stream=0)¶
Validate canonical structure and snapshot device CSR arrays.
Pointers and columns must use matching signed int32 or int64 types. Values must be float64. All three arrays must be contiguous on the selected device. A setup-time index download is included in this call.
- property shape¶
Matrix dimensions (rows, columns).
- Type:
tuple
- property nnz¶
Number of stored entries.
- Type:
int
- property device¶
Device ordinal of the snapshot.
- Type:
int
- class cgpy.cuda.DenseMatrix¶
Own an immutable dense float64 device snapshot.
Construct with
from_hostorfrom_device. The matrix has no public mutable storage view; prepared solvers take a further owning snapshot.- classmethod from_host(source, *, device=0, stream=0)¶
Normalize a real 2D host array and explicitly upload a snapshot.
- classmethod from_device(source, *, device=0, stream=0)¶
Snapshot a C-contiguous float64 CUDA Array Interface matrix.
Version-2 producers must be ready on the specified stream. Version-3 producer stream dependencies are honored. The call is synchronous.
- property shape¶
Matrix dimensions (rows, columns).
- Type:
tuple
- property device¶
Device ordinal of the snapshot.
- Type:
int
- class cgpy.cuda.PreparedSolver¶
Own an immutable GPU matrix and optional Jacobi inverse.
Construct with
prepareorprepare_host. The device, explicit dense kernel and preconditioner are fixed; fusion, tolerances and refresh policy are selected on each solve. Matrix snapshots and Jacobi setup are outside prepared-call timing. Distinct workspace/output pairs are required for overlapping solves; input storage must stay free of conflicting writes.- property size¶
Nonempty square system dimension.
- Type:
int
- property shape¶
Square matrix dimensions.
- Type:
tuple
- property device¶
Device ordinal of the matrix snapshot.
- Type:
int
- property dense_kernel¶
Explicit native or cublas dense kernel; CSR requires native.
- Type:
str
- property preconditioner¶
Fixed none or jacobi selection.
- Type:
str
- property preconditioner_setup_ns¶
Completed Jacobi setup nanoseconds, or zero without Jacobi.
- Type:
int
- property preconditioner_nbytes¶
Stored Jacobi inverse bytes, excluding runtime resources.
- Type:
int
- create_workspace(*, stream=0, execution_mode='host')¶
Allocate storage with an explicit stream and scalar-control engine.
execution_mode selects host (default) or device and is fixed for this workspace. The immutable plan can serve either mode with distinct workspaces; solves have no redundant execution-mode argument.
- create_vector(*, stream=0)¶
Allocate an independent zero output on this plan’s device.
- solve_into(b, x, *, workspace, rtol=1e-08, atol=0.0, maxiter=1000, algorithm='standard', fused=True, residual_refresh=50)¶
Solve with cached setup and caller-owned GPU output/workspace.
- Parameters:
b (Vector or CUDA Array Interface object) – Contiguous float64 device vectors. x is writable initial guess and output, disjoint from b. Reset x before a repeated comparison.
x (Vector or CUDA Array Interface object) – Contiguous float64 device vectors. x is writable initial guess and output, disjoint from b. Reset x before a repeated comparison.
workspace (Workspace) – Matching size/device/kernel, unused by overlapping calls. Its stream determines execution and the version-2 producer readiness contract. Its execution_mode selects the native host/device scalar engine.
rtol (float) – Finite nonnegative true-residual tolerances.
atol (float) – Finite nonnegative true-residual tolerances.
maxiter (int) – Nonnegative update limit; zero checks only the initial guess.
algorithm ({'standard', 'explicit'}) – Shared CPU/GPU policy. Jacobi requires standard CG.
fused (bool) – Select fused or separate native operations for either kernel.
residual_refresh (int) – Standard-CG residual replacement/restart interval; zero disables periodic replacement, retaining candidate/final true checks.
- Returns:
cgpy.SolveInfo – Numerical status and counts. Numerical failure may leave a current approximation in x. Native solving releases the GIL and returns synchronously on the workspace stream.
- Raises:
ValueError – Invalid array type/layout/device, aliasing, workspace, options or kernel/preconditioner combination.
RuntimeError – CUDA allocation, stream or execution failure.
- class cgpy.cuda.Session(size, *, device=0, stream=0, dense_kernel='native', execution_mode='host')¶
Reuse one CUDA workspace while returning a fresh solution each call.
- Parameters:
size (int) – Fixed system dimension.
device (int, default=0) – Fixed CUDA device ordinal.
stream (int, default=0) – Borrowed stream handle, which must outlive the session.
dense_kernel ({'native', 'cublas'}, default='native') – Fixed backend. CSR matrices require native.
execution_mode ({'host', 'device'}, default='host') – Fixed scalar-control engine, independent of fusion.
Notes
Owns an existing Workspace, including its vectors and backend resources. Matrix inputs are borrowed per call; no matrix or Jacobi inverse is cached. Each solve allocates an independent output and constructs Jacobi when selected. Use prepare to snapshot a matrix and cache Jacobi instead. Calls complete synchronously. Overlapping or reentrant calls on the same session raise ValueError; independent sessions own independent resources. Construction and destruction are outside session-scope benchmark timing.
- property size¶
Fixed system dimension.
- Type:
int
- property device¶
Fixed CUDA device ordinal.
- Type:
int
- property stream¶
Borrowed stream used by all calls.
- Type:
int
- property dense_kernel¶
Fixed native or cuBLAS backend.
- Type:
str
- property execution_mode¶
Fixed host or device scalar-control engine.
- Type:
str
- solve(A, b, x0=None, *, rtol=1e-08, atol=0.0, maxiter=1000, algorithm='standard', fused=True, residual_refresh=50, preconditioner='none')¶
Solve a resident system using fresh output and per-call setup.
- Parameters:
A (DenseMatrix, CsrMatrix or CUDA Array Interface object) – Borrowed nonempty square SPD matrix matching size/device. Different matrices may be supplied on successive calls; inputs must remain free of conflicting writes until the synchronous call returns.
b (Vector or CUDA Array Interface object) – Contiguous float64 vectors. Omitted x0 selects zero. Its reset or device copy is included in each call; inputs are preserved.
x0 (Vector or CUDA Array Interface object) – Contiguous float64 vectors. Omitted x0 selects zero. Its reset or device copy is included in each call; inputs are preserved.
rtol (float) – Finite nonnegative true-residual tolerances.
atol (float) – Finite nonnegative true-residual tolerances.
maxiter (int) – Nonnegative update limit; zero checks only the initial guess.
algorithm ({'standard', 'explicit'}, default='standard') – Existing CG policy; Jacobi requires standard.
fused (bool, default=True) – Select fused or separate native operations.
residual_refresh (int, default=50) – Standard residual replacement interval; zero disables periodic replacement, retaining candidate/final true-residual checks.
preconditioner ({'none', 'jacobi'}, default='none') – Recomputed from this call’s matrix, never retained by the session.
- Returns:
SolveResult – Independent owning GPU Vector plus status and operation counts. Earlier results remain valid after another call or session deletion.
- Raises:
ValueError – Invalid inputs/options, mismatched size/device, unsupported CSR backend, or overlapping/reentrant use of this session.
RuntimeError – CUDA allocation, stream or execution failure.
Notes
Device/stream/kernel/execution mode are fixed at construction. Version-2 producers must be ready on the borrowed stream; version-3 dependencies are honored. No upload, download, CPU fallback or hidden pool occurs. Numerical failures use the same returned statuses as cuda.solve.
- class cgpy.cuda.SupportsCudaArrayInterface(*args, **kwargs)¶
Expose a CUDA descriptor whose metadata is validated at runtime.
- class cgpy.cuda.Vector(size, *, device=0, stream=0)¶
Own a writable float64 GPU vector, initialized to zero.
- Parameters:
size (int) – Nonnegative number of elements.
device (int, default=0) – Device ordinal, fixed for the allocation’s lifetime.
stream (int, default=0) – Borrowed stream for initialization. All methods finish their work before returning; CUDA Array Interface exports have no pending work.
Notes
__cuda_array_interface__permits zero-copy interoperability. Its consumer must retain this owner and avoid conflicting mutation. Inputs from PyTorch must be detached float64 contiguous CUDA tensors. Allocation and host transfers belong outside a prepared-solve timing interval.- classmethod from_host(source, *, device=0, stream=0)¶
Snapshot a real host vector, normalizing and uploading it once.
- property size¶
Number of allocated float64 elements.
- Type:
int
- property shape¶
One-dimensional shape.
- Type:
tuple
- property device¶
Device ordinal of the owned allocation.
- Type:
int
- fill(value, *, stream=0)¶
Fill the vector on a borrowed stream and wait for completion.
- copy_from_host(source, *, stream=0)¶
Normalize and synchronously upload a matching host vector.
- copy_from_device(source, *, stream=0)¶
Copy a matching contiguous float64 device vector on a stream.
CUDA Array Interface version 3 producer dependencies are honored. A version-2 producer, including PyTorch, must be ready on this same stream or explicitly synchronized by the caller before the copy.
- to_host(*, stream=0)¶
Synchronously download an independent NumPy float64 snapshot.
- class cgpy.cuda.Workspace(size, *, device=0, stream=0, dense_kernel='native', execution_mode='host')¶
Own solver vectors, reduction scratch and selected backend resources.
- Parameters:
size (int) – Matrix dimension. Storage is allocated once and reused across solves.
device (int, default=0) – Device ordinal, fixed at construction.
stream (int, default=0) – Borrowed stream handle, fixed at construction; it must outlive this workspace. One workspace/output pair belongs to one active solve.
dense_kernel ({'native', 'cublas'}, default='native') – Fixed kernel selection. Only cublas allocates a handle and its additional workspace; all resources are prepared at construction.
execution_mode ({'host', 'device'}, default='host') – Fixed native scalar-control engine. Host reads individual reductions; device keeps recurrence scalars on the GPU and reads completed status packets. Both modes finish on the workspace stream before returning.
Notes
Device, stream, kernel and execution mode come from this workspace when calling solve_into. A prepared solver additionally requires a matching kernel because its matrix setup fixes that choice. Distinct workspaces permit overlapping calls against an immutable plan. Each workspace owns its backend resources and reduction storage. Reuse on the same stream is safe because public solves return only after completion.
- property size¶
Fixed system dimension.
- Type:
int
- property device¶
Fixed device ordinal.
- Type:
int
- property stream¶
Borrowed stream handle used by all workspace operations.
- Type:
int
- property dense_kernel¶
Kernel whose resources were prepared at construction.
- Type:
str
- property execution_mode¶
Fixed host or device scalar-control engine.
- Type:
str
- cgpy.cuda.build_info()¶
Return CUDA build metadata or the reason the extension is disabled.
- cgpy.cuda.enabled()¶
Return whether the CUDA extension loads, without requiring a GPU.
- cgpy.cuda.is_available(device=0)¶
Return whether the extension loads and CUDA exposes the requested GPU.
A disabled extension or absent/incompatible driver returns False. This capability query never selects a different backend or device. The GPU must also support an architecture included in the explicitly configured build.
- cgpy.cuda.prepare(A, *, device=0, stream=0, dense_kernel='native', preconditioner='none')¶
Snapshot a device matrix and cache optional Jacobi setup.
A must be a DenseMatrix, CsrMatrix or contiguous float64 CUDA Array Interface matrix. It must be nonempty, square and SPD. Kernel is explicitly native or cublas; CSR requires native. Jacobi requires a finite positive diagonal/inverse and standard CG. Version-2 producers must be ready on the borrowed stream. Setup is synchronous; future solves select their stream by creating a workspace. Returns PreparedSolver.
- cgpy.cuda.prepare_host(A, *, device=0, stream=0, dense_kernel='native', preconditioner='none')¶
Upload a real host dense/CPU-CSR matrix and prepare a snapshot.
Transfers, snapshot allocation and optional Jacobi setup are included in preparation. Repeated solve_into calls keep these costs outside timing. Kernel, device and preconditioner have the same contract as prepare.
- cgpy.cuda.runtime_info(device=0)¶
Describe the GPU, compute capability and CUDA runtime versions.
driver_api_versionis the driver’s supported CUDA API version, rather than NVIDIA’s display-driver release number. Raises RuntimeError when CUDA or the requested device is unavailable.
- cgpy.cuda.solve(A, b, x0=None, *, device=0, stream=0, rtol=1e-08, atol=0.0, maxiter=1000, algorithm='standard', fused=True, residual_refresh=50, preconditioner='none', dense_kernel='native', execution_mode='host')¶
Solve a device system with fresh output, workspace and per-call setup.
A is an owned CUDA matrix or contiguous float64 CUDA Array Interface dense matrix; b and optional x0 are device float64 vectors. Omitted x0 selects zero. Inputs are preserved; the result owns a separate GPU vector. Device and borrowed stream are explicit. No host upload/download is performed. Standard/explicit CG, Jacobi, fusion, tolerance and error contracts follow solve_into; numerical failures are reported in the returned status. Native computation releases the GIL and finishes before returning. execution_mode is explicitly host (default) or device, selecting the new workspace’s scalar-control engine independently of dense_kernel/fused. Device mode retains recurrence scalars on the GPU; completed status packets and true-residual checks still synchronize with the host.
- Returns:
SolveResult – GPU Vector and status/counts. Download with result.x.to_host().
- cgpy.cuda.solve_into(A, b, x, *, workspace, rtol=1e-08, atol=0.0, maxiter=1000, algorithm='standard', fused=True, residual_refresh=50, preconditioner='none')¶
Solve a device-resident system with caller-owned output and workspace.
A is an owned CUDA matrix or a contiguous float64 CUDA Array Interface dense matrix; b/x are device float64 vectors. No host conversion or CPU fallback occurs. Output and workspace must be disjoint from inputs and unused by overlapping calls. The workspace fixes the device, stream, dense kernel and host/device scalar-control execution_mode. Version-2 producers must be ready on that stream; version-3 dependencies are honored. Each call constructs Jacobi when selected. Use prepare to cache setup. Options, exceptions, synchronous completion and SolveInfo follow PreparedSolver.solve_into, including the true-residual stopping rule max(atol, rtol*||b||2). Callers must supply an SPD matrix.
- cgpy.cuda.synchronize(device=0, stream=0)¶
Wait for a CUDA stream and restore the caller’s current device.
- Parameters:
device (int, default=0) – Explicit device ordinal.
stream (int, default=0) – Borrowed CUDA stream handle on that device; zero is the legacy default. The caller must keep a nondefault stream alive until the call returns.