use super::{
config::RadauMatrixLayout,
error::{RadauConfigError, RadauError},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct RadauJacobianPattern {
dimension: usize,
entries: Vec<(usize, usize)>,
}
impl RadauJacobianPattern {
pub(crate) fn from_entries(
dimension: usize,
entries: impl IntoIterator<Item = (usize, usize)>,
) -> Result<Self, RadauError> {
if dimension == 0 {
return Err(RadauConfigError::ZeroDimension.into());
}
let mut entries = entries.into_iter().collect::<Vec<_>>();
if entries
.iter()
.any(|&(row, col)| row >= dimension || col >= dimension)
{
return Err(RadauConfigError::JacobianPatternOutOfBounds.into());
}
entries.sort_unstable();
entries.dedup();
Ok(Self { dimension, entries })
}
pub(crate) fn dense(dimension: usize) -> Result<Self, RadauError> {
let entries = (0..dimension).flat_map(|row| (0..dimension).map(move |col| (row, col)));
Self::from_entries(dimension, entries)
}
pub(crate) fn banded(dimension: usize, lower: usize, upper: usize) -> Result<Self, RadauError> {
let entries = (0..dimension).flat_map(|col| {
let first = col.saturating_sub(upper);
let last = (col + lower + 1).min(dimension);
(first..last).map(move |row| (row, col))
});
Self::from_entries(dimension, entries)
}
pub(crate) fn for_layout(
dimension: usize,
layout: RadauMatrixLayout,
sparse_entries: Option<&[(usize, usize)]>,
) -> Result<Self, RadauError> {
match layout {
RadauMatrixLayout::Dense => Self::dense(dimension),
RadauMatrixLayout::Sparse => Self::from_entries(
dimension,
sparse_entries
.ok_or(RadauConfigError::SparsePatternMissing)?
.iter()
.copied(),
),
RadauMatrixLayout::Banded { lower, upper } => Self::banded(dimension, lower, upper),
}
}
pub(crate) fn dimension(&self) -> usize {
self.dimension
}
pub(crate) fn entries(&self) -> &[(usize, usize)] {
&self.entries
}
pub(crate) fn values_len(&self, layout: RadauMatrixLayout) -> usize {
match layout {
RadauMatrixLayout::Dense => self.dimension * self.dimension,
RadauMatrixLayout::Sparse => self.entries.len(),
RadauMatrixLayout::Banded { lower, upper } => (lower + upper + 1) * self.dimension,
}
}
pub(crate) fn compact_slot(
&self,
layout: RadauMatrixLayout,
row: usize,
col: usize,
) -> Option<usize> {
match layout {
RadauMatrixLayout::Dense => Some(row * self.dimension + col),
RadauMatrixLayout::Banded { lower, upper } => (row < self.dimension
&& col < self.dimension
&& row + upper >= col
&& col + lower >= row)
.then(|| (upper + row - col) * self.dimension + col),
RadauMatrixLayout::Sparse => self
.entries
.iter()
.position(|&(entry_row, entry_col)| (entry_row, entry_col) == (row, col)),
}
}
pub(crate) fn is_in_layout(&self, layout: RadauMatrixLayout, row: usize, col: usize) -> bool {
self.compact_slot(layout, row, col).is_some()
}
}