Repository navigation
v1.19.0 #2210
shi-eric
announced in
Announcements
v1.19.0
#2210
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Warp v1.19.0
Warp v1.19 is a regular feature release with updates across the library. Highlights include reusable sparse assembly, new sparse preconditioners, Surface Nets meshing, CPU performance improvements, and experimental texture-sampling gradients.
New features
Reuse sparse structure across updates
In time stepping or a nonlinear FEM solve, a matrix's coefficients can change repeatedly while the mesh connectivity and degrees of freedom stay fixed. Reusing the nonzero pattern avoids sorting and merging the same row and column indices at every assembly.
warp.sparse.bsr_set_from_triplets(..., build_mapping=True)returns aBsrTripletMapfor subsequent value updates withbsr_set_from_mapped_values()(#1923).Build mappings with compact topology and
prune_numerical_zeros=False, and keep the destination topology unchanged. The default gather reduction avoids atomics.reduction="atomic"permits an accumulation order that can vary between runs.For repeated FEM bilinear assembly, select the corresponding construction policy, then reuse the same output matrix:
construction="mapped"uses an atomic-free reduction.construction="mapped_atomic"avoids temporary triplet-value storage, with a floating-point accumulation order that can vary between runs. Rebuild the cached map withcapacity="auto"after changing the assembly setup or modifying the matrix elsewhere. See the reuse requirements for the full constraints. These policies support CUDA graph replay after setup.Surface Nets meshing
Surface Nets offers a different vertex layout from Marching Cubes that can produce more evenly spaced vertices and fewer skinny triangles. This makes it worth trying for visualization and downstream mesh processing, including pipelines that use only triangles.
warp.geometry.IsoSurfaceNetsplaces vertices inside cells by averaging the points where the surface crosses their edges. It also produces quads directly, and shares theIsoSurfaceBaseinterface with Marching Cubes so you can compare extractors using the same scalar field (#1614).Inputs must be three-dimensional
wp.float32arrays with at least two nodes on every axis. Gradients propagate from vertex positions to field values, with cell classification and topology held fixed. Surfaces contained inside the grid are closed. Surfaces extending beyond it produce open meshes and may stop short of the grid boundary.The isosurface example now accepts
--methodand--topologyto select the extractor and face type.Sparse solver preconditioners
FSAI preconditioning
Symmetric positive-definite systems that are difficult for Jacobi preconditioning now have another option.
warp.optim.linear.FSAIbuilds a factorized sparse approximate inverse on the matrix's device and works as theMargument to conjugate-gradient or conjugate-residual solves. For fixed compact topology,reuse_pattern=Trueletsupdate()refit the factors after values change (#2039).Store both halves of the symmetric matrix. FSAI does not infer a missing triangle or globally check symmetry and positive definiteness. Inputs may use
wp.float16,wp.float32, orwp.float64. Construction synchronizes and must run outside graph capture. Updates keep the selected factor pattern, so rebuild if convergence deteriorates. After an eager warm-up update, updates can be captured in CUDA graphs. Inspectupdate_statusafter replay for numerical failures. FSAI does not support automatic differentiation.Block-Jacobi preconditioning
Try block-Jacobi when scalar Jacobi converges slowly and your matrix groups strongly coupled unknowns into blocks. Treating each diagonal block as a small coupled system lets the preconditioner account for relationships that scalar Jacobi ignores. Choose
"block_jacobi_direct","block_jacobi_sequential", or"block_jacobi_tile"throughwarp.optim.linear.preconditioner()(#1891).All three require a square BSR matrix with square blocks. The sequential and tile variants require symmetric positive-definite diagonal blocks. The tile variant supports
wp.float32andwp.float64. Scalar 1×1 blocks fall back to ordinary diagonal preconditioning.Textures
Texture sampling and gradients
Important
This is an experimental feature. The API may change without a formal deprecation cycle.
Optimization can now differentiate a sampled texture value with respect to its sampling coordinates and mipmap level of detail. Coordinate gradients require linear spatial filtering, and level-of-detail gradients require linear mip filtering. Closest filtering produces zero coordinate gradients, and LOD gradients are zero where the level of detail is clamped. Texture data remains non-differentiable (#1301).
Closest sampling is also available again for
wp.int32andwp.uint32textures, after its rejection in Warp 1.18. These samples convert numerically towp.float32without normalization. Integer textures default to closest filtering and reject linear filtering (#2065):Tile programming
Optional TF32 matrix multiplication
Tile matrix multiplication now offers TensorFloat-32 (TF32), which uses lower input precision than
wp.float32while retainingwp.float32accumulation and storage. Passprecision="tf32"towp.tile_matmul()(#937). Selectingprecision="tf32"may not improve performance with the current implementation.TF32 requires the cuBLASDx path and a CUDA compile target of compute capability 8.0 or higher. CPU execution and unsupported CUDA paths use
wp.float32arithmetic and emit a compilation warning. Adjoint matrix products use the same precision choice. Tensor Core use depends on the workload and tile configuration.We aim to improve TF32 performance in future releases by optimizing Warp's internal data layouts and how intermediate results are accumulated (#1965).
Python and compiler workflows
Array subclass support
Initialized subclasses of
wp.array,wp.indexedarray,wp.fabricarray, andwp.indexedfabricarraycan now be used wherever their parent array family is accepted. Your application can define its own array classes and Python helper methods while passing instances directly to kernels, function overloads,wp.map(), and struct fields (#1723).Python metadata and helper methods are unavailable inside kernels. New allocations and views are not guaranteed to preserve the subclass or its metadata. Kernel specialization uses the parent family, dtype, and dimensionality. See the array subclass guide for ownership and descriptor requirements.
Avoid object-array overhead in NumPy
Passing a Warp scalar type to NumPy constructors such as
np.zeros()ornp.full()previously created an object array, adding allocation and conversion overhead during setup. With NumPy 2.4 or newer, these calls now allocate native numeric arrays, avoiding that slowdown when preparing simulation data (#2038):wp.bfloat16is excluded. With older NumPy versions, usewp.dtype_to_numpy().Count set bits
Integer bit masks can now be counted with
wp.bit_count(), at Python scope or inside kernels. It accepts signed and unsigned scalar integers from 8 to 64 bits and returnswp.int32. Signed inputs count the fixed-width two's-complement representation, including the sign bit (#2071).Performance improvements
CPU execution and compilation
CPU compiler improvements can reduce simulation time without changes to kernels. Warp 1.19 enables loop and component vectorization and uses more aggressive helper inlining (#1954, #1994). Gains depend on the workload and CPU, and cold compilation can take longer.
Independent local checks compared the Warp 1.18.0 release wheel with a source build of Warp 1.19.0 on Linux, using one logical CPU of an AMD EPYC 9B45. These are complete Newton simulation-step timings after warmup, excluding compilation, model setup, and rendering:
Both versions ran identical Newton examples. Results vary by workload and CPU.
Compilation tradeoff: More aggressive CPU optimization can increase compilation time. Reusing compiled kernels amortizes that cost, while short jobs or workloads that frequently compile new kernels may take longer overall.
Other performance improvements
@wp.funccalls, including some backward passes, spend less time accessing shared tile storage (Tiles allocated inside loops or@wp.funccalls get scalar shared-memory loads #2085).wp.static()reuse parsed source, and Python-scope builtins have lower first-call latency for uncached argument signatures (@wp.kernel(module="unique")Python-side decoration cost makes factory-pattern kernels expensive #1486, Avoid repeated export checks for uncached Python built-in calls #1990).API Capture updates
Source fallback and loading from memory
Important
This is an experimental feature. The API may change without a formal deprecation cycle.
Saved CUDA graphs can now optionally retain generated source for recompilation when a packaged binary cannot load on the replay GPU. Set
source_fallback=Trueinwp.capture_save()to include that source alongside the compiled kernels. The file-based loader tries compatible binaries before recompiling the retained source (#1837).This option adds CUDA source JIT compilation on the deployment target when fallback is needed. It does not export a multi-architecture CUDA fatbin.
Source fallback requires the exact Warp version used for export. See the API Capture guide for compatibility limits.
When saving an existing API Capture graph:
For a graph saved as
"demo", deploy both the.wrpfile and its companion module directory:For standalone C++ replay, see the loader requirements and example.
Related updates:
wp.capture_load_from_bytes()loads its.wrpand module buffers without first writing them out as separate files (Support loading APIC captures from memory #1724).wp.array(data, dtype=wp.handle)now accepts Python sequences and NumPy arrays. Use this type for mesh IDs that must be remapped when a saved graph is loaded (Loaded APIC graph crashes when a kernel reads mesh ids from an array #2114).Given those saved artifacts, load and replay from buffers:
Loading from bytes requires compatible packaged binaries. Source recompilation and persistent replay-device caches are available through the file-based loader.
New and updated examples
nccl4pyfor halo exchange. It requiresmpi4py, an MPI implementation, and thenccl4pyextra matching the CUDA major version (Add a multi-GPU FDTD Luneburg lens example using NCCL #2086).Breaking changes
Removals in this release
maskedargument is removed. Usetopology="masked"inbsr_set_from_triplets(),bsr_assign(),bsr_set_transpose(),bsr_axpy(), andbsr_mm(). Remove explicitmasked=Falseto retain compact behavior (Add 4-array BSR storage with row capacity #1537).max_verts,max_tris, anddevicewhen constructingIsoSurfaceMarchingCubesor its deprecatedwp.MarchingCubesalias, and omitmax_vertsandmax_trisfromresize()calls. Remove accesses to the corresponding compatibility attributes, as well asidandruntime(Deprecate legacy MarchingCubes arguments and state #1594)..wrpfile format bumped to version 18API Capture files now use
.wrpformat version 18 to fix mesh-ID relocation from handle arrays and struct arrays. Recapture graphs saved in earlier formats. For mesh IDs that must remain valid after loading in another process, usewp.handle(#2114):When writing mesh IDs during capture, use a kernel that receives the mesh as a
wp.handleargument. IDs written witharray.fill_()during capture are not remapped on load, even in awp.handlearray.Deterministic kernels reject unsafe access patterns
Deterministic mode now rejects kernels that mix deterministic atomic updates with direct reads, writes, or ordinary atomics on the same array (#1817). For example, this kernel raises
wp.WarpDeterminismErrorwhen compiled:To make floating-point accumulation repeatable, Warp records the atomic updates and applies them in a fixed order after the kernel finishes. The read of
values[i]above would therefore happen before the increment is applied, despite appearing after it in the source. Rejecting the kernel prevents silently changing its result. Use separate input and output arrays, or move the reads into a later kernel launch.Parallel CUDA backward passes that both modify an array and accumulate into its gradient must also be split. Deterministic compilation failures now report a cause and suggested fix through
wp.WarpDeterminismError, aRuntimeErrorsubclass. Update exception handlers accordingly:Array subclass launch and gradient changes
Kernel launches now use the data, dtype, and shape stored by the Warp array itself, including for subclasses. An overridden NumPy or CUDA array interface can no longer substitute different data or a different layout. If your subclass relied on such reinterpretation, pass an explicit view or converted array instead. For example, if
astores float32 data shaped(4, 3)andkernelexpectswp.array[wp.vec3](#1723):Use a value conversion when you intend to change numerical dtype, rather than reinterpret its bytes.
Automatic differentiation now also uses the subclass's
.gradarray andretain_gradsetting, as for a regularwp.array. This can change gradients when kernels overwrite outputs or update arrays in place. Review those cases against the migration guide.Corrected loop-unrolling limit
An off-by-one fix means stepped
range()loops that previously slipped pastmax_unrollmay now compile as dynamic loops. This can affect gradients because dynamic-loop differentiation has limitations.Migration: Review stepped
range()loops in kernels that rely on unrolling. Where needed, raisemax_unrollto at least the loop's actual iteration count (#1980).For example,
range(0, 33, 2)has 17 iterations, so preserving unrolling requires a limit of at least 17:Custom FEM interpolation API changes
FEM interpolation converts requested physical field values into coefficients stored by a function space. This conversion previously failed to reproduce even constant vector fields in some Nédélec and Raviart-Thomas spaces. The fix changes
FunctionSpace.dof_value()to receive the weights and coordinate transform needed to compute those coefficients (#2062):Migration for custom implementations: If you implement your own
FunctionSpace, providelocal_inverse_value_map_inner()andlocal_inverse_value_map_outer(). Custom vector-valuedBasisSpaceorShapeFunctionimplementations must also providemake_node_dual_weight(). Warp's built-in spaces already provide these methods. The same update also fixes interpolation in quadratic and cubic B-spline spaces.Announcements
Upcoming removals
Python 3.10 support is deprecated and will be removed in Warp 1.20. Python 3.10 reached end of life on October 1, 2026. Runtime and source-build warnings now identify this change. Upgrade to Python 3.11 or newer to continue receiving Warp updates (Drop Python 3.10 support #2128).
The per-environment sequence form of
warp.fem.Nanogrid.from_environment_voxels()andwarp.fem.AdaptiveNanogrid.from_environment_voxels()will be removed in Warp 1.20. This form was deprecated in Warp 1.16. Pass flatpoints,point_envs, andenv_countinstead, withcell_levelsfor adaptive grids.warp.sparse.BsrMatrix.copy_nnz_async()will be removed in Warp 1.20. Usennz_sync()to read the exact host-side block count, andnotify_nnz_changed()after directly modifying sparse storage metadata.Windows on ARM64 wheels
Important
Windows on ARM64 wheel support is experimental.
Acknowledgments
We also thank the following contributors from outside the core Warp development team:
texture_sample#1301).tile_sort()silently produces wrong results, or faults, for acceptedblock_dimvalues #1748).wp.tile_map()backward fails to compile for built-ins likewp.exp,wp.sqrt,wp.divandwp.pow#2031,wp.static()around a built-in function raisesAttributeError: 'Function' object has no attribute 'user_overloads'#2014).wp.tile_sum(axis=...)silently under-reads the axis whenblock_dimis smaller than the warp size #1996).For a complete list of changes, see the full changelog.
This discussion was created from the release v1.19.0.
All reactions