container/adapters tutorial
This tutorial shows how to pick the right ready-made dictionary for each of
the repository’s types and use it with the container algorithms, including
the live views of @mutable.Matrix. If you want to write dictionaries for a
type of your own, read the container tutorial instead.
Quick start
moon add Luna-Flow/linear-algebra@0.5.0
///|
import {
"Luna-Flow/linear-algebra/container",
"Luna-Flow/linear-algebra/container/adapters" @container_adapters,
"Luna-Flow/linear-algebra/immut",
"Luna-Flow/linear-algebra/mutable",
}
The factory name tells you the type and the capability:
///|
test "read one entry through an adapter" {
let v = @immut.Vector::from_array([10, 20, 30])
let ops = @container_adapters.immutable_vector_read_ops()
inspect((ops.length)(v), content="3")
inspect((ops.get)(v, 2).unwrap(), content="30")
}
Everyday tasks
Copy a row out of a matrix
A row view is a vector source, so any vector target can receive it:
///|
test "copy a row view into an immutable vector" {
let m = @mutable.Matrix::from_2d_array([[1, 2, 3], [4, 5, 6]])
let row : @immut.Vector[Int] = @container.vector_convert(
m.row_view(1),
@container_adapters.mutable_row_view_read_ops(),
@container_adapters.immutable_vector_build_ops(),
).unwrap()
m.set(1, 0, 40)
inspect(row, content="|4, 5, 6|")
}
Materialize a transpose view
The transpose view of @mutable.Matrix is a matrix source, so
matrix_convert materializes it in any representation:
///|
test "materialize a transpose view" {
let m = @mutable.Matrix::from_2d_array([[1, 2, 3], [4, 5, 6]])
let t : @immut.Matrix[Int] = @container.matrix_convert(
m.to_transpose(),
@container_adapters.mutable_transpose_read_ops(),
@container_adapters.immutable_matrix_build_ops(),
).unwrap()
inspect(t, content="|1, 4|\n|2, 5|\n|3, 6|")
}
Write through a column view
///|
test "clear a column through its view" {
let m = @mutable.Matrix::from_2d_array([[1, 2], [3, 4], [5, 6]])
let col = m.col_view(0)
let edit = @container_adapters.mutable_col_view_mutable_edit_ops()
for i in 0..<col.length() {
(edit.set)(col, i, 0).unwrap()
}
inspect(m, content="|0, 2|\n|0, 4|\n|0, 6|")
}
Move between the default backend wrappers
///|
test "mutable dense wrapper to immutable dense wrapper" {
let source = @default.DenseVector::from_array([1.5, 2.5])
let target : @default.ImmutableDenseVector[Double] = @container.vector_convert(
source,
@container_adapters.dense_vector_read_ops(),
@container_adapters.immutable_dense_vector_build_ops(),
).unwrap()
inspect(target[1], content="2.5")
}
Going further
Choosing the edit form. Types with value semantics (@immut, the
Immutable* wrappers) only have *_persistent_edit_ops; types with in-place
semantics (@mutable, views, DenseVector, DenseMatrix) only have
*_mutable_edit_ops. Generic code that edits should take the record that
matches the ownership model it expects.
Views as targets. Views have no build dictionary. To fill a row of an
existing matrix from another vector, read the source and write through
mutable_row_view_mutable_edit_ops in a loop.
Common pitfalls
- Expecting a conversion to stay linked. Converting a view copies; later writes to the matrix do not reach the copy, as the row example shows.
- Using
immutable_*for the default wrappers.immutable_matrix_*adapts@immut.Matrix; the wrapper@default.ImmutableDenseMatrixhas its ownimmutable_dense_matrix_*factories.
Next steps
- adapters API for the full list.
- container tutorial for writing your own dictionaries.
- adapters design for the dependency structure.