use itertools::Itertools;
use ordered_float::OrderedFloat;
use crate::algebra::chain_complexes::barcode::{get_barcode, Barcode};
use crate::algebra::chain_complexes::{ChainComplex, FilteredChainComplex};
use crate::algebra::matrices::operations::iterate_rows_and_columns::SequenceOfRows;
use crate::algebra::matrices::operations::umatch::gimbled::GimbledUmatch;
use crate::algebra::matrices::types::matching::GeneralizedMatchingMatrixWithSequentialOrder;
use crate::algebra::matrices::types::transpose::OrderAntiTranspose;
use crate::utilities::iterators::general::{TwoTypeIterator, IterWrappedVec, IterWrappedVecReverse};
use crate::algebra::matrices::{operations::umatch::row_major::Umatch};
use crate::algebra::matrices::operations::umatch::row_major::comb::{SourceComb, SourceCombInverse, TargetComb, TargetCombInverse};
use crate::algebra::matrices::operations::{MatrixOracleOperations, SequenceOfColumns};
use crate::algebra::matrices::query::{MatrixAlgebra, MatrixOracle};
use crate::algebra::rings::traits::{SemiringOperations, DivisionRingOperations};
use crate::utilities::order::{JudgeOrder, JudgePartialOrder};
use crate::algebra::vectors::entries::{KeyValPair};
use std::fmt::Debug;
use std::cmp::Ordering;
use std::hash::Hash;
use std::collections::{HashMap, HashSet};
use std::iter::Cloned;
use std::vec::IntoIter;
use derive_new::new;
use derive_getters::Dissolve;
#[derive(new,Clone,Copy,Debug,PartialEq)]
pub struct DifferentialComb<
'a,
MatrixToFactor,
>
where
MatrixToFactor: MatrixAlgebra,
MatrixToFactor::ColumnIndex: Hash + Eq, MatrixToFactor::RowIndex: Hash + Eq, {
umatch: &'a GimbledUmatch< MatrixToFactor >,
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixOracle for
DifferentialComb
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: JudgePartialOrder< EntryForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{
type Coefficient = BoundaryMatrix::Coefficient; type RowIndex = IndexForRowsAndColumns; type ColumnIndex = IndexForRowsAndColumns; type RowEntry = EntryForRowsAndColumns; type ColumnEntry = EntryForRowsAndColumns; type Row = IterWrappedVec< EntryForRowsAndColumns >; type RowReverse = IterWrappedVecReverse< EntryForRowsAndColumns >; type Column = IterWrappedVec< EntryForRowsAndColumns >;
type ColumnReverse = IterWrappedVecReverse< EntryForRowsAndColumns >;
fn structural_nonzero_entry( & self, row: &Self::RowIndex, column: &Self::ColumnIndex ) -> Option< Self::Coefficient > {
match self.umatch.generalized_matching_matrix_ref().has_a_match_for_row_index( column ) {
false => { self.umatch.source_comb().structural_nonzero_entry( row, column ) },
true => { self.umatch.target_comb().structural_nonzero_entry( row, column ) },
}
}
fn has_column_for_index( & self, index: & Self::ColumnIndex) -> bool {
self.umatch.matrix_to_factor_ref().has_column_for_index( index )
}
fn has_row_for_index( & self, index: &Self::RowIndex ) -> bool {
self.umatch.matrix_to_factor_ref().has_row_for_index( index )
}
fn row( & self, index: &Self::RowIndex ) -> Self::Row
{
let matching_matrix = self.umatch.generalized_matching_matrix_ref();
if matching_matrix.lacks_a_match_for_column_index( index ) {
return IterWrappedVec::new( self.umatch.target_comb().row( index ).collect() );
}
let target_row = self.umatch.target_comb().row( index );
let mut source_row = self.umatch.source_comb().row( index );
source_row.next(); let source_row = source_row.filter( |entry|
matching_matrix.lacks_a_match_for_row_index( & entry.key() )
);
let order_operator = self.umatch.matrix_to_factor_ref().order_operator_for_row_entries();
let merged = target_row.merge_by(
source_row,
| a,b | { order_operator.judge_lt( a, b ) }
)
.collect::<Vec<_>>();
return IterWrappedVec::new( merged );
}
fn row_reverse( & self, index: &Self::RowIndex ) -> Self::RowReverse {
self.row( index ).reset_and_run_backward()
}
fn column( & self, index: &Self::ColumnIndex) -> Self::Column
{
match self.umatch.generalized_matching_matrix_ref().has_a_match_for_row_index( index ) {
false => IterWrappedVec::new( self.umatch.source_comb().column( index ).collect() ),
true => IterWrappedVec::new( self.umatch.target_comb().column( index ).collect() ),
}
}
fn column_reverse( & self, index: &Self::ColumnIndex) -> Self::ColumnReverse
{
self.column( index ).reset_and_run_backward()
}
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixAlgebra for
DifferentialComb
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: Clone + JudgeOrder< EntryForRowsAndColumns >,
OrderOperatorForRowAndColumnIndices: Clone + JudgeOrder< IndexForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{
type RingOperator = BoundaryMatrix::RingOperator;
type OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries;
type OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices;
type OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries;
type OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices;
fn ring_operator( &self ) -> Self::RingOperator {
self.umatch.ring_operator()
}
fn order_operator_for_row_entries( &self ) -> Self::OrderOperatorForRowEntries {
self.umatch.order_operator_for_row_entries()
}
fn order_operator_for_row_indices( &self ) -> Self::OrderOperatorForRowIndices {
self.umatch.order_operator_for_row_indices()
}
fn order_operator_for_column_entries( &self ) -> Self::OrderOperatorForColumnEntries {
self.umatch.order_operator_for_column_entries()
}
fn order_operator_for_column_indices( &self ) -> Self::OrderOperatorForColumnIndices {
self.umatch.order_operator_for_column_indices()
}
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixOracleOperations for
DifferentialComb
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: JudgePartialOrder< EntryForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{}
#[derive(new,Clone,Copy,Debug,PartialEq)]
pub struct DifferentialCombInverse<
'a,
MatrixToFactor,
>
where
MatrixToFactor: MatrixAlgebra,
MatrixToFactor::ColumnIndex: Hash + Eq, MatrixToFactor::RowIndex: Hash + Eq, {
umatch: &'a GimbledUmatch< MatrixToFactor >,
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixOracle for
DifferentialCombInverse
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash + 'a, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: JudgePartialOrder< EntryForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{
type Coefficient = BoundaryMatrix::Coefficient; type RowIndex = IndexForRowsAndColumns; type ColumnIndex = IndexForRowsAndColumns; type RowEntry = EntryForRowsAndColumns; type ColumnEntry = EntryForRowsAndColumns; type Row = IterWrappedVec< EntryForRowsAndColumns >;
type RowReverse = IterWrappedVec< EntryForRowsAndColumns >;
type Column = IterWrappedVecReverse< EntryForRowsAndColumns >;
type ColumnReverse = IterWrappedVec< EntryForRowsAndColumns >;
fn structural_nonzero_entry( & self, row: &Self::RowIndex, column: &Self::ColumnIndex ) -> Option< Self::Coefficient > {
match self.umatch.generalized_matching_matrix_ref().has_a_match_for_column_index( row ) {
false => { self.umatch.target_comb_inverse().structural_nonzero_entry( row, column ) },
true => { self.umatch.source_comb_inverse().structural_nonzero_entry( row, column ) },
}
}
fn has_column_for_index( & self, index: & Self::ColumnIndex) -> bool {
self.umatch.matrix_to_factor_ref().has_column_for_index( index )
}
fn has_row_for_index( & self, index: &Self::RowIndex ) -> bool {
self.umatch.matrix_to_factor_ref().has_row_for_index( index )
}
fn column_reverse( & self, index: &Self::RowIndex ) -> Self::ColumnReverse
{
let matching_matrix = self.umatch.generalized_matching_matrix_ref();
if matching_matrix.lacks_a_match_for_row_index( index ) {
return
IterWrappedVec::new(
self.umatch.source_comb_inverse().column_reverse( index ).collect()
)
}
let source_inverse_column_rev = self.umatch.source_comb_inverse().column_reverse( index );
let mut target_inverse_column_rev = self.umatch.target_comb_inverse().column_reverse( index );
target_inverse_column_rev.next(); let target_inverse_column_rev = target_inverse_column_rev.filter( |entry|
matching_matrix.lacks_a_match_for_column_index( & entry.key() )
);
let order_operator = self.umatch.matrix_to_factor_ref()
.order_operator_for_column_entries_reverse();
let merged = source_inverse_column_rev.merge_by(
target_inverse_column_rev,
| a,b | { order_operator.judge_lt( a, b ) }
)
.collect::<Vec<_>>();
return
IterWrappedVec::new( merged )
}
fn column( & self, index: &Self::RowIndex ) -> Self::Column {
return self.column_reverse( index ).reset_and_run_backward();
}
fn row( & self, index: &Self::RowIndex) -> Self::Row
{
match self.umatch.generalized_matching_matrix_ref().has_a_match_for_column_index( index ) {
false => IterWrappedVec::new( self.umatch.target_comb_inverse().row( index ).collect() ),
true => IterWrappedVec::new( self.umatch.source_comb_inverse().row( index ).collect() ),
}
}
fn row_reverse( & self, index: &Self::ColumnIndex) -> Self::RowReverse
{
match self.umatch.generalized_matching_matrix_ref().has_a_match_for_column_index( index ) {
false => IterWrappedVec::new( self.umatch.target_comb_inverse().row_reverse( index ).collect() ),
true => IterWrappedVec::new( self.umatch.source_comb_inverse().row_reverse( index ).collect() ),
}
}
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixAlgebra for
DifferentialCombInverse
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash + 'a, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: Clone + JudgeOrder< EntryForRowsAndColumns >,
OrderOperatorForRowAndColumnIndices: Clone + JudgeOrder< IndexForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{
type RingOperator = BoundaryMatrix::RingOperator;
type OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries;
type OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices;
type OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries;
type OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices;
fn ring_operator( &self ) -> Self::RingOperator {
self.umatch.ring_operator()
}
fn order_operator_for_row_entries( &self ) -> Self::OrderOperatorForRowEntries {
self.umatch.order_operator_for_row_entries()
}
fn order_operator_for_row_indices( &self ) -> Self::OrderOperatorForRowIndices {
self.umatch.order_operator_for_row_indices()
}
fn order_operator_for_column_entries( &self ) -> Self::OrderOperatorForColumnEntries {
self.umatch.order_operator_for_column_entries()
}
fn order_operator_for_column_indices( &self ) -> Self::OrderOperatorForColumnIndices {
self.umatch.order_operator_for_column_indices()
}
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
MatrixOracleOperations for
DifferentialCombInverse
< 'a, BoundaryMatrix, >
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash + 'a, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: JudgePartialOrder< EntryForRowsAndColumns >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{}
#[derive(Debug,Clone,Dissolve)]
pub struct DifferentialUmatch<
BoundaryMatrix,
>
where
BoundaryMatrix: MatrixAlgebra,
BoundaryMatrix::ColumnIndex: Hash,
BoundaryMatrix::RowIndex: Hash,
{
umatch: GimbledUmatch< BoundaryMatrix >,
min_homology_dimension: isize,
max_homology_dimension: isize,
}
impl <
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
DifferentialUmatch<
BoundaryMatrix,
>
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>
+ MatrixOracleOperations
+ ChainComplex,
OrderOperatorForRowAndColumnEntries: JudgePartialOrder< EntryForRowsAndColumns >,
OrderOperatorForRowAndColumnIndices: JudgeOrder< IndexForRowsAndColumns >,
IndexForRowsAndColumns: Clone + Debug + Hash + Eq, BoundaryMatrix::Coefficient: Debug,
BoundaryMatrix::RowEntry: KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::ColumnEntry: KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
EntryForRowsAndColumns: Clone + Debug + PartialEq,
{
pub fn new(
boundary_matrix: BoundaryMatrix,
min_homology_dimension: isize,
max_homology_dimension: isize,
)
-> DifferentialUmatch
< BoundaryMatrix >
{
let mut buffer = Vec::new();
let mut row_indices_in_decreasing_order = Vec::new();
for degree in min_homology_dimension-1 .. max_homology_dimension+1 {
buffer.extend(
boundary_matrix
.basis_vector_indices_for_dimension( degree )
);
buffer.reverse();
row_indices_in_decreasing_order.append( &mut buffer );
}
let umatch = Umatch::new_with_compression(
boundary_matrix,
row_indices_in_decreasing_order,
);
DifferentialUmatch {
umatch: GimbledUmatch::RowMajor(umatch),
min_homology_dimension,
max_homology_dimension,
}
}
pub fn column_major( & self ) -> Option< Self >
where
BoundaryMatrix: Clone,
{
match self.umatch.column_major() {
Some(umatch) => {
Some( DifferentialUmatch {
umatch,
min_homology_dimension: self.min_homology_dimension,
max_homology_dimension: self.max_homology_dimension,
})
},
None => None, }
}
pub fn is_column_major( & self ) -> bool {
self.umatch.is_column_major()
}
pub fn boundary_matrix( &self ) -> & BoundaryMatrix
{
self.umatch.matrix_to_factor_ref()
}
pub fn generalized_matching_matrix( &self ) -> & GeneralizedMatchingMatrixWithSequentialOrder<
IndexForRowsAndColumns,
IndexForRowsAndColumns,
BoundaryMatrix::Coefficient,
>
{
self.asymmetric_umatch().generalized_matching_matrix_ref()
}
pub fn min_homology_dimension( &self ) -> isize
{
self.min_homology_dimension
}
pub fn max_homology_dimension( &self ) -> isize
{
self.max_homology_dimension
}
pub fn dimensions_where_homology_is_computed( &self ) -> std::ops::Range<isize> {
self.min_homology_dimension .. self.max_homology_dimension + 1
}
pub fn differential_comb( &self ) -> DifferentialComb< '_, BoundaryMatrix, >
{ DifferentialComb::new( & self.umatch ) }
pub fn differential_comb_inverse( &self ) -> DifferentialCombInverse< '_, BoundaryMatrix, >
{ DifferentialCombInverse::new( & self.umatch ) }
pub fn asymmetric_umatch(&self) -> & GimbledUmatch< BoundaryMatrix >
{ & self.umatch }
pub fn ring_operator( &self ) -> BoundaryMatrix::RingOperator {
self.umatch.ring_operator()
}
pub fn indices_in_homologically_valid_dimensions( & self ) -> Vec< IndexForRowsAndColumns > {
let mut indices = Vec::new();
for degree in self.min_homology_dimension .. self.max_homology_dimension + 1 {
indices.extend(
self.boundary_matrix()
.basis_vector_indices_for_dimension( degree )
);
}
indices
}
pub fn row_reduction_indices( &self ) -> Vec< IndexForRowsAndColumns > {
let mut buffer = Vec::new();
let mut row_indices_in_decreasing_order = Vec::new();
for degree in self.min_homology_dimension - 1 .. self.max_homology_dimension + 1 {
buffer.extend(
self.boundary_matrix()
.basis_vector_indices_for_dimension( degree )
);
buffer.reverse();
row_indices_in_decreasing_order.append( &mut buffer );
}
row_indices_in_decreasing_order
}
pub fn row_reduction_indices_plus_negatives( &self ) -> HashSet< IndexForRowsAndColumns > {
let mut indices = HashSet::new();
for dimension in self.min_homology_dimension - 1 .. self.max_homology_dimension + 1 {
indices.extend(
self.boundary_matrix()
.basis_vector_indices_for_dimension( dimension )
);
}
let matching_matrix = self.umatch.generalized_matching_matrix_ref();
for column_index in matching_matrix.matched_column_indices_in_sequence() {
indices.insert( column_index.clone() );
}
indices
}
pub fn homology_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_homology()
};
a.collect()
}
pub fn cohomology_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
self.homology_indices()
}
pub fn cycle_space_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_cycle_space()
};
a.collect()
}
pub fn cocycle_space_indices( &self ) ->
Vec<IndexForRowsAndColumns>
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_cocycle_space()
};
a.collect()
}
pub fn boundary_space_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_boundary_space(),
};
a.collect()
}
pub fn coboundary_space_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_coboundary_space(),
};
a.collect()
}
pub fn non_homology_indices( &self ) ->
Vec< IndexForRowsAndColumns >
{
let v = self.indices_in_homologically_valid_dimensions();
let a: SelectedIndices<'_, BoundaryMatrix, Vec<IndexForRowsAndColumns>> = SelectedIndices{
umatch: &self.umatch,
row_index_iterator: v.into_iter(),
criteria: IndexSelectionCriterion::for_non_homology(),
};
a.collect()
}
pub fn persistent_homology_indices( &self ) ->
Vec< IndexForRowsAndColumns >
where
BoundaryMatrix: FilteredChainComplex< FiltrationValue: Ord >
{
let mut ph_indices = Vec::new();
let generalized_matching_matrix = self.generalized_matching_matrix();
let boundary_matrix = self.boundary_matrix();
for index in self.indices_in_homologically_valid_dimensions() {
if generalized_matching_matrix.has_a_match_for_column_index( & index ) {
continue }
if let Some( column_index ) = generalized_matching_matrix.column_index_for_row_index(&index) {
let birth_filtration = boundary_matrix.filtration_value_for_basis_vector_with_index( &index ).ok().unwrap();
let death_filtration = boundary_matrix.filtration_value_for_basis_vector_with_index( &column_index ).ok().unwrap();
if birth_filtration < death_filtration {
ph_indices.push( index );
}
}
}
ph_indices
}
pub fn bounding_index_for( &self, index: &IndexForRowsAndColumns ) -> Option< IndexForRowsAndColumns >
where
BoundaryMatrix: ChainComplex,
{
self.generalized_matching_matrix()
.column_index_for_row_index( index )
}
pub fn bounded_index_for( &self, index: &IndexForRowsAndColumns ) -> Option< IndexForRowsAndColumns >
where
BoundaryMatrix: ChainComplex,
{
self.generalized_matching_matrix()
.row_index_for_column_index( index )
}
pub fn cobounding_index_for( &self, index: &IndexForRowsAndColumns ) -> Option< IndexForRowsAndColumns >
where
BoundaryMatrix: ChainComplex,
{
self.generalized_matching_matrix()
.row_index_for_column_index( index )
}
pub fn cobounded_index_for( &self, index: &IndexForRowsAndColumns ) -> Option< IndexForRowsAndColumns >
where
BoundaryMatrix: ChainComplex,
{
self.generalized_matching_matrix()
.column_index_for_row_index( index )
}
pub fn homology_basis( &self ) ->
SequenceOfColumns
<
DifferentialComb<'_, BoundaryMatrix>,
IntoIter<IndexForRowsAndColumns>,
>
{
SequenceOfColumns::new(
self.differential_comb(),
self.homology_indices().into_iter(),
)
}
pub fn cohomology_basis( &self ) ->
SequenceOfRows<
DifferentialCombInverse< '_, BoundaryMatrix, >,
IntoIter<IndexForRowsAndColumns>
>
{
SequenceOfRows::new(
self.differential_comb_inverse(),
self.homology_indices().into_iter(),
)
}
pub fn cycle_space_basis( &self ) ->
SequenceOfColumns
<
DifferentialComb<'_, BoundaryMatrix>,
IntoIter<IndexForRowsAndColumns>,
>
{
SequenceOfColumns::new(
self.differential_comb(),
self.cycle_space_indices().into_iter(),
)
}
pub fn cocycle_space_basis( &self ) ->
SequenceOfRows<
DifferentialCombInverse< '_, BoundaryMatrix, >,
IntoIter<IndexForRowsAndColumns>
>
{
SequenceOfRows::new(
self.differential_comb_inverse(),
self.cocycle_space_indices().into_iter(),
)
}
pub fn boundary_space_basis( &self ) ->
SequenceOfColumns
<
DifferentialComb<'_, BoundaryMatrix>,
IntoIter<IndexForRowsAndColumns>,
>
{
SequenceOfColumns::new(
self.differential_comb(),
self.boundary_space_indices().into_iter(),
)
}
pub fn coboundary_space_basis( &self ) ->
SequenceOfRows<
DifferentialCombInverse< '_, BoundaryMatrix, >,
IntoIter<IndexForRowsAndColumns>
>
{
SequenceOfRows::new(
self.differential_comb_inverse(),
self.coboundary_space_indices().into_iter(),
)
}
pub fn non_homology_basis( &self ) ->
SequenceOfColumns
<
DifferentialComb<'_, BoundaryMatrix>,
IntoIter<IndexForRowsAndColumns>,
>
{
SequenceOfColumns::new(
self.differential_comb(),
self.non_homology_indices().into_iter(),
)
}
pub fn print_homology_basis( & self ) {
for ( counter, basis_vector ) in self.homology_basis().into_iter().enumerate() {
println!(" Homology basis vector {}: {:?}", counter, basis_vector);
}
}
pub fn print_cohomology_basis( & self ) {
for ( counter, basis_vector ) in self.cohomology_basis().into_iter().enumerate() {
println!(" Coomology basis vector {}: {:?}", counter, basis_vector);
}
}
pub fn barcode
(
&self,
return_cycle_representatives: bool,
return_bounding_chains: bool,
)
-> Barcode<
BoundaryMatrix::RowIndex,
BoundaryMatrix::ColumnEntry
>
where
BoundaryMatrix: FilteredChainComplex< FiltrationValue = OrderedFloat< f64 > >,
OrderOperatorForRowAndColumnEntries: Clone,
OrderOperatorForRowAndColumnIndices: Clone,
{
get_barcode(
&self,
return_cycle_representatives,
return_bounding_chains
)
}
pub fn betti_numbers( &self ) -> HashMap< isize, isize >
where
BoundaryMatrix: ChainComplex
{
let boundary_matrix = self.boundary_matrix();
let mut betti_numbers = HashMap::new();
for dimension in self.dimensions_where_homology_is_computed() {
betti_numbers.insert(dimension, 0);
}
for key in self.homology_indices() {
let dim = boundary_matrix
.dimension_for_basis_vector_with_index( &key ).ok().unwrap();
* betti_numbers.entry( dim ).or_insert(0) += 1;
}
betti_numbers
}
pub fn cycle_space_dimensions( &self ) -> HashMap< isize, isize >
where
BoundaryMatrix: ChainComplex
{
let boundary_matrix = self.boundary_matrix();
let mut subspace_dimensions = HashMap::new();
for dimension in self.dimensions_where_homology_is_computed() {
subspace_dimensions.insert(dimension, 0);
}
for key in self.cycle_space_indices() {
let dim = boundary_matrix
.dimension_for_basis_vector_with_index( &key ).ok().unwrap();
* subspace_dimensions.entry( dim ).or_insert(0) += 1;
}
subspace_dimensions
}
pub fn cocycle_space_dimensions( &self ) -> HashMap< isize, isize >
where
BoundaryMatrix: ChainComplex
{
let boundary_matrix = self.boundary_matrix();
let mut subspace_dimensions = HashMap::new();
for dimension in self.dimensions_where_homology_is_computed() {
subspace_dimensions.insert(dimension, 0);
}
for key in self.cocycle_space_indices() {
let dim = boundary_matrix
.dimension_for_basis_vector_with_index( &key ).ok().unwrap();
* subspace_dimensions.entry( dim ).or_insert(0) += 1;
}
subspace_dimensions
}
pub fn boundary_space_dimensions( &self ) -> HashMap< isize, isize >
where
BoundaryMatrix: ChainComplex
{
let boundary_matrix = self.boundary_matrix();
let mut subspace_dimensions = HashMap::new();
for dimension in self.dimensions_where_homology_is_computed() {
subspace_dimensions.insert(dimension, 0);
}
for key in self.boundary_space_indices() {
let dim = boundary_matrix
.dimension_for_basis_vector_with_index( &key ).ok().unwrap();
* subspace_dimensions.entry( dim ).or_insert(0) += 1;
}
subspace_dimensions
}
pub fn coboundary_space_dimensions( &self ) -> HashMap< isize, isize >
where
BoundaryMatrix: ChainComplex
{
let boundary_matrix = self.boundary_matrix();
let mut subspace_dimensions = HashMap::new();
for dimension in self.dimensions_where_homology_is_computed() {
subspace_dimensions.insert(dimension, 0);
}
for key in self.coboundary_space_indices() {
let dim = boundary_matrix
.dimension_for_basis_vector_with_index( &key ).ok().unwrap();
* subspace_dimensions.entry( dim ).or_insert(0) += 1;
}
subspace_dimensions
}
pub fn escolar_hiraoka_indices< DimensionEvaluator >( &self, birth_column: & IndexForRowsAndColumns, mut get_dimension: DimensionEvaluator, )
-> Vec< IndexForRowsAndColumns >
where
DimensionEvaluator: FnMut( & IndexForRowsAndColumns)-> isize,
{
let order_operator = self.asymmetric_umatch().order_operator_for_row_entries();
let generalized_matching_matrix_ref = self.umatch.generalized_matching_matrix_ref();
let zero = BoundaryMatrix::RingOperator::zero();
let to_entry = |i: IndexForRowsAndColumns| EntryForRowsAndColumns::new( i, zero.clone() );
let objective_dimension = get_dimension( birth_column );
let death_column_opt = generalized_matching_matrix_ref .column_index_for_row_index( birth_column );
let mut columns = Vec::new();
for row_index in self.row_reduction_indices() {
if get_dimension( & row_index ) != objective_dimension { continue }
if order_operator.judge_ge( &to_entry(row_index.clone()), &to_entry(birth_column.clone()) ) { continue }
if generalized_matching_matrix_ref.has_a_match_for_column_index( & row_index ) { continue }
let comp_death_opt = generalized_matching_matrix_ref
.column_index_for_row_index( birth_column );
if let Some( death_column ) = death_column_opt.as_ref() {
if let Some( comp_death ) = comp_death_opt {
if order_operator.judge_gt( &to_entry(comp_death.clone()), &to_entry(death_column.clone()) ) {
continue
}
} else {
continue
}
}
columns.push(row_index);
}
columns
}
pub fn escolar_hiraoka_indices_relaxed< DimensionEvaluator, FiltrationOrder >( &self, birth_column: & IndexForRowsAndColumns, mut get_dimension: DimensionEvaluator, mut filtration_order: FiltrationOrder )
-> Vec< IndexForRowsAndColumns >
where
DimensionEvaluator: FnMut( & IndexForRowsAndColumns)-> isize,
FiltrationOrder: FnMut( & IndexForRowsAndColumns, & IndexForRowsAndColumns )-> Ordering,
{
let generalized_matching_matrix_ref = self.umatch.generalized_matching_matrix_ref();
let objective_dimension = get_dimension( birth_column );
let death_column_opt = generalized_matching_matrix_ref .column_index_for_row_index( birth_column );
let mut columns = Vec::new();
for row_index in self.row_reduction_indices() {
if row_index == *birth_column { continue }
if get_dimension( & row_index ) != objective_dimension { continue }
if filtration_order( &row_index, birth_column) == Ordering::Greater { continue }
if generalized_matching_matrix_ref.has_a_match_for_column_index( & row_index ) { continue }
let comp_death_opt = generalized_matching_matrix_ref
.column_index_for_row_index( birth_column );
if let Some( death_column ) = death_column_opt.as_ref() {
if let Some( comp_death ) = comp_death_opt {
if filtration_order( &comp_death, death_column) == Ordering::Greater { continue }
} else {
continue
}
}
columns.push(row_index);
}
columns
}
}
#[derive(Debug,Clone)]
pub struct IndexSelectionCriterion{ boundary: bool, bounding: bool, harmonic: bool}
impl IndexSelectionCriterion{
fn for_homology() -> Self { IndexSelectionCriterion { boundary: false, bounding: false, harmonic: true } }
fn for_cycle_space() -> Self { IndexSelectionCriterion { boundary: true, bounding: false, harmonic: true } }
fn for_cocycle_space() -> Self { IndexSelectionCriterion { boundary: false, bounding: true, harmonic: true } }
fn for_boundary_space() -> Self { IndexSelectionCriterion { boundary: true, bounding: false, harmonic: false } }
fn for_coboundary_space() -> Self { IndexSelectionCriterion { boundary: false, bounding: true, harmonic: false } }
fn for_non_homology() -> Self { IndexSelectionCriterion { boundary: true, bounding: true, harmonic: false } }
}
#[derive(Debug,Clone)]
pub struct SelectedIndices<
'a,
BoundaryMatrix,
DecreasingRowIndexIterator,
>
where
BoundaryMatrix: MatrixAlgebra,
BoundaryMatrix::RowIndex: Hash,
BoundaryMatrix::ColumnIndex: Hash,
DecreasingRowIndexIterator: Clone + IntoIterator,
{
umatch: &'a GimbledUmatch< BoundaryMatrix >,
row_index_iterator: DecreasingRowIndexIterator::IntoIter,
criteria: IndexSelectionCriterion,
}
impl <
'a,
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
DecreasingRowIndexIterator,
>
Iterator for
SelectedIndices<
'a,
BoundaryMatrix,
DecreasingRowIndexIterator,
>
where
DecreasingRowIndexIterator: Clone + IntoIterator< Item = BoundaryMatrix::RowIndex >,
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>,
IndexForRowsAndColumns: Clone + Debug + Hash + Eq, BoundaryMatrix::RowEntry: KeyValPair< Key = BoundaryMatrix::RowIndex, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::ColumnEntry: KeyValPair< Key = BoundaryMatrix::RowIndex, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
{
type Item = IndexForRowsAndColumns;
fn next(&mut self) -> Option<Self::Item> {
let matching = self.umatch.generalized_matching_matrix_ref();
let criterion = &self.criteria;
self.row_index_iterator
.find( |x|
( criterion.bounding && matching.has_a_match_for_column_index(x) )
||
( criterion.boundary && matching.has_a_match_for_row_index(x) )
||
( criterion.harmonic && matching.lacks_match_for_index(x) )
)
}
}
type SequenceOfDifferentialCombColumns< 'a, BoundaryMatrix, IndexIterator > =
SequenceOfColumns<
DifferentialComb< 'a, BoundaryMatrix >,
SelectedIndices< 'a, BoundaryMatrix, IndexIterator >
>;
mod tests {
use itertools::Itertools;
use crate::algebra::chain_complexes::ChainComplex;
use crate::algebra::matrices::debug::{matrix_oracle_is_internally_consistent, matrix_order_operators_are_internally_consistent, product_is_identity_matrix};
use crate::algebra::matrices::operations::umatch::differential::DifferentialUmatch;
use crate::algebra::matrices::operations::MatrixOracleOperations;
use crate::algebra::matrices::query::{MatrixAlgebra, MatrixOracle};
use crate::algebra::matrices::types::product::ProductMatrix;
use crate::algebra::matrices::types::vec_of_vec::sorted::VecOfVec;
use crate::algebra::rings::traits::DivisionRingOperations;
use crate::algebra::vectors::entries::KeyValPair;
use crate::topology::simplicial::from::graph_weighted::DiagonalEntryIterator;
use crate::utilities::order::{JudgeOrder, JudgePartialOrder};
use std::fmt::Debug;
use std::hash::Hash;
#[test]
fn test_differential_umatch_random_symmetric_matrix() {
use crate::algebra::vectors::entries::KeyValGet;
use crate::algebra::matrices::types::third_party::IntoCSR;
use crate::algebra::matrices::query::MatrixOracle;
use crate::algebra::vectors::operations::VectorOperations;
use crate::algebra::matrices::operations::umatch::differential::DifferentialUmatch;
use crate::topology::simplicial::from::graph_weighted::VietorisRipsComplex;
use crate::utilities::iterators::general::minmax;
use ordered_float::OrderedFloat;
use std::sync::Arc;
use itertools::Itertools;
let number_of_points = 5;
let min_homology_dimension = 0;
let max_homology_dimension = 1;
let dissimilarity_matrix_vecvec = VecOfVec::random_symmetric_zero_diagonal_with_enclosing_radius_threshold(number_of_points);
let dissimilarity_matrix = Arc::new( dissimilarity_matrix_vecvec.into_csr(number_of_points, number_of_points) );
let ring_operator = crate::algebra::rings::types::native::FieldRationalSize::new();
let boundary_matrix_data = VietorisRipsComplex::new( dissimilarity_matrix.clone(), number_of_points, ring_operator ).ok().unwrap();
let boundary_matrix = Arc::new(boundary_matrix_data);
let mut indices_to_check = boundary_matrix.cliques_in_row_reduction_order(2);
let order_operator = boundary_matrix.order_operator_for_row_indices();
indices_to_check.sort_unstable_by( |a,b| order_operator.judge_cmp(a,b) );
let differential_umatch = DifferentialUmatch::new(
boundary_matrix,
min_homology_dimension,
max_homology_dimension,
);
validate_differential_umatch(
& differential_umatch,
& indices_to_check,
);
validate_differential_umatch(
& differential_umatch.column_major().unwrap(),
& indices_to_check,
);
}
#[allow(dead_code)]
fn validate_differential_umatch
<
BoundaryMatrix,
OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowAndColumnIndices,
IndexForRowsAndColumns,
EntryForRowsAndColumns,
>
(
differential_umatch: & DifferentialUmatch< BoundaryMatrix >,
indices_to_check: & Vec< BoundaryMatrix::RowIndex >
)
where
BoundaryMatrix: MatrixAlgebra<
RowEntry = EntryForRowsAndColumns,
RowIndex = IndexForRowsAndColumns,
OrderOperatorForRowEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForRowIndices = OrderOperatorForRowAndColumnIndices,
ColumnEntry = EntryForRowsAndColumns,
ColumnIndex = IndexForRowsAndColumns,
OrderOperatorForColumnEntries = OrderOperatorForRowAndColumnEntries,
OrderOperatorForColumnIndices = OrderOperatorForRowAndColumnIndices,
>
+ MatrixOracleOperations
+ ChainComplex
+ Clone,
BoundaryMatrix::Coefficient: Debug,
BoundaryMatrix::RowEntry: KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::ColumnEntry: KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
BoundaryMatrix::RingOperator: DivisionRingOperations,
IndexForRowsAndColumns: Clone + Debug + Eq + Hash, EntryForRowsAndColumns: Clone + Debug + PartialEq + KeyValPair< Key = IndexForRowsAndColumns, Val = BoundaryMatrix::Coefficient >,
OrderOperatorForRowAndColumnEntries: Clone + JudgeOrder< EntryForRowsAndColumns >,
OrderOperatorForRowAndColumnIndices: Clone + JudgeOrder< IndexForRowsAndColumns >,
{
let matching_packet = differential_umatch.asymmetric_umatch().generalized_matching_matrix_ref_packet();
let matching_matrix = differential_umatch.generalized_matching_matrix();
let diff_comb = differential_umatch.differential_comb();
let diff_comb_inverse = differential_umatch.differential_comb_inverse();
let boundary_matrix = differential_umatch.boundary_matrix();
let dj = ProductMatrix::new( boundary_matrix, diff_comb.clone() ); let jm = ProductMatrix::new( diff_comb.clone(), matching_packet.clone() );
assert!(
matrix_order_operators_are_internally_consistent(
& diff_comb,
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
).is_ok()
);
matrix_order_operators_are_internally_consistent(
&diff_comb_inverse,
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
).unwrap_or_else(|e| panic!("{}", e));
assert!(
matrix_oracle_is_internally_consistent(
differential_umatch.asymmetric_umatch().source_comb_inverse(),
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
)
);
assert!(
matrix_oracle_is_internally_consistent(
differential_umatch.asymmetric_umatch().target_comb_inverse(),
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
)
);
assert!(
matrix_oracle_is_internally_consistent(
& diff_comb,
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
)
);
assert!(
matrix_oracle_is_internally_consistent(
& diff_comb_inverse,
indices_to_check.iter().cloned(),
indices_to_check.iter().cloned(),
)
);
for index in indices_to_check.iter() {
assert_eq!(
index,
& diff_comb
.row(index)
.next() .unwrap()
.key()
);
}
for index in indices_to_check.iter() {
assert_eq!(
index,
& diff_comb_inverse
.row(index)
.next() .unwrap()
.key()
);
}
for index in indices_to_check.iter() {
let row = diff_comb_inverse.row(index);
match matching_matrix.has_a_match_for_column_index(index) {
true => {
assert!( row.eq(
differential_umatch.umatch.source_comb_inverse().row(index)
) );
},
false => {
assert!( row.eq(
differential_umatch.umatch.target_comb_inverse().row(index)
) );
}
}
}
for index in indices_to_check.iter() {
let column = diff_comb.column(index);
match matching_matrix.has_a_match_for_row_index(index) {
true => {
assert!( column.eq(
differential_umatch.umatch.target_comb().column(index)
) );
},
false => {
assert!( column.eq(
differential_umatch.umatch.source_comb().column(index)
) );
}
}
}
for row_index in differential_umatch.row_reduction_indices() {
if ! matching_matrix.has_match_for_index(&row_index) {
assert!( dj.column(&row_index).next().is_none() );
}
}
for column_index in matching_matrix.matched_column_indices_in_sequence() {
if ! dj.column(column_index).eq(
jm.column(column_index)
) {
println!("Column index: {:?}", column_index);
println!("DJ column: {:#?}", dj.column(column_index).collect_vec());
println!("JM column: {:#?}", jm.column(column_index).collect_vec());
}
assert!(
dj.column(column_index).eq(
jm.column(column_index)
)
);
}
assert!(
product_is_identity_matrix(
& diff_comb,
& diff_comb_inverse,
indices_to_check.iter().cloned()
)
);
}
}