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_host or from_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 prepare or prepare_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_version is 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.