-
Notifications
You must be signed in to change notification settings - Fork 2
Version 0.3
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.
If you previously modified any ingredient settings by accessing fields directly, you must update your code to use the getter and setter methods.
s = SparseSign()
# Direct access
val = s.nnz # Get the number of non-zeros
s.nnz = 5 # Set the number of non-zeros to fives = 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 fiveIn 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.
# ...
endTo this:
# Hypothetical new version
mutable struct SparseSign <: Compressor
# ...
density::Float64 # Density of number of non-zeros.
# ...
endIf 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
endBy adopting this pattern now, user code remains stable regardless of how the internal storage evolves.
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