# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).


## [Unreleased]
[Unreleased]: https://github.com/althonos/lightmotif/compare/v0.10.1...HEAD


## [v0.10.1] - 2026-03-25
[v0.10.1]: https://github.com/althonos/lightmotif/compare/v0.10.0...v0.10.1

### Added

#### `lightmotif`
- Lossy constructor for `EncodedSequence` replacing unknown characters with `Alphabet::default_symbol`.
- `block_size` option to change the number of rows of the block used in a `Scanner`.

### Fixed

#### `lightmotif`
- `ScoringMatrix::to_discrete` failing conversion on cells with `-inf` scores (due to zero pseudocounts).
- Panic in `Scanner::next` caused by empty scores buffer.
- Non-saturating add being used in `score_u8_neon` causing `Scanner` to ignore some hits on Arm platforms.

### Changed

#### `lightmotif`
- Update `rand` dependency to `v0.10.0`.

##### `lightmotif-py`
- Update `pyo3` dependency to `v0.28.0.`.


## [v0.10.0] - 2025-05-19
[v0.10.0]: https://github.com/althonos/lightmotif/compare/v0.9.1...v0.10.0

### Added

#### `lightmotif`
- Experimental Gibbs sampler implementation for motif detection in `zoops` and `oops` modes.
- `IndexMut<MatrixCoordinates>` trait implementation for `DenseMatrix` type.
- `AsRef<Vec<T>>` for `Scores<T>`.
- `information_content` methods to `WeightMatrix` and `ScoringMatrix`.
- `consensus` method to `CountMatrix`.
- `FrequencyMatrix::into_scoring` to take ownership of a frequency matrix and converting it inplace into a `ScoringMatrix`.
- Implement `size_hint` for `StripedScores` iterator.
- Cumulative distribution approximation for `ScoringMatrix`.
- Add `ScoringMatrix::to_score_distribution` method to compute a score distribution suitable for P-value estimation.
- Trait to compute correlations for `lightmotif::pwm` matrices.
- `new_unchecked` constructors to `lightmotif::pwm` types (undocumented).
- Efficient look-up table implementation for Aarch64 NEON `f32`-scoring.

#### `lightmotif-io`
- Reader for MEME format files.

### Changed

#### `lightmotif`
- Implement `DenseMatrix` iterators without unsafe code.
- Make `lightmotif::scan::Hit` fields read-only.
- Inline short functions in `lightmotif` core moduels.
- Make the `ravel` methods of `DenseMatrix` unsafe.
- Allow accessing and configuring the row capacity of a `DenseMatrix`.
- Always pre-allocate additional rows when creating a new `StripedSequence` to avoid reallocation for wrap rows.
- Use pointers to avoid matrix indexing inside pipeline hot loops.
- Skip use of NEON for `Score<Protein>` since generic code is faster.

#### `lightmotif-io`
- Update `nom` dependency to `8.0`.

#### `lightmotif-py`
- Update `pyo3` dependency to `0.23`.
- Use `maturin` instead of `setuptools-rust` to build Python bindings from source.

### Fixed

#### `lightmotif`
- Use all columns of the matrix in `CountMatrix::entropy`.
- Fix missing `_mm_sfence` in `score_f32_avx2_permute` code.
- Avoid multiple `log` calls in `FrequencyMatrix::to_scoring`.

### Removed

#### `lightmotif`
- Const method declaration for matrix traits not `const` in `lightmotif::pwm`.


## [v0.9.1] - 2024-09-03
[v0.9.1]: https://github.com/althonos/lightmotif/compare/v0.9.0...v0.9.1

### Added

#### `lightmotif-py`
- Arch User Repository package `python-lightmotif` to distribute the Python package on ArchLinux.

### Fixed

#### `lightmotif-py`
- Fix issues with source distribution missing required files for compilation.
- Remove unused test data to reduce size of source distribution.


## [v0.9.0] - 2024-09-02
[v0.9.0]: https://github.com/althonos/lightmotif/compare/v0.8.0...v0.9.0

### Added

#### `lightmotif`
- `DenseMatrix::ravel` to get the array of a `DenseMatrix` as a single continuous slice.

#### `lightmotif-py`
- Support for protein alphabets ([#5](https://github.com/althonos/lightmotif/issues/5)).
- `protein` property to `CountMatrix`, `WeightMatrix`, `ScoringMatrix`, `EncodedSequence` and `StripedSequence`.
- `Scanner` class to use the new DNA scanning algorithm.
- Buffer protocol implmementation for `ScoringMatrix` objects.
- `lightmotif.load` function to load one or more `Motif` from a file-like object ([#6](https://github.com/althonos/lightmotif/issues/6)).
- `Motif.name` property to store the optional name of a `Motif`.
- Several `Motif` subclasses to store motif and metadata loaded from a file.
- Type annotations for the `lightmotif.lib` package.

### Fixed

#### `lightmotif-io`
- Parsing of floating-point numbers in JASPAR parser.
- Panic on error unwrapping in `lightmotif-io`.

### Changed

#### `lightmotif`
- Rewrite `DenseMatrix` with constant platform-specific alignment to reduce amount of `unsafe` code.

### Removed

#### `lightmotif-py`
- `alphabet` parameter of `ScoringMatrix` and `EncodedSequence`.


## [v0.8.0] - 2024-06-28
[v0.8.0]: https://github.com/althonos/lightmotif/compare/v0.7.3...v0.8.0

### Added

#### `lightmotif`
- `lightmotif::pwm::DiscreteMatrix` to store a discretized `ScoringMatrix`.
- `MatrixElement` marker trait for `Default + Copy` types that can be used as a `DenseMatrix` element.
- `lightmotif::scores::Scores` wrapper of `Vec<T>` with shortcut `max`, `threshold` and `argmax` methods.
- AVX2 and NEON implementations of `Score<u8, Dna>` pipeline operation.

### Fixed

#### `lightmotif`
- Remove requirement for `T: Debug` in `Clone` implementation of `DenseMatrix<T>`.
- SSE2 and AVX2 implementations of `Argmax` not returning the last index in case of ties.

#### `lightmotif-tfmpvalue`
- Cache internal buffer for Q-values to avoid reallocation between iterations.

### Changed

#### `lightmotif`
- Cache the `Pipeline` used in `lightmotif::scan::Scanner`.
- Make `lightmotif::scores::StripedScores` generic over the score type.
- Make `Maximum` and `Threshold` generic over the score type.
- Use a discrete matrix in `Scanner` to quickly eliminate blocks without candidate position above given threshold.
- Compile crate without the `rand` and `rand-distr` features by default.

#### `lightmotif-py`
- Update `pyo3` dependency to `v0.22.0`.


## [v0.7.3] - 2024-06-17
[v0.7.3]: https://github.com/althonos/lightmotif/compare/v0.7.2...v0.7.3

### Added

#### `lightmotif-io`
- `lightmotif_io::jaspar` to read matrices in raw JASPAR format.
- Implementation of `Clone` and `Error` for the `lightmotif_io::error::Error` type.

### Changed

#### `lightmotif`
- Use logarithmic subtraction in `FrequencyMatrix::to_scoring`.
- Use `_mm256_permutevara8x32_ps` instead of `_mm256_permutevar_ps` in AVX2 implementation to avoid special handling of unknown matric character `N`.

### Fixed

#### `lightmotif`
- Striping of empty sequences panicking on AVX2 implementation.
- Scoring of empty range of rows panicking.
- Invalid alignment of data in AVX2 code.
- `Clone` implementation of `DenseMatrix` not preserving memory alignment.


## [v0.7.2] - 2024-06-14
[v0.7.2]: https://github.com/althonos/lightmotif/compare/v0.7.1...v0.7.2

### Added

#### `lightmotif-py`
- Add `__len__` implementation for all matrix clases.
- Allow changing the logarithm base in `WeightMatrix.log_odds` method.
- Implement `__getitem__` for row access for all matrix classes.


## [v0.7.1] - 2024-06-14
[v0.7.1]: https://github.com/althonos/lightmotif/compare/v0.7.0...v0.7.1

### Fixed

#### `lightmotif`
- Resizing of `StripedScores` output in NEON implementation of `Score`.


## [v0.7.0] - 2024-06-14
[v0.7.0]: https://github.com/althonos/lightmotif/compare/v0.6.0...v0.7.0

### Added

#### `lightmotif`
- Implement indexing of `StripedSequence` by sequence index.
- `matrix` accessor to all matrix types in `lightmotif::pwm` and `lightmotif::seq`.
- `entropy` and `information_content` methods to `CountMatrix`.
- `SymbolCount` trait for counting the number of occurrences of a symbol in an iterable.
- Several `Background` constructors for counting occurences in one or more sequences.
- `FromIterator<A::Symbol>` constructor for `EncodedSequence<A>`.
- `MultipleOf<N>` trait to simplify typenums in platform code signatures.
- Sampling of random sequences using the `rand` dependency under a feature flag.
- `ScoringMatrix.score_into` method to re-use a `StripedScores` buffer.
- `ScoringMatrix.score_position` method to score a single sequence position.
- Indexing by `MatrixCoordinates` in `DenseMatrix`.
- Support for chanding logarithm base when building a `ScoringMatrix` from a `WeightMatrix`.
- Scanning algorithm for finding hits in a sequence with an iterator without allocating `StripedScores` for each sequence position.

### `lightmotif-py`
- Support for optional TFMPvalue interface in Python bindings under GPLv3+ code.
- Constructor for `ScoringMatrix` class.
- `ScoringMatrix.reverse_complement` to compute the reverse-complement of a scoring matrix.

### Changed

#### `lightmotif`
- Make `EncodedSequence.stripe` use a dispatching `Pipeline` internally.
- Require power-of-two alignment in `DenseMatrix` implementations.
- Update `generic-array` dependency to `v1.0`.
- Change order of parameters in `ScoringMatrix.score`.
- Reorganize scoring trait and implement row-slice scoring for AVX2 and SSE2.
- Rewrite `Pipeline::threshold` to return matrix coordinates instead of a sequence index.
- Rewrite `Pipeline::argmax` to return matrix coordinates instead of a sequence index.

#### `lightmotif-py`
- Streamline the use of pipelined functions in Python bindings.

### Fixed

#### `lightmotif`
- Handling of unknown residues in `permute` implementation of `Score` on AVX2.
- `PartialEq` for `DenseMatrix` to ignore alignment padding in each row. 

### Removed

#### `lightmotif`
- Platform-specific code for thresholding a `StripedScores` matrix.
- Direct attribute access in `StripedSequence`.

#### `lightmotif-transfac`
- Remove crate from repository, superseded by the `lightmotif-io` crate.


## [v0.6.0] - 2023-12-13
[v0.6.0]: https://github.com/althonos/lightmotif/compare/v0.5.1...v0.6.0

### Added

#### `lightmotif`
- Validating constructor `::pwm::FrequencyMatrix::new` testing for frequencies on each ranks.
- Getter to the raw data matrix of a `::pwm::FrequencyMatrix`.

#### `lightmotif-io`
New crate with JASPAR, TRANSFAC and UNIPROBE format parsers.

### Changed

#### `lightmotif`
- Make `max_score` and `min_score` columns of `::pwm::ScoringMatrix` ignore the wildcard symbol column.

#### `lightmotif-tfmpvalue`
- Invert decay in `TfmPvalue` to reduce rounding errors when computing granularity.
- Use a fast integer hashing algorithm for `i64` keys in maps used for recording *Q*-values.
- Compute the optimal column permutation to accelerate the computation of score distributions.

### Removed

#### `lightmotif-transfac`
Deprecate crate in favour of `lightmotif-io`.


## [v0.5.1] - 2023-08-31
[v0.5.1]: https://github.com/althonos/lightmotif/compare/v0.5.0...v0.5.1

### Fixed

#### `lightmotif`
- Compilation for Arm NEON platforms.


## [v0.5.0] - 2023-08-31
[v0.5.0]: https://github.com/althonos/lightmotif/compare/v0.4.0...v0.5.0

### Added

#### `lightmotif`
- Arm NEON implementation of `Threshold` and `Encode`.
- `Alphabet::as_str` method to get the symbols of an alphabet as a string.
- `DenseMatrix::fill` method to fill a dense matrix with a constant value.
- Convenience getters and conversion traits to `StripedSequence`.
- `Stripe` trait to implement striping of an encoded sequence with SIMD.
- Dynamic dispatched pipeline selecting the best implementation as runtime.
- `PartialEq` implementation for `EncodedSequence`.

#### `lightmotif-py`
- Buffer and copy protocols for `StripedSequence` and `EncodedSequence`.
- Indexing support to `EncodedSequence`.

#### `lightmotif-tfmpvalue`
- Convenience methods to access the wrapped `ScoringMatrix` reference in `TfmPvalue`.

### Changed

#### `lightmotif`
- `Encode::encode` now returns an `EncodedSequence` instead of a raw `Vec`.
- Performance improvements for `Encode` for AVX2 and NEON by removing non-const function calls in loop.
- Performance improvements for `Threshold` by skipping the index buffer initialization.
- Avoid buffer initialization when allocating a new buffer in `EncodedSequence::encode`.
- Require `Symbol` implementors to implement `Eq`.

#### `lightmotif-py`
- Use the dynamic dispatch pipeline to run vectorized operations.

#### `lightmotif-tfmpvalue`
- Make `TfmPvalue` generic over the type of reference to the wrapped `ScoringMatrix`.

### Fixed

#### `lightmotif`
- `Debug` implementation of `DenseMatrix` crashing when attempting to render the padding bytes.


## [v0.4.0] - 2023-08-10
[v0.4.0]: https://github.com/althonos/lightmotif/compare/v0.3.0...v0.4.0

### Changed

#### `lightmotif`
- Improve `DenseMatrix::resize` performance when downsizing.
- Explicitly panic when sequence is too long to be processed with `u32` indices in AVX2 and SSE2.
- Reorganize `DenseMatrix` column alignment and index storage.
- Improve `Score` performance by using pointers instead of slices in SIMD loops.
- Rename `DenseMatrix::columns_effective` to `DenseMatrix::stride`.
- Use streaming intrinsics to store data in AVX2 and SSE2 implementations.
- Rename `BestPosition` trait to `Maximum`.

#### `lightmotif-py`
- Avoid error on missing symbols in `CountMatrix.__init__`

### Added

#### `lightmotif`
- `Threshold` trait to find all position above a certain score in a `StripedScores` matrix.
- `Encode` trait to encode an ASCII sequence into a `Vec<Symbol>`.
- AVX2 implementation of `Score` using `gather` instead of `permute` for protein alphabets.
- `From<Vec<_>>` and `Default` trait implementations to `EncodedSequence`.
- `Alphabet` methods to operate on ASCII strings.
- `StripedScores.is_empty` method to check if a `StripedScores` contains any score.

#### `lightmotif-py`
- `StripedScores.threshold` method wrapping the `Threshold` pipeline operation.
- `StripedScores.max` and `StripedScores.argmax` methods to get the best score and best position.

### Fixed

#### `lightmotif`
- `Score` causing an overflow when given a sequence shorter than the PSSM.
- `Maximum` trait returns the smallest position on equal maxima.


## [v0.3.0] - 2023-06-25
[v0.3.0]: https://github.com/althonos/lightmotif/compare/v0.2.0...v0.3.0

### Changed
- Rewrite the SSE2 maximum search implementation using a generic number of columns.
- Refactor `lightmotif::pwm` to avoid infinite odds-ratio for columns with zero background frequencies.

### Added
- `lightmotif-tfmpvalue` crate implementing the TFMPvalue for computing p-values for a `ScoringMatrix`.
- `DenseMatrix::from_rows` method to create a dense matrix from an iterable of rows.
- `PartialEq` implementation for matrices in `lightmotif`.
- Methods to compute the minimum and maximum scores of a `ScoringMatrix`.


## [v0.2.0] - 2023-05-15
[v0.2.0]: https://github.com/althonos/lightmotif/compare/v0.1.1...v0.2.0

### Changed
- Crate structure to avoid cluttering the main `lightmotif` module namespace.
- Swizzling used in the SSSE3 implementation to make it require SSE2 only.
- TRANSFAC parser to support parsing sections in arbitrary order and accept additional metadata.
- Use `memchr` to parse lines faster in TRANSFAC parser.
- Use `typenum` and `generic-array` to handle constant matrix dimensions.
- Make `Score` trait generic over the number of columns in the striped sequence.

### Added
- Accessors for some of the attributes of `lightmotif_transfac::Matrix`.
- Pipeline method to extract the best position from a `StripedScore` matrix.
- Child trait for alphabet complementation.
- Methods for reverse-complementing all matrices from `lightmotif::pwm`.
- `Alphabet::symbols` method to get all the symbols of an alphabet.
- `StripedSequence::encode` constructor to encode and stripe a text sequence without allocating an extra `EncodedSequence`.
- Iterator methods and helper struct for `StripedScores`.
- `Protein` alphabet to `lightmotif::abc`.
- `DenseMatrix::uninitialized` constructor to allocate a dense matrix without filling its contents.
- `lightmotif::num` module with `typenum` re-exports and additional `StrictlyPositive` marker trait.
- Arm NEON implementation of the position scoring algorithm.
- `Display` implementation for `EncodedSequence` instead of an arbitrary `ToString` implementation.


## [v0.1.1] - 2023-05-04
[v0.1.1]: https://github.com/althonos/lightmotif/compare/v0.1.0...v0.1.1

### Added
- Helper crate to detect CPU features support at runtime.

### Fixed
- AVX2 code being imported on x86-64 platforms without checking for OS support.
- AVX2-enabled extension always being compiled even on platforms with no AVX2 support.

### Removed
- `built` and `pyo3-built` build dependencies (causing issues with workspaces).


## [v0.1.0] - 2023-05-04
[v0.1.0]: https://github.com/althonos/lightmotif/compare/4ccf9596b7...v0.1.0

Initial release.
