sparse smart structured scalable

A High-Performance Framework for Sparse Matrices

sTiles targets the full Cholesky → solve → selected inverse pipeline on a single shared-memory node, using tile-based parallelism. Today's release handles symmetric positive-definite matrices across the entire spectrum, from very sparse to fully dense, with one unified solver. Distributed-memory support is on the roadmap.

Sparse density pattern
Sparse
Semi-Sparse density pattern
Semi-Sparse
Semi-Dense density pattern
Semi-Dense
Dense density pattern
Dense
In Production

Deployed in R-INLA

sTiles powers the sparse matrix engine of R-INLA, a widely-used package for Bayesian inference with applications in spatial statistics, epidemiology, and ecology. The framework is designed for any application requiring high-performance sparse Cholesky factorization and selective inversion.

Research

Publications

Peer-reviewed research papers describing the sTiles framework and its applications.

📄

sTiles: An Accelerated Computational Framework for Sparse Factorizations of Structured Matrices

Esmail Abdul Fattah, Hatem Ltaief, Håvard Rue, David Keyes

Core paper focused on the sTiles solver architecture and tile-based algorithms.

Read on IEEE Xplore →
ISC 2025
📄

GPU-Accelerated Parallel Selected Inversion for Structured Matrices Using sTiles

Esmail Abdul Fattah, Hatem Ltaief, Håvard Rue, David Keyes

Focus on GPU acceleration for selected inversion computations.

Read on arXiv →
arXiv Preprint
Capabilities

Key Features

Designed for high-performance sparse matrix computations with modern hardware.

Tile-Based Factorization

Configurable tile sizes for cache-friendly Cholesky factorizations. Smart tiles adapt storage format based on fill-in density.

Intelligent Ordering

Multiple ordering algorithms including AMD, RCM, and SCOTCH nested dissection, with auto, parallel, and smart ordering strategies.

Selected Inversion

Efficient computation of selected inverse elements matching the sparsity pattern. Much faster than full matrix inversion.

Multi-RHS Solvers

Solve multiple right-hand sides efficiently by reusing factorizations. Triangular solves: L, L^T, LL^T.

Python, R & C/C++

Use sTiles from Python (pip install sTiles), R, or C/C++. Pre-built for Linux, macOS, and Windows. The native library downloads automatically, no compiler needed.

Learn

Examples

Pick your language, install in one line, and run.

Install from PyPIpip
pip install sTiles
quickstart.pyPython
import numpy as np, scipy.sparse as sp
from sTiles import sTiles

# Q: a symmetric positive-definite sparse matrix
Q = sp.diags([[-1.]*5, [4.]*6, [-1.]*5], [-1, 0, 1], format="csc")

# Two phases. Preprocessing (ordering + tile layout) depends only on WHERE the
# non-zeros are, so it runs once; the numeric factorization runs per value set.
s = sTiles.analyze(Q, cores=4, inverse=True)   # 1. symbolic: ordering + layout (cores=1 by default)
s.factorize()                                  # 2. numeric Cholesky

s.logdet             # log|Q|
s.solve(np.ones(6))  # solve Q x = b
s.selinv_diag()      # diag(Q^-1), the marginal variances
s.selinv_elm(0, 0)   # any inverse element within the factor pattern

# 3. new values, same pattern: reuses the analysis, pays only numeric cost
s.update(Q * 1.1)
s.logdet

s.summary()          # dimensions, fill, and the two phase timings
s.close()

# sTiles(Q, ...) runs analyze + factorize in one call, for a single
# factorization where the split does not matter.

Pure Python (ctypes), no compiler. The native libstiles downloads automatically on first import.
cores defaults to 1 and is never auto-detected: pass cores=os.cpu_count() to use every core, or any number up to it, to actually get parallel speed.

Install from GitHubR
remotes::install_github("esmail-abdulfattah/sTiles", subdir = "R/sTiles")
quickstart.RR
library(sTiles); library(Matrix)

# Q: a symmetric positive-definite sparse matrix
Q <- as(bandSparse(6, k = c(0, 1),
        diagonals = list(rep(4, 6), rep(-1, 5)), symmetric = TRUE), "CsparseMatrix")

# Two phases. Preprocessing (ordering + tile layout) depends only on WHERE the
# non-zeros are, so it runs once; the numeric factorization runs per value set.
s <- sTiles_analyze(Q, cores = 4, inverse = TRUE)  # 1. symbolic: ordering + layout (cores=1 by default)
sTiles_factorize(s)                               # 2. numeric Cholesky

sTiles_logdet(s)            # log|Q|
sTiles_solve(s, rep(1, 6))  # solve Q x = b
sTiles_selinv_diag(s)       # diag(Q^-1), the marginal variances
sTiles_selinv_elm(s, 1, 1)  # any inverse element within the factor pattern

# 3. new values, same pattern: reuses the analysis, pays only numeric cost
Q2 <- Q; Q2@x <- Q2@x * 1.1
sTiles_update(s, Q2)
sTiles_logdet(s)

sTiles_summary(s)           # dimensions, fill, and the two phase timings
sTiles_close(s)

# sTiles(Q, ...) does analyze + factorize in one call when you only need one
# factorization.

No compiler. The native libstiles downloads automatically on first use.
cores defaults to 1 and is never auto-detected: pass cores = parallel::detectCores() to use every core, or any number up to it, to actually get parallel speed.

Install (link the library)shell
# Easiest: let pkg-config supply the paths (the .pc is relocatable,
# so it resolves against wherever you unpacked the archive).
export PKG_CONFIG_PATH=/path/to/stiles/lib/pkgconfig
g++ -O3 -fopenmp app.cpp $(pkg-config --cflags --libs stiles) -o app

# Linux  (libstiles.so)
g++ -O3 -fopenmp app.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -Wl,-rpath,/path/to/stiles/lib -lstiles -o app

# macOS, Apple Silicon  (libstiles.dylib -- self-contained;
# -lomp here is for YOUR app's own OpenMP, not for sTiles)
clang++ -O3 -Xpreprocessor -fopenmp app.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -Wl,-rpath,/path/to/stiles/lib -lstiles -lomp -o app

# Windows, MinGW-w64 UCRT64  (libstiles.dll)
g++ -O3 -fopenmp app.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -lstiles -o app.exe
# then run app.exe with the archive's lib\ on PATH -- the .dll ships
# beside its runtime dependencies (OpenBLAS, gfortran, gomp, ...).
example.cppC++
#include "stiles.h"
#include <vector>
#include <cstdio>

int main() {
    int N = 4, NNZ = 7;                    // 4x4 SPD, one triangle (row >= col)
    std::vector<int>    rows = {0,1,1,2,2,3,3};
    std::vector<int>    cols = {0,0,1,1,2,2,3};
    std::vector<double> vals = {10,-1,10,-1,10,-1,10};

    int  calls[]={1}, cores[]={4}, variant[]={0};
    bool need_inv[]={true};

    void* h = nullptr;
    sTiles_create(&h, 1, calls, cores, variant, need_inv);
    sTiles_assign_graph_one_call(0, 0, &h, N, NNZ, rows.data(), cols.data());
    sTiles_init_group(0, &h);              // symbolic factorization
    sTiles_assign_values(0, 0, &h, vals.data());

    sTiles_bind(0, 0, &h);
    sTiles_chol(0, 0, &h);                 // Cholesky
    sTiles_selinv(0, 0, &h);               // selected inverse
    sTiles_unbind(0, 0, &h);

    printf("logdet = %.6f\n", sTiles_get_logdet(0, 0, &h));
    printf("Var[0] = %.6f\n", sTiles_get_selinv_elm(0, 0, 0, 0, &h));
    sTiles_quit();
}
solve.cppC++
#include "stiles.h"
#include <vector>
#include <cstdio>

int main() {
    int N = 4, NNZ = 7;
    std::vector<int>    rows = {0,1,1,2,2,3,3};
    std::vector<int>    cols = {0,0,1,1,2,2,3};
    std::vector<double> vals = {10,-1,10,-1,10,-1,10};

    int  calls[]={1}, cores[]={4}, variant[]={0};
    bool need_inv[]={false};

    void* h = nullptr;
    sTiles_create(&h, 1, calls, cores, variant, need_inv);
    sTiles_assign_graph_one_call(0, 0, &h, N, NNZ, rows.data(), cols.data());
    sTiles_init_group(0, &h);
    sTiles_assign_values(0, 0, &h, vals.data());

    sTiles_bind(0, 0, &h);
    sTiles_chol(0, 0, &h);
    double b[4] = {1, 2, 3, 4};            // b, overwritten with the solution x
    sTiles_solve_LLT(0, 0, &h, b, 1);      // solve A x = b  (nrhs = 1)
    sTiles_unbind(0, 0, &h);

    printf("x = [%.4f %.4f %.4f %.4f]\n", b[0], b[1], b[2], b[3]);
    sTiles_quit();
}

Download the pre-built library from the download page. Full C API in the API Reference below.

Talks

Presentations

Conference talks and workshop presentations on sTiles.

From Single Core to Many Cores to GPUs

INLA Workshop, University of Glasgow, Scotland (2025)

Tailored for statisticians working with INLA methodology.

sTiles: Accelerated Sparse Factorizations

ISC High Performance 2025, Hamburg, Germany

Technical presentation for HPC audience.

What's Slowing Down Your Statistical Model, and How Tiling Fixes It?

CIRAD, Montpellier, France (Apr. 2026)

Seminar on computational bottlenecks in spatial statistical models and how tiling-based algorithms (sTiles) accelerate them.

Dense Tiling Meets Structured Sparsity: Scalable Algorithms with sTiles

SIAM PP26, Minisymposium (Mar. 4, 2026)

Minisymposium talk on scalable tiling algorithms for structured sparse matrices.

GPU-Accelerated Parallel Selected Inversion for Structured Matrices

SIAM PP26, Contributed Talk (2026)

GPU-accelerated selected inversion using sTiles for structured sparse matrices.

Documentation

API Reference

C API for integrating sTiles into your applications. Click to expand function details.

Object Creation & Initialization

Groups and Calls: the two-level parallelism model

Required reading before calling sTiles_create and sTiles_assign_graph_one_call
+ expand

Use sTiles_create to declare how many matrices you need to factorize and how many threads to use, then sTiles_assign_graph_one_call to hand off each matrix's sparsity pattern, and sTiles_init_group to run the symbolic phase. The two parameters that govern this setup are groups and calls per group: a group owns a sparsity pattern (symbolic factorization runs once per group), and each group holds one or more calls, independent matrices with different values that are factorized in parallel.

sTiles_create( num_groups=2, calls_per_group={2,3}, cores_per_group={8,6} )
Group 0
Sparsity pattern A
symbolic phase → once
Call 0vals₀ · 8 cores
Call 1vals₁ · 8 cores
Group 1
Sparsity pattern B
symbolic phase → once
Call 0vals₀ · 6 cores
Call 1vals₁ · 6 cores
Call 2vals₂ · 6 cores
Group: one sparsity pattern; symbolic phase runs once
Call: one numerical matrix; chol / selinv / solve run per call
Cores: threads per call for tile-parallel computation
sTiles_create Create sTiles solver object
+ expand
int sTiles_create(void** stile, int num_groups, const int* calls_per_group, const int* cores_per_group, const int* factor_type, const bool* get_inverse);

Parameters

stile Output pointer to the created sTiles object. Pass the address of a void* variable.
num_groups Number of groups. Each group owns one sparsity pattern; symbolic factorization runs once per group (ordering, fill analysis, tile layout) and is reused by every call in that group. Use multiple groups when you have matrices with structurally different sparsity patterns that you want to factorize together. Most single-matrix problems use num_groups = 1.
calls_per_group Array of length num_groups. calls_per_group[g] is the number of independent matrices in group g, all sharing group g's sparsity pattern but with different numerical values. Each call gets its own factor storage and is factorized independently via sTiles_chol / sTiles_selinv. Calls within a group run in parallel (launch one OpenMP thread per call). Use calls_per_group[g] = 1 for a single matrix. Use calls_per_group[g] = N_θ to factorize Nθ hyperparameter samples simultaneously (INLA pattern).
cores_per_group Array of length num_groups. cores_per_group[g] is the number of CPU threads allocated to each call in group g for tile-level parallelism inside sTiles_chol / sTiles_selinv. Total threads consumed at peak ≈ calls_per_group[g] × cores_per_group[g] per group. For a single-call setup this is simply the number of cores for the factorization.
factor_type Array of length num_groups. Factorization variant per group (0 = standard Cholesky).
get_inverse Array of length num_groups. Set get_inverse[g] = true to enable selected inversion (sTiles_selinv) for group g. Memory for the inverse is pre-allocated during sTiles_init_group; calling sTiles_selinv on a group where this is false will fail.

Returns: 0 on success, negative error code on failure.

sTiles_assign_graph_one_call Assign sparsity pattern for one call
+ expand
int sTiles_assign_graph_one_call(int group_id, int call_id, void** stile, int N, int NNZ, int* rows, int* cols);

Parameters

group_id, call_id Target group and call indices (0-based)
N Matrix dimension (N x N)
NNZ Number of stored non-zeros in one triangle, diagonal included. Pass a single triangle of the symmetric matrix; the examples use the lower triangle (row ≥ col).
rows, cols COO format row/column indices (0-based). Important: sTiles takes ownership of these pointers directly (no copy). Arrays must remain valid until sTiles_quit() or the call is reassigned.

Returns: 0 on success.

sTiles_init_group Initialize group (symbolic factorization)
+ expand
int sTiles_init_group(int group_id, void** stile);

Performs symbolic factorization, ordering, and memory allocation. Call after assigning graphs.

sTiles_assign_values Assign numerical values to matrix
+ expand
int sTiles_assign_values(int group_id, int call_id, void** stile, double* values);

Values can be updated between solves without re-initializing.

Core Configuration

sTiles_set_tile_size Set tile dimension for blocked operations
+ expand
void sTiles_set_tile_size(int size);

Parameters

size Tile dimension in elements. Typical values: 32, 40, 64, 128.
sTiles_set_tile_type_mode Choose tile storage format
+ expand
void sTiles_set_tile_type_mode(int value);

Parameters

value 0 = dense tiles (standard column-major storage), 1 = semisparse tiles (LAPACK banded storage that reduces memory for low fill-in tiles), 2 = non-uniform tiles, 3 = auto (resolve to 0/1/2 after the symbolic phase). Default is 1 (semisparse), which suits most large sparse matrices.

Execution

sTiles_bind / sTiles_unbind Activate/deactivate persistent thread teams
+ expand
int sTiles_bind(int group_id, int call_id, void** stile);
int sTiles_unbind(int group_id, int call_id, void** stile);

sTiles_bind: Activates a persistent thread team for the specified call. This does not create new threads but activates pre-allocated worker threads for parallel tile operations. Must be called before sTiles_chol, sTiles_selinv, or solve functions.

sTiles_unbind: Deactivates the thread team, releasing workers back to the pool. Always call after finishing computations on a call. Forgetting to unbind may cause resource leaks or deadlocks.

Returns: 0 on success.

sTiles_chol Perform Cholesky factorization (A = LL^T)
+ expand
int sTiles_chol(int group_id, int call_id, void** stile);

Returns 0 on success, non-zero if matrix is not positive definite.

sTiles_selinv Compute selected inverse elements
+ expand
int sTiles_selinv(int group_id, int call_id, void** stile);

Computes A^{-1} elements matching the sparsity pattern. Call sTiles_chol first.

sTiles_solve_LLT Solve Ax = b using Cholesky factorization
+ expand
int sTiles_solve_LLT(int group_id, int call_id, void** stile, double* b, int nrhs);

In-place solve. b is overwritten with solution x. Column-major for multiple RHS.

Query & Cleanup

sTiles_get_logdet Get log-determinant of factored matrix
+ expand
double sTiles_get_logdet(int group_id, int call_id, void** stile);

Returns log|A| = 2 * sum(log(L_ii)). Computed efficiently during factorization.

sTiles_get_selinv_elm Retrieve inverse element A^{-1}[i][j]
+ expand
double sTiles_get_selinv_elm(int group_id, int call_id, int row, int col, void** stile);

Returns A^{-1}[row][col] if within the sparsity pattern (0-based indices).

sTiles_quit Clean shutdown of sTiles
+ expand
void sTiles_quit(void);

Releases all allocated memory and destroys all thread contexts. Call once at program termination.

Setup

Getting Started

For C/C++ projects: download the pre-built library and link against it. (Python and R: see Examples above.)

Quick Start (C/C++)

1

Download

Register and download → the sTiles package for your platform

2

Include Header

#include "stiles.h"

3

Link Library

Link against libstiles.so (Linux), libstiles.dylib (macOS), or libstiles.dll (Windows)

4

Run

Call sTiles API functions from your C/C++ application

Pre-built Binaries

Pre-built binaries are available for Linux (x86_64 and arm64), macOS (Apple Silicon), and Windows (x86_64). The x86_64 build runs on any x86-64 CPU (AVX2 baseline with runtime AVX-512 acceleration). Register to see the full platform list and download links.

Register & Download →

Free for research and academic use. A short form helps us understand our user community.

Compilation Example Shell
# Linux (x86_64 or arm64)
# BLAS/LAPACK are embedded inside libstiles, so -lstiles is all you need.
g++ -O3 -fopenmp myapp.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -Wl,-rpath,/path/to/stiles/lib -lstiles -o myapp

# macOS (Apple Silicon)
# The dylib is self-contained; libomp provides the OpenMP runtime.
clang++ -O3 -Xpreprocessor -fopenmp myapp.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -Wl,-rpath,/path/to/stiles/lib -lstiles -lomp -o myapp

# Windows (x86_64): build in the MinGW-w64 UCRT64 shell (MSYS2 / Rtools44), not MSVC.
# Note: link uses the libstiles.dll.a import lib, and there is no -rpath on Windows.
g++ -O3 -fopenmp myapp.cpp \
  -I/path/to/stiles/include \
  -L/path/to/stiles/lib -lstiles -o myapp.exe
# libstiles.dll links its BLAS and runtime dynamically, but the archive now
# SHIPS them beside it (libopenblas, libgfortran-5, libquadmath-0, libgomp-1,
# libgcc_s_seh-1, libstdc++-6, libwinpthread-1, libhwloc-15, libltdl-7).
# Keep that lib\ directory on PATH, or copy its contents beside myapp.exe.
Reference

How to Cite

If you use sTiles in your research, please cite the appropriate paper(s) below.

📖

General Use of sTiles

@inproceedings{fattah2025stiles,
  title     = {{sTiles}: An Accelerated Computational
               Framework for Sparse Factorizations
               of Structured Matrices},
  author    = {Fattah, Esmail Abdul and Ltaief, Hatem
               and Rue, H{\aa}vard and Keyes, David},
  booktitle = {ISC High Performance 2025 Research Paper
               Proceedings (40th International Conference)},
  pages     = {1--14},
  year      = {2025},
  organization = {Prometeus GmbH}
}
📖

Selected Inverse Functionality

@article{fattah2025gpu,
  title   = {{GPU}-Accelerated Parallel Selected
             Inversion for Structured Matrices
             Using {sTiles}},
  author  = {Fattah, Esmail Abdul and Ltaief, Hatem
             and Rue, H{\aa}vard and Keyes, David},
  journal = {arXiv preprint arXiv:2504.19171},
  year    = {2025}
}
Connect

Get In Touch

Esmail Abdul Fattah

Esmail Abdul Fattah

Developer & Maintainer

King Abdullah University of Science and Technology (KAUST)
Thuwal, Saudi Arabia

✉ Email List

Join the sTiles mailing list to receive updates about releases, new features, and research developments.

Subscribe →

💻 Contribute

Interested in contributing to sTiles? We're looking for help with Python wrappers and additional language bindings.

Get Involved →

🧪 Test Matrices

Have matrices that perform poorly with current solvers? Share them with us to help improve sTiles robustness.

Send Matrices →

Acknowledgments

sTiles builds upon excellent open-source libraries and research. We gratefully acknowledge the following projects and their contributors.

  • SCOTCH:Graph partitioning and sparse matrix ordering library. Used for nested dissection ordering.
    labri.fr/perso/pelegrin/scotch
    Pellegrini, F. & Roman, J. (1996). "SCOTCH: A Software Package for Static Mapping by Dual Recursive Bipartitioning of Process and Architecture Graphs."
  • SuiteSparse:Suite of sparse matrix algorithms including AMD, COLAMD, and CHOLMOD ordering methods.
    people.engr.tamu.edu/davis/suitesparse.html
    Davis, T. A. (2006). "Direct Methods for Sparse Linear Systems." SIAM.
  • METIS:Graph partitioning library for fill-reducing orderings and nested dissection.
    github.com/KarypisLab/METIS
    Karypis, G. & Kumar, V. (1998). "A Fast and High Quality Multilevel Scheme for Partitioning Irregular Graphs." SIAM J. Sci. Comput.
  • BLAS/LAPACK:Foundational linear algebra libraries for dense matrix operations within tiles.
    netlib.org/lapack
    Anderson, E. et al. (1999). "LAPACK Users' Guide." SIAM.
  • OpenMP:Shared-memory parallel programming API for multi-threaded execution.
    openmp.org
    OpenMP Architecture Review Board. "OpenMP Application Programming Interface."
  • RCM Algorithm:Reverse Cuthill-McKee algorithm for bandwidth reduction in sparse matrices.
    Cuthill, E. & McKee, J. (1969). "Reducing the bandwidth of sparse symmetric matrices." ACM '69.