This document is intended for developers who wish to extend MatGen with new scaling algorithms, execution backends, or simply understand the internal architecture better.
MatGen follows a strict Dispatch-Implementation pattern to separate the public API from backend-specific logic.
matgen/
├── include/matgen/
│ ├── algorithms/ # Public API for algorithms (e.g., scaling.h)
│ └── core/
│ └── execution/ # Execution policies (policy.h)
└── src/
├── algorithms/ # Dispatch logic (decides which backend to call)
└── backends/ # Concrete implementations
├── seq/ # Sequential code
├── omp/ # OpenMP code
├── cuda/ # CUDA kernels
└── mpi/ # MPI logic
To add a new algorithm (e.g., Bicubic Interpolation), follow these steps:
Add the function prototype to include/matgen/algorithms/scaling.h. Use matgen_exec_policy_t to allow backend selection.
// include/matgen/algorithms/scaling.h
matgen_error_t matgen_scale_bicubic_with_policy(
matgen_exec_policy_t policy,
const matgen_csr_matrix_t* source,
matgen_index_t new_rows,
matgen_index_t new_cols,
matgen_csr_matrix_t** result);Create a dispatch file in src/algorithms/scaling/. This function acts as a router.
// src/algorithms/scaling/bicubic_dispatch.c
#include "matgen/algorithms/scaling.h"
#include "matgen/core/execution/dispatch.h"
matgen_error_t matgen_scale_bicubic_with_policy(...) {
// 1. Resolve Policy (Auto -> Specific)
matgen_exec_policy_t resolved = matgen_exec_resolve(policy);
// 2. Create Context
matgen_dispatch_context_t ctx = matgen_dispatch_create(resolved);
// 3. Dispatch Macro
MATGEN_DISPATCH_BEGIN(ctx, "bicubic_scale") {
MATGEN_DISPATCH_CASE_SEQ:
return matgen_scale_bicubic_seq(...); // You must implement this!
#ifdef MATGEN_HAS_OPENMP
MATGEN_DISPATCH_CASE_PAR:
return matgen_scale_bicubic_omp(...);
#endif
// ... other backends
MATGEN_DISPATCH_DEFAULT:
return MATGEN_ERROR_UNSUPPORTED;
}
MATGEN_DISPATCH_END();
}Always implement the sequential version first in src/backends/seq/algorithms/scaling/. This serves as the reference implementation and fallback. Also, you must define a header for backend functions (e.g. matgen_scale_bicubic_{backend}) in src/backends/{backend}/internal/bicubic_{backend}.h for convenient include in dispatch.
Add the new files to the corresponding CMakeLists.txt file's target sources for the linker to successfully link your algorithms to the program:
- Add dispatch file to
src/algorithms/scaling/CMakeLists.txt - Add back-end sequential version of your algorithm to
src/backends/seq/algorithms/scaling/CMakeLists.txt - Add back-end parallel version of your algorithm to
src/backends/{backend}/algorithms/scaling/CMakeLists.txtwhere backend is the parallelization framework (OPENMP/MPI/CUDA)
testbed/scale_cli.c is a basic CLI program that has options to test backends and algorithms in runtime. You must add your public definition of algorithm (include/algorithms/scaling.h -> matgen_scale_bicubic_with_policy) in CLI to run and test.
THIS STEP IS TOTALLY OPTIONAL BECAUSE OF YOUR INTERNAL CODE STAYS INTERNAL AND YOU CAN JUST CREATE CONVERSION FUNCTION TO FINAL FORM TO WRITE MATRIX MARKET FILE.
Currently, MatGen relies on CSR for processing and COO for I/O. Adding a new format requires a few steps.
Create a new header in include/matgen/core/matrix/.
// include/matgen/core/matrix/ellpack.h
typedef struct {
matgen_index_t rows;
matgen_index_t cols;
matgen_index_t max_row_nnz;
matgen_index_t* col_indices; // Dimensions: rows * max_row_nnz
matgen_value_t* values; // Dimensions: rows * max_row_nnz
} matgen_ell_matrix_t;You have two options to make this usable:
- Option A: The Adapter Path (Recommended)
- Implement conversion functions to/from CSR.
- Benefit: You can immediately use all existing scaling algorithms (Nearest Neighbor, Bilinear) by converting, scaling, and converting back.
- Drawback: Conversion overhead.
- Option B: Native Implementation
- Re-implement the scaling algorithms specifically for your format.
- Benefit: Maximum performance.
- Drawback: Significant development effort.
If you choose Option A, implement:
matgen_error_t matgen_ell_from_csr(const matgen_csr_matrix_t* src, matgen_ell_matrix_t** dst);
matgen_error_t matgen_ell_to_csr(const matgen_ell_matrix_t* src, matgen_csr_matrix_t** dst);To add high-performance support for your new matrix type (e.g., fast creation or conversion):
Implement the conversion logic using OpenMP parallel loops.
- Location:
src/backends/omp/core/matrix/ - Strategy: Parallelize the loop over rows.
#pragma omp parallel for
for (matgen_index_t i = 0; i < rows; ++i) {
// Process row i independenty
}- Define a Device Struct: If the matrix lives on GPU, the pointers in your struct must point to device memory.
- Location:
src/backends/cuda/core/matrix/ - Implementation:
- Allocate memory using
cudaMalloc. - Write a
__global__kernel to perform the conversion/construction. - One thread per row (or one thread per element) strategy usually works best.
- Allocate memory using
// src/backends/cuda/core/matrix/ell_cuda.cu
__global__ void csr_to_ell_kernel(...) {
matgen_index_t row = blockIdx.x * blockDim.x + threadIdx.x;
if (row < rows) {
// ...
}
}MatGen is designed to handle massive matrices, often exceeding the limits of standard 32-bit integers.
- What is it? Typically
uint64_t(unsigned long long). - When to use it? For anything related to rows, columns, or indices in the matrix.
- Why? Matrix dimensions can easily exceed 4 billion (2^32). Using
intwill cause overflows and segfaults on large datasets.
- What is it? Typically
size_toruint64_t. - When to use it? For the number of non-zeros (
nnz), array lengths, or loop counters iterating over data arrays. - Why?
nnzis often much larger than rows/cols.
- What is it? Typically
double(f64) orfloat(f32). - When to use it? For the actual data values inside the matrix.
- Tip: Avoid hardcoding
floatordouble. Usematgen_value_tso precision can be switched project-wide viatypes.h.
- Always return
matgen_error_t. - Never use
exit()inside the library. - Propagate errors up the stack. If a backend fails (e.g., CUDA out of memory), the dispatch logic (if smart enough) could theoretically fallback, but currently, it returns the error.