v0.0.24¶
New: SymmetricMatrix¶
lythonic.symmetric.SymmetricMatrix is a square, string-keyed matrix whose two
axes are the same Universe and whose value depends on an unordered pair of
keys: a correlation or covariance matrix, a distance matrix, a similarity or
adjacency matrix. ExposureMatrix could not express these, since its two axes
are different universes and nothing kept the two halves in agreement.
Symmetry is structural, not validated. Only a lower triangle is stored, so an asymmetric value has nowhere to go — there is no symmetry check, no tolerance for how symmetric is symmetric enough, and no rule for which half wins.
Storage adapts to density: a near-full matrix is held as a dense triangle, a mostly-empty one as a dense diagonal plus sparse off-diagonal records. The variant is chosen by a fixed rule the caller cannot select or observe, so two matrices with the same content always encode identically and byte equality keeps tracking semantic equality.
The type is generic rather than correlation- or covariance-specific, and makes
no positive semi-definiteness promise: that is a query on the numpy facade
(is_psd, min_eigenvalue, eigenvalues), so construction and deserialization
need no numpy and do no eigendecomposition. Every key needs an explicit diagonal
value; build() raises naming any key that has none.
SymmetricMatrixBuilder is the only mutable object, following the split
ExposureMatrix established. There is deliberately no row-wise write — under
symmetry one key's row is part of every other key's row.
New: KeyedVector¶
lythonic.vector.KeyedVector is a Universe plus one value per key, in
universe order. It fills the gap between the dict accessors, which keep the keys
but drop the axis, and the array accessors, which keep the alignment but drop
the keys and require numpy.
Unlike the matrix types, it stores NaN and the infinities — it is a far more
general object, and computed results legitimately contain them. Two consequences
follow: non-finite values serialize as "NaN", "Infinity" and "-Infinity"
strings, which is valid JSON everywhere and round-trips losslessly; and equality
is defined on the type rather than inherited, comparing values positionally with
NaN matching NaN, so a vector loaded from JSON equals the one that was saved.
cast aligns a vector to another universe, dropping keys outside it and filling
introduced keys with NaN — the one fill value that cannot pass for a
measurement, since any arithmetic touching it yields NaN. Conversions sit behind
np and pd facades; a pandas Series is an exact structural match, and
from_series validates the index is unique and all strings.
The type is purely additive. ExposureMatrix's dict-returning accessors are
unchanged, and KeyedVector.from_mapping bridges from them in one lossless
call.
Documentation¶
- Three how-to guides for the keyed data types: choosing between
FrameData,ExposureMatrix,SymmetricMatrixandKeyedVector; the class-in / instance-out facade rule and per-library conversions; and composing the four, including building a covariance matrix from a correlation matrix and a volatility vector. - Reference pages for
lythonic.symmetricandlythonic.vector. CONTEXT.mdgains Symmetric matrix, Pair, Diagonal, Keyed vector, Entry and Aligned; Universe notes that a matrix may name both axes with one universe.- The
ExposureMatrixdesign sketch underdocs/ideas/is removed now thatlythonic.exposurecarries the design; its one unresolved question — thatfrom_matrixcannot detect a transposed square array — moved todocs/open-questions.md, where it now covers both matrix types.
Decisions and tickets¶
New ADRs:
- 0003 Symmetric matrix storage is a canonical two-variant union — two lower-triangle encodings chosen by the exact byte-count crossover, not caller-selectable, so byte equality keeps tracking semantic equality.
- 0004 Positive semi-definiteness is a query, not an invariant — checking it at construction would make numpy mandatory, put a cubic-time computation on every load, and be wrong for the many symmetric matrices that legitimately are not PSD.
- 0005 KeyedVector admits non-finite
values — NaN and the
infinities are stored, against the rule the matrix types follow, which forces
the
"NaN"wire spelling and a custom equality.
Closed tickets: