Skip to content

Version 0.3

Tunan Wang edited this page Nov 26, 2025 · 6 revisions

Version 0.2 established the main structure of stochastic linear algebra methods. Version 0.3 focuses on enhancing the library's stability and performance.

❗ Breaking Change: This version introduces a new API paradigm for configuration. We have transitioned from direct field access to explicit getter and setter methods. This allows users to manipulate configurations more safely, straightforwardly, and robustly.

Usage case

If you previously modified any ingredient settings by accessing fields directly, you must update your code to use the getter and setter methods.

${\color{red}\text{Version 0.2 (Not recommended):}}$

s = SparseSign()
# Direct access
val = s.nnz       # Get the number of non-zeros
s.nnz = 5         # Set the number of non-zeros to five

${\color{green}\text{Version 0.3 (Recommended):}}$

s = SparseSign()
# Use getters to read values
val = get_nnz(s)  # Get the number of non-zeros
# Use setters to modify values
set_nnz!(s, 5)    # Set the number of non-zeros to five

Motivation

In Version 0.2, users needed to access mutable structures' fields directly to configure ingredients after instantiation. This approach is brittle: it exposes internal implementation details, meaning that any future refactoring of the structs could break user code. For example, imagine a hypothetical scenario in Version 0.4 where we decide to store sparsity density instead of the explicit count of non-zeros (nnz) in SparseSign for algorithmic reasons. The struct would change from this:

# Old version
mutable struct SparseSign <: Compressor
    # ...
    nnz::Int64        # Number of non-zeros.
    # ...
end

To this:

# Hypothetical new version
mutable struct SparseSign <: Compressor
    # ...
    density::Float64  # Density of number of non-zeros.
    # ...
end

If users rely on direct field access, the following code would crash in Version 0.4:

# User code:
s = SparseSign()
x = s.nnz  # ERROR: Field `nnz` no longer exists!
s.nnz = 5  # ERROR: Field `nnz` no longer exists!

To prevent this, following the Julia Style Guide, we introduce getter and setter methods in Version 0.3. This abstraction layer keeps the user interface robust. Even if the internal fields change in the future, we can adapt the implementation without breaking user code. For example, we can adapt the implementation in Version 0.4 with:

# Hypothetical internal implementation
get_nnz(s::SparseSign) = round(Int, s.density * s.compression_dim)

function set_nnz!(s::SparseSign, k::Int64)
    s.density = k / s.compression_dim
    return s
end

By adopting this pattern now, user code remains stable regardless of how the internal storage evolves.

Implementation

We have exposed getter and setter helper functions for every ingredient. Below is the implementation for SparseSign as an example. Note that setter now includes validation logic to ensure the object remains in a valid state.

# Getters
get_compression_dim(s::SparseSign) = s.compression_dim
get_nnz(s::SparseSign) = s.nnz
get_cardinality(s::SparseSign) = s.cardinality
get_type(s::SparseSign) = s.type

# Setters 
"""
    set_compression_dim!(s::SparseSign, new_dim::Int64)

Updates the compression dimension. 
Throws an ArgumentError if `new_dim` is invalid or smaller than the current `nnz`.
"""
function set_compression_dim!(s::SparseSign, new_dim::Int64)
    if new_dim <= 0
        throw(ArgumentError("New `compression_dim` must be positive."))
    end
    
    if s.nnz > new_dim
        throw(ArgumentError("New `compression_dim`, $new_dim, must be greater than or equal to current `nnz`, $(s.nnz)."))
    end
    
    s.compression_dim = new_dim
    return s
end

"""
    set_nnz!(s::SparseSign, new_nnz::Int64)

Updates the number of non-zeros.
Throws an ArgumentError if `new_nnz` is invalid or larger than the current `compression_dim`.
"""
function set_nnz!(s::SparseSign, new_nnz::Int64)
    if new_nnz <= 0
        throw(ArgumentError("New `nnz` must be positive."))
    end
    
    if new_nnz > s.compression_dim
        throw(ArgumentError("New `nnz`, $new_nnz, must be less than or equal to current `compression_dim`, $(s.compression_dim)."))
    end
    
    s.nnz = new_nnz
    return s
end

"""
    set_cardinality!(s::SparseSign, new_cardinality::Cardinality)

Updates the cardinality.
"""
function set_cardinality!(s::SparseSign, new_cardinality::Cardinality)
    s.cardinality = new_cardinality
    return s
end

"""
    set_type!(s::SparseSign, new_type::Type{<:Number})

Updates the element type.
"""
function set_type!(s::SparseSign, new_type::Type{<:Number})
    s.type = new_type
    return s
end

Clone this wiki locally