#[cfg(feature = "serde-serialize")]
mod pattern_serde;
use crate::SparseFormatError;
use crate::cs::transpose_cs;
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SparsityPattern {
major_offsets: Vec<usize>,
minor_indices: Vec<usize>,
minor_dim: usize,
}
impl SparsityPattern {
pub fn zeros(major_dim: usize, minor_dim: usize) -> Self {
Self {
major_offsets: vec![0; major_dim + 1],
minor_indices: vec![],
minor_dim,
}
}
#[inline]
#[must_use]
pub fn major_offsets(&self) -> &[usize] {
&self.major_offsets
}
#[inline]
#[must_use]
pub fn minor_indices(&self) -> &[usize] {
&self.minor_indices
}
#[inline]
#[must_use]
pub fn major_dim(&self) -> usize {
assert!(!self.major_offsets.is_empty());
self.major_offsets.len() - 1
}
#[inline]
#[must_use]
pub fn minor_dim(&self) -> usize {
self.minor_dim
}
#[inline]
#[must_use]
pub fn nnz(&self) -> usize {
self.minor_indices.len()
}
#[inline]
#[must_use]
pub fn lane(&self, major_index: usize) -> &[usize] {
self.get_lane(major_index).unwrap()
}
#[inline]
#[must_use]
pub fn get_lane(&self, major_index: usize) -> Option<&[usize]> {
let offset_begin = *self.major_offsets().get(major_index)?;
let offset_end = *self.major_offsets().get(major_index + 1)?;
Some(&self.minor_indices()[offset_begin..offset_end])
}
pub fn try_from_offsets_and_indices(
major_dim: usize,
minor_dim: usize,
major_offsets: Vec<usize>,
minor_indices: Vec<usize>,
) -> Result<Self, SparsityPatternFormatError> {
use SparsityPatternFormatError::*;
if major_offsets.len() != major_dim + 1 {
return Err(InvalidOffsetArrayLength);
}
{
let first_offset_ok = *major_offsets.first().unwrap() == 0;
let last_offset_ok = *major_offsets.last().unwrap() == minor_indices.len();
if !first_offset_ok || !last_offset_ok {
return Err(InvalidOffsetFirstLast);
}
}
{
for lane_idx in 0..major_dim {
let range_start = major_offsets[lane_idx];
let range_end = major_offsets[lane_idx + 1];
if range_start > range_end {
return Err(NonmonotonicOffsets);
}
let minor_indices = &minor_indices[range_start..range_end];
let mut iter = minor_indices.iter();
let mut prev: Option<usize> = None;
while let Some(next) = iter.next().copied() {
if next >= minor_dim {
return Err(MinorIndexOutOfBounds);
}
if let Some(prev) = prev {
match prev.cmp(&next) {
std::cmp::Ordering::Greater => return Err(NonmonotonicMinorIndices),
std::cmp::Ordering::Equal => return Err(DuplicateEntry),
std::cmp::Ordering::Less => {}
}
}
prev = Some(next);
}
}
}
Ok(Self {
major_offsets,
minor_indices,
minor_dim,
})
}
pub unsafe fn from_offset_and_indices_unchecked(
major_dim: usize,
minor_dim: usize,
major_offsets: Vec<usize>,
minor_indices: Vec<usize>,
) -> Self {
assert_eq!(major_offsets.len(), major_dim + 1);
{
let first_offset_ok = *major_offsets.first().unwrap() == 0;
let last_offset_ok = *major_offsets.last().unwrap() == minor_indices.len();
assert!(first_offset_ok && last_offset_ok);
}
Self {
major_offsets,
minor_indices,
minor_dim,
}
}
#[must_use]
pub fn entries(&self) -> SparsityPatternIter<'_> {
SparsityPatternIter::from_pattern(self)
}
pub fn disassemble(self) -> (Vec<usize>, Vec<usize>) {
(self.major_offsets, self.minor_indices)
}
#[must_use]
pub fn transpose(&self) -> Self {
let values = vec![(); self.nnz()];
let (new_offsets, new_indices, _) = transpose_cs(
self.major_dim(),
self.minor_dim(),
self.major_offsets(),
self.minor_indices(),
&values,
);
Self::try_from_offsets_and_indices(
self.minor_dim(),
self.major_dim(),
new_offsets,
new_indices,
)
.expect("Internal error: Transpose should never fail.")
}
}
impl Default for SparsityPattern {
fn default() -> Self {
Self {
major_offsets: vec![0],
minor_indices: vec![],
minor_dim: 0,
}
}
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SparsityPatternFormatError {
InvalidOffsetArrayLength,
InvalidOffsetFirstLast,
NonmonotonicOffsets,
MinorIndexOutOfBounds,
DuplicateEntry,
NonmonotonicMinorIndices,
}
impl From<SparsityPatternFormatError> for SparseFormatError {
fn from(err: SparsityPatternFormatError) -> Self {
use crate::SparseFormatErrorKind;
use crate::SparseFormatErrorKind::*;
use SparsityPatternFormatError::DuplicateEntry as PatternDuplicateEntry;
use SparsityPatternFormatError::*;
match err {
InvalidOffsetArrayLength
| InvalidOffsetFirstLast
| NonmonotonicOffsets
| NonmonotonicMinorIndices => {
SparseFormatError::from_kind_and_error(InvalidStructure, Box::from(err))
}
MinorIndexOutOfBounds => {
SparseFormatError::from_kind_and_error(IndexOutOfBounds, Box::from(err))
}
PatternDuplicateEntry => SparseFormatError::from_kind_and_error(
#[allow(unused_qualifications)]
SparseFormatErrorKind::DuplicateEntry,
Box::from(err),
),
}
}
}
impl fmt::Display for SparsityPatternFormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SparsityPatternFormatError::InvalidOffsetArrayLength => {
write!(f, "Length of offset array is not equal to (major_dim + 1).")
}
SparsityPatternFormatError::InvalidOffsetFirstLast => {
write!(f, "First or last offset is incompatible with format.")
}
SparsityPatternFormatError::NonmonotonicOffsets => {
write!(f, "Offsets are not monotonically increasing.")
}
SparsityPatternFormatError::MinorIndexOutOfBounds => {
write!(f, "A minor index is out of bounds.")
}
SparsityPatternFormatError::DuplicateEntry => {
write!(f, "Input data contains duplicate entries.")
}
SparsityPatternFormatError::NonmonotonicMinorIndices => {
write!(
f,
"Minor indices are not monotonically increasing within each lane."
)
}
}
}
}
impl Error for SparsityPatternFormatError {}
#[derive(Debug, Clone)]
pub struct SparsityPatternIter<'a> {
major_offsets: &'a [usize],
minor_indices: &'a [usize],
current_lane_idx: usize,
remaining_minors_in_lane: &'a [usize],
}
impl<'a> SparsityPatternIter<'a> {
fn from_pattern(pattern: &'a SparsityPattern) -> Self {
let first_lane_end = pattern.major_offsets().get(1).unwrap_or(&0);
let minors_in_first_lane = &pattern.minor_indices()[0..*first_lane_end];
Self {
major_offsets: pattern.major_offsets(),
minor_indices: pattern.minor_indices(),
current_lane_idx: 0,
remaining_minors_in_lane: minors_in_first_lane,
}
}
}
impl<'a> Iterator for SparsityPatternIter<'a> {
type Item = (usize, usize);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some(minor_idx) = self.remaining_minors_in_lane.first() {
let item = Some((self.current_lane_idx, *minor_idx));
self.remaining_minors_in_lane = &self.remaining_minors_in_lane[1..];
item
} else {
loop {
if self.current_lane_idx + 2 >= self.major_offsets.len() {
return None;
} else {
self.current_lane_idx += 1;
let lower = self.major_offsets[self.current_lane_idx];
let upper = self.major_offsets[self.current_lane_idx + 1];
if upper > lower {
self.remaining_minors_in_lane = &self.minor_indices[(lower + 1)..upper];
return Some((self.current_lane_idx, self.minor_indices[lower]));
}
}
}
}
}
}