use crate::algebra::chain_complexes::ChainComplex;
use crate::algebra::matrices::query::{MatrixAlgebra, MatrixOracle, };
use crate::algebra::matrices::operations::MatrixOracleOperations;
use crate::algebra::rings::traits::RingOperations;
use crate::utilities::iterators::general::{symmetric_difference_of_ordered_iterators, IntersectOrderedIterators, TwoTypeIterator};
use crate::utilities::iterators::merge::hit::IteratorsMergedInSortedOrder;
use crate::utilities::iterators::is_sorted::IsSortedBy;
use crate::utilities::sequences_and_ordinals::{SortedVec, CombinationsReverse};
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::{ Cloned, Flatten};
use std::slice::Iter;
use itertools::{Combinations, Dedup, Itertools, KMerge};
use crate::topology::simplicial::boundary::{SimplexBoundaryAscend, SimplexBoundaryDescend};
use crate::topology::simplicial::simplices::vector::{dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter, dimension_0_through_d_simplices_in_dimensionwise_lexicographic_order_iter, dimension_d_simplices_in_lexicographic_order_iter, dimension_d_simplices_in_reverse_lexicographic_order_iter};
use crate::utilities::order::{ LexicographicOrderDominatedByReverselength, OrderOperatorAuto, OrderOperatorAutoReverse, OrderOperatorByKey, OrderOperatorByKeyCustom};
#[derive(Clone)]
pub struct DowkerComplex
< Vertex, RingOperator >
where
Vertex: Clone + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{
relation_rows: Vec< SortedVec< Vertex > >,
relation_columns: Vec< SortedVec< usize > >,
ring_operator: RingOperator,
}
impl < Vertex, RingOperator >
DowkerComplex
< Vertex, RingOperator >
where
Vertex: Clone + Debug + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{
pub fn new( relation_rows: Vec< SortedVec< Vertex > >, ring_operator: RingOperator ) -> Self {
let max_column_index: Option<usize> = relation_rows
.iter()
.map(|simplex| simplex.vec().iter().cloned().map(|x| x.into()) )
.flatten()
.max();
if max_column_index.is_none() {
return DowkerComplex{ relation_rows, relation_columns: Vec::with_capacity(0), ring_operator, }
}
let mut row_counts = vec![ 0usize; max_column_index.unwrap() + 1 ];
for dowker_set in relation_rows.iter() {
for vertex in dowker_set.vec() {
let vertex_usize: usize = vertex.clone().into();
row_counts[ vertex_usize ] += 1; }
}
let mut transpose: Vec<Vec<usize>> = Vec::with_capacity( max_column_index.unwrap() );
for row_count in row_counts { transpose.push( Vec::with_capacity(row_count) ) }
for ( row_index, dowker_set ) in relation_rows.iter().enumerate() {
for vertex in dowker_set.vec() {
let vertex_usize: usize = vertex.clone().into();
let row_of_transpose: &mut Vec<usize> = transpose.get_mut( vertex_usize ).unwrap();
row_of_transpose.push( row_index ); }
}
let mut transpose_safe = Vec::with_capacity( transpose.len() );
for sorted_set in transpose.into_iter() {
transpose_safe.push( SortedVec::new(sorted_set).ok().unwrap() );
}
DowkerComplex{ relation_rows, relation_columns: transpose_safe, ring_operator, }
}
pub fn from_vectors( dowker_simplices: Vec< Vec< Vertex > >, ring_operator: RingOperator ) -> Result< Self, Vec< Vertex > > {
let mut relation_rows = Vec::with_capacity( dowker_simplices.len() );
for ( counter, simplex ) in dowker_simplices.into_iter().enumerate() {
match SortedVec::new(simplex) {
Err( vec ) => {
println!("Error: attempted to create a Dowker boundary matrix from a sequence of simplices, but simplex number {:?}, which is {:?}, is not sorted.", counter, &vec);
return Err( vec );
} Ok(set) => {
relation_rows.push( set );
}
}
}
Ok( DowkerComplex::new( relation_rows, ring_operator ) )
}
pub fn relation_rows( &self ) -> & Vec< SortedVec< Vertex > > { & self.relation_rows }
pub fn relation_columns( &self ) -> & Vec< SortedVec< usize > > { & self.relation_columns }
fn max_vertex( &self ) -> Option< Vertex > {
self.relation_rows
.iter()
.map(|simplex| simplex.vec().iter().cloned() )
.flatten()
.max()
}
pub fn simplices_in_row_reduction_order(
&self,
max_simplex_dimension: isize
) ->
Flatten< std::vec::IntoIter<
TwoTypeIterator<
std::iter::Empty< Vec<Vertex> >,
Dedup<
IteratorsMergedInSortedOrder<
CombinationsReverse< Vertex, &Vec< Vertex > >,
OrderOperatorAutoReverse,
>,
>
>,
>>
{
return dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter(
self.relation_rows(),
max_simplex_dimension
)
}
pub fn simplices_in_lexicographic_order( &self, max_simplex_dimension: isize )
->
Flatten<
std::vec::IntoIter<
TwoTypeIterator<
std::iter::Empty< Vec<Vertex> >,
Dedup< KMerge< Combinations<Cloned<Iter<Vertex>>> > >,
>
>
>
{
dimension_0_through_d_simplices_in_dimensionwise_lexicographic_order_iter( & self.relation_rows, max_simplex_dimension )
}
}
impl < Vertex, RingOperator >
MatrixOracle for
DowkerComplex
< Vertex, RingOperator >
where
Vertex: Clone + Debug + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{
type Coefficient = RingOperator::Element;
type RowIndex = Vec< Vertex >;
type ColumnIndex = Vec< Vertex >;
type RowEntry = ( Vec< Vertex >, Self::Coefficient );
type ColumnEntry = ( Vec< Vertex >, Self::Coefficient );
type Row = DowkerBoundaryMatrixRow< Vertex, RingOperator >;
type RowReverse = DowkerBoundaryMatrixRowReverse< Vertex, RingOperator >;
type Column = SimplexBoundaryAscend< Vertex, RingOperator >;
type ColumnReverse = SimplexBoundaryDescend< Vertex, RingOperator >;
fn row( & self, index: & Self::RowIndex ) -> Self::Row {
DowkerBoundaryMatrixRow::from_vec_of_dowker_sets( index.clone(), & self.relation_rows, self.ring_operator.clone() ).unwrap()
}
fn row_reverse( & self, index: & Self::RowIndex ) -> Self::RowReverse {
DowkerBoundaryMatrixRowReverse::from_vec_of_dowker_sets( index.clone(), & self.relation_rows, self.ring_operator.clone() ).unwrap()
}
fn column( & self, index: & Self::ColumnIndex) -> Self::Column {
SimplexBoundaryAscend::new( index.clone(), self.ring_operator.clone() )
}
fn column_reverse( & self, index: & Self::ColumnIndex) -> Self::ColumnReverse {
SimplexBoundaryDescend::new( index.clone(), self.ring_operator.clone() )
}
fn has_row_for_index( & self, index: & Self::RowIndex ) -> bool {
if ! index.iter().is_sorted_by( |a,b| a.lt(b) ) {
return false
}
IntersectOrderedIterators::new(
index.iter().map(|v| self.relation_columns[ usize::from(v.clone()) ].vec().iter().cloned() )
)
.next()
.is_some()
}
fn has_column_for_index( & self, index: & Self::ColumnIndex) -> bool {
self.has_row_for_index(index)
}
fn structural_nonzero_entry(& self, row: & Self::RowIndex, column: & Self::ColumnIndex ) -> Option< Self::Coefficient > {
if ! self.has_row_for_index(row) {
panic!("Attempted to look up the entry in row {:?}, column {:?} of a Dowker boundary matrix, but {:?} is either an invalid simplex (not sorted or has a repeat entry) or it is not contained in the Dowker compelx", row, column, row );
}
if ! self.has_column_for_index(row) {
panic!("Attempted to look up the entry in row {:?}, column {:?} of a Dowker boundary matrix, but {:?} is either an invalid simplex (not sorted or has a repeat entry) or it is not contained in the Dowker compelx", row, column, column );
}
if column.len() != row.len() + 1 {
return None
}
let mut symmetric_difference = symmetric_difference_of_ordered_iterators( row.iter(), column.iter() );
let first_different_vertex = symmetric_difference.next();
if symmetric_difference.next().is_some() {
return None
} else {
for (counter, vertex) in column.iter().enumerate() {
if Some(vertex) == first_different_vertex {
let coefficient = self.ring_operator.minus_one_to_power( counter );
return Some( coefficient )
}
}
}
panic!("Error retreiving structural nonzero entry.");
}
}
impl < Vertex, RingOperator >
MatrixAlgebra for
DowkerComplex
< Vertex, RingOperator >
where
Vertex: Clone + Debug + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{
type RingOperator = RingOperator;
type OrderOperatorForRowEntries = OrderOperatorByKeyCustom<LexicographicOrderDominatedByReverselength>;
type OrderOperatorForRowIndices = LexicographicOrderDominatedByReverselength;
type OrderOperatorForColumnEntries = OrderOperatorByKeyCustom<LexicographicOrderDominatedByReverselength>;
type OrderOperatorForColumnIndices = LexicographicOrderDominatedByReverselength;
fn ring_operator( &self ) -> Self::RingOperator {
self.ring_operator.clone()
}
fn order_operator_for_row_entries( &self ) -> Self::OrderOperatorForRowEntries {
OrderOperatorByKeyCustom::new(LexicographicOrderDominatedByReverselength::new())
}
fn order_operator_for_row_indices( &self ) -> Self::OrderOperatorForRowIndices {
LexicographicOrderDominatedByReverselength::new()
}
fn order_operator_for_column_entries( &self ) -> Self::OrderOperatorForColumnEntries {
OrderOperatorByKeyCustom::new(LexicographicOrderDominatedByReverselength::new())
}
fn order_operator_for_column_indices( &self ) -> Self::OrderOperatorForColumnIndices {
LexicographicOrderDominatedByReverselength::new()
}
}
impl < Vertex, RingOperator >
MatrixOracleOperations for
DowkerComplex
< Vertex, RingOperator >
where Vertex: Clone + Debug + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{}
impl < Vertex, RingOperator >
ChainComplex for
DowkerComplex
< Vertex, RingOperator >
where
Vertex: Clone + Debug + Ord + Hash,
RingOperator: Clone + RingOperations,
usize: From< Vertex >,
{
type BasisVectorIndicesIterable = Vec< Vec< Vertex > >;
fn basis_vector_indices_for_dimension( &self, dimension: isize ) -> Self::BasisVectorIndicesIterable {
dimension_d_simplices_in_lexicographic_order_iter(
& self.relation_rows,
dimension
).collect()
}
fn dimension_for_basis_vector_with_index( &self, index: & Self::RowIndex ) -> Result<isize, Self::RowIndex> {
if ! self.has_row_for_index(index) {
return Err( index.clone() )
}
Ok( (index.len() as isize) - 1 )
}
}
#[derive(Clone, Debug)]
pub struct DowkerBoundaryMatrixRowReverse< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Ord + Clone,
{
next_cofacet_opt: Option< Vec< Vertex > >,
next_coefficient: RingOperator::Element,
vertices_to_insert: Vec< Vertex >, retrieval_locus: usize, insertion_locus: usize, ring_operator: RingOperator,
}
impl < Vertex, RingOperator >
DowkerBoundaryMatrixRowReverse
< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Clone + Debug + Hash + Ord,
{
pub fn from_vec_of_dowker_sets( facet: Vec< Vertex >, relation_rows: &Vec< SortedVec< Vertex > >, ring_operator: RingOperator ) -> Result< Self, Vec< Vertex > > {
if facet.is_empty() {
return Ok( DowkerBoundaryMatrixRowReverse{
next_cofacet_opt: None,
next_coefficient: RingOperator::one(), vertices_to_insert: vec![], retrieval_locus: 0, insertion_locus: 0, ring_operator, } )
}
let facet = SortedVec::new( facet ); if let Err(vec) = facet {
println!("Error: attempted to compute the coboundary of simplex {:?}, but the vertices of the simplex are not sorted in strictly ascending order", &vec );
return Err( vec );
}
let facet = facet.unwrap(); let vertices_to_insert: Vec< Vertex > = relation_rows
.iter()
.filter( |x| x.contains_subset( &facet ) )
.map(|x| x.vec().iter())
.kmerge()
.dedup()
.filter(|x| ! facet.contains(x) )
.cloned()
.collect();
if vertices_to_insert.is_empty() {
return Ok( DowkerBoundaryMatrixRowReverse{
next_cofacet_opt: None,
next_coefficient: RingOperator::one(), vertices_to_insert: vec![], retrieval_locus: 0, insertion_locus: 0, ring_operator, } )
}
let mut coefficient = ring_operator.minus_one_to_power( facet.len() );
let mut next_cofacet = facet.into_vec();
let mut insertion_locus = next_cofacet.len();
let retrieval_locus = vertices_to_insert.len() - 1;
let inserted_vertex = vertices_to_insert[retrieval_locus].clone();
while ( insertion_locus > 0 )
&&
( inserted_vertex < next_cofacet[ insertion_locus - 1 ] ) {
insertion_locus -= 1;
coefficient = ring_operator.negate( coefficient );
}
next_cofacet.insert( insertion_locus, inserted_vertex );
Ok( DowkerBoundaryMatrixRowReverse{
next_cofacet_opt: Some( next_cofacet ),
next_coefficient: coefficient,
vertices_to_insert, retrieval_locus, insertion_locus, ring_operator,
} )
}
}
impl < Vertex, RingOperator >
Iterator for
DowkerBoundaryMatrixRowReverse
< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Ord + Clone,
{
type Item = (Vec<Vertex>, RingOperator::Element);
fn next( &mut self ) -> Option< Self::Item >{
match self.next_cofacet_opt {
None => { None }
Some( ref mut next_cofacet ) => {
let return_value = ( next_cofacet.clone(), self.next_coefficient.clone() );
if self.retrieval_locus > 0 {
self.retrieval_locus -= 1;
let inserted_vertex = self.vertices_to_insert[ self.retrieval_locus ].clone();
while ( self.insertion_locus > 0 )
&&
( inserted_vertex < next_cofacet[ self.insertion_locus - 1 ] ) {
next_cofacet[ self.insertion_locus ] = next_cofacet[ self.insertion_locus - 1 ].clone();
self.insertion_locus -= 1;
self.next_coefficient = self.ring_operator.negate( self.next_coefficient.clone() );
}
next_cofacet[ self.insertion_locus ] = inserted_vertex;
}
else {
self.next_cofacet_opt = None;
}
Some( return_value )
}
}
}
}
#[derive(Clone, Debug)]
pub struct DowkerBoundaryMatrixRow< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Ord + Clone,
{
next_cofacet_opt: Option< Vec< Vertex > >,
next_coefficient: RingOperator::Element,
vertices_to_insert: Vec< Vertex >, retrieval_locus: usize, insertion_locus: usize, ring_operator: RingOperator,
}
impl < Vertex, RingOperator >
DowkerBoundaryMatrixRow
< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Clone + Debug + Hash + Ord,
{
pub fn from_vec_of_dowker_sets( facet: Vec< Vertex >, relation_rows: &Vec< SortedVec< Vertex > >, ring_operator: RingOperator ) -> Result< Self, Vec< Vertex > > {
if facet.is_empty() {
return Ok( DowkerBoundaryMatrixRow{
next_cofacet_opt: None,
next_coefficient: RingOperator::one(), vertices_to_insert: vec![], retrieval_locus: 0, insertion_locus: 0, ring_operator, } )
}
let facet = SortedVec::new( facet ); if let Err(vec) = facet {
println!("Error: attempted to compute the coboundary of simplex {:?}, but the vertices of the simplex are not sorted in strictly ascending order", &vec );
return Err( vec );
}
let facet = facet.unwrap(); let vertices_to_insert: Vec< Vertex > = relation_rows
.iter()
.filter( |x| x.contains_subset( &facet ) )
.map(|x| x.vec().iter())
.kmerge()
.dedup()
.filter(|x| ! facet.contains(x) )
.cloned()
.collect();
if vertices_to_insert.is_empty() {
return Ok( DowkerBoundaryMatrixRow{
next_cofacet_opt: None,
next_coefficient: RingOperator::one(),
vertices_to_insert: vec![],
retrieval_locus: 0,
insertion_locus: 0,
ring_operator,
} )
}
let mut coefficient = RingOperator::one();
let mut next_cofacet = facet.into_vec();
let mut insertion_locus = 0;
let retrieval_locus = 0;
let inserted_vertex = vertices_to_insert[retrieval_locus].clone();
while ( insertion_locus < next_cofacet.len() )
&&
( inserted_vertex > next_cofacet[ insertion_locus ] ) {
insertion_locus += 1;
coefficient = ring_operator.negate( coefficient );
}
next_cofacet.insert( insertion_locus, inserted_vertex );
Ok( DowkerBoundaryMatrixRow{
next_cofacet_opt: Some( next_cofacet ),
next_coefficient: coefficient,
vertices_to_insert, retrieval_locus, insertion_locus, ring_operator,
} )
}
}
impl < Vertex, RingOperator >
Iterator for
DowkerBoundaryMatrixRow
< Vertex, RingOperator >
where
RingOperator: RingOperations,
Vertex: Ord + Clone,
{
type Item = (Vec<Vertex>, RingOperator::Element);
fn next( &mut self ) -> Option< Self::Item >{
match self.next_cofacet_opt {
None => { None }
Some( ref mut next_cofacet ) => {
let return_value = ( next_cofacet.clone(), self.next_coefficient.clone() );
if self.retrieval_locus + 1 < self.vertices_to_insert.len() {
self.retrieval_locus += 1;
let inserted_vertex = self.vertices_to_insert[ self.retrieval_locus ].clone();
while ( self.insertion_locus + 1 < next_cofacet.len() )
&&
( inserted_vertex > next_cofacet[ self.insertion_locus + 1 ] ) {
next_cofacet[ self.insertion_locus ] = next_cofacet[ self.insertion_locus +1 ].clone();
self.insertion_locus += 1;
self.next_coefficient = self.ring_operator.negate( self.next_coefficient.clone() );
}
next_cofacet[ self.insertion_locus ] = inserted_vertex;
}
else {
self.next_cofacet_opt = None;
}
Some( return_value )
}
}
}
}
pub fn sideways_ladder_edges( offset_from_left: usize, number_of_holes: usize ) -> Vec< Vec< usize > > {
let mut hyperedges = Vec::with_capacity( 1 + 3 * number_of_holes );
for p in offset_from_left .. offset_from_left + number_of_holes + 1 {
hyperedges.push( vec![2*p, 2*p + 1] )
}
for p in offset_from_left .. offset_from_left + number_of_holes {
hyperedges.push( vec![ 2*p, 2*(p+1) ] );
hyperedges.push( vec![ 2*p + 1, 2*(p+1) + 1 ] );
}
return hyperedges
}
use crate::algebra::matrices::debug::{matrix_oracle_is_internally_consistent, matrix_order_operators_are_internally_consistent };
use crate::algebra::rings::types::field_prime_order::PrimeOrderField;
pub fn validate_dowker_boundary_matrix<Vertex>(
relation_rows: Vec<SortedVec<Vertex>>,
max_dim: isize,
)
where
Vertex: Clone + Hash + Debug + Ord,
usize: From< Vertex >,
{
let ring_operator = PrimeOrderField::new(47);
let boundary_matrix = DowkerComplex::new( relation_rows.clone(), ring_operator );
let mut sorted_row_indices: Vec<_> = boundary_matrix.simplices_in_row_reduction_order( max_dim ).collect();
( &mut sorted_row_indices ).reverse();
let mut sorted_column_indices: Vec<_> = boundary_matrix.simplices_in_row_reduction_order( max_dim + 1 ).collect();
( &mut sorted_column_indices ).reverse();
assert!(
matrix_oracle_is_internally_consistent(
& boundary_matrix,
sorted_row_indices.clone(),
sorted_column_indices.clone(),
)
);
assert!(
matrix_order_operators_are_internally_consistent(
& boundary_matrix,
sorted_row_indices.clone(),
sorted_column_indices.clone(),
)
.is_ok()
);
}
#[cfg(test)]
mod tests {
use crate::utilities::random::random_sequences;
use super::*;
#[test]
fn test_dowker_boundary_small(){
let relation_rows: Vec<Vec<usize>> =
vec![
vec![0,1,2],
vec![0,3],
vec![1,3],
vec![2,3]
];
let relation_rows = relation_rows.into_iter()
.map(|v| SortedVec::new(v).ok().unwrap() )
.collect::<Vec<_>>();
validate_dowker_boundary_matrix( relation_rows, 2 );
}
#[test]
fn test_dowker_boundary_big(){
for _ in 0..10 {
let relation_rows = random_sequences(7, (0..7).step_by(2), 0.1 );
let _verbose = false;
validate_dowker_boundary_matrix( relation_rows, 2 );
}
}
}
#[cfg(test)]
mod docstring_tests {
use crate::topology::simplicial::simplices::vector::{dimension_d_simplices_in_lexicographic_order_iter, dimension_d_simplices_in_reverse_lexicographic_order_iter, dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter};
#[test]
fn docstring_test_dowker_homology() {
use itertools::Itertools;
use crate::topology::simplicial::from::relation::DowkerComplex;
use crate::algebra::matrices::operations::umatch::differential::DifferentialUmatch;
use crate::algebra::rings::types::field_prime_order::PrimeOrderField;
use crate::utilities::sequences_and_ordinals::SortedVec;
let min_homology_dimension = 0;
let max_homology_dimension = 2;
let ring_operator = PrimeOrderField::new(3);
let dowker_simplices: Vec< SortedVec< usize > >
= vec![
vec![0,1,2],
vec![0,3],
vec![1,3],
vec![2,3]
]
.into_iter()
.map( |x| SortedVec::new(x).unwrap() )
.collect_vec();
let boundary_matrix = DowkerComplex::new( dowker_simplices.clone(), ring_operator );
let _triangles_ascending_order = dimension_d_simplices_in_lexicographic_order_iter( &dowker_simplices, 2);
let _triangles_descending_order = dimension_d_simplices_in_reverse_lexicographic_order_iter( &dowker_simplices, 2);
let _row_indices = boundary_matrix.simplices_in_row_reduction_order( max_homology_dimension );
let row_indices = dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter(&dowker_simplices, max_homology_dimension);
let factored = DifferentialUmatch::new(
boundary_matrix,
min_homology_dimension,
max_homology_dimension,
);
let homology_dimensions = factored.betti_numbers();
for dim in 0 .. 2 {
println!(
"The betti number in dimension {:?} is {:?}.",
dim, homology_dimensions .get( & dim ) .unwrap_or( & 0) );
}
println!();
println!(
"The following are basis vectors for homology in dimensions 0 through {:?}",
max_homology_dimension,
);
for (cycle_number, cycle) in factored.homology_basis().enumerate() {
let cycle: Vec<_> = cycle.collect();
println!("Basis vector {:?} = {:?}", cycle_number, cycle);
}
}
#[test]
fn test() {
use crate::topology::simplicial::from::relation::DowkerBoundaryMatrixRow;
use crate::algebra::rings::types::field_prime_order::PrimeOrderField;
use crate::utilities::sequences_and_ordinals::SortedVec;
let ring_operator = PrimeOrderField::new(3);
let simplex = vec![1,3];
let relation_rows = vec![ SortedVec::from_iter( vec![0,1,2,3,4] ).unwrap() ];
let coboundary = DowkerBoundaryMatrixRow::from_vec_of_dowker_sets( simplex, &relation_rows, ring_operator ).unwrap();
itertools::assert_equal( coboundary, vec![ (vec![0,1,3], 1), (vec![1,2,3], 2), (vec![1,3,4], 1) ]);
}
#[test]
fn doc_test_sideways_ladder() {
use crate::topology::simplicial::from::relation::sideways_ladder_edges;
let number_of_holes = 1;
let offset_from_left = 1;
let mut edges = sideways_ladder_edges(number_of_holes, offset_from_left);
edges.sort();
let ground_truth = vec![
vec![2,3],
vec![2,4],
vec![3,5],
vec![4,5],
];
assert_eq!( edges, ground_truth );
}
#[test]
fn doc_test_misc_REDUNDANT_OK_TO_DELETE() {
use crate::algebra::matrices::operations::umatch::differential::DifferentialUmatch;
use crate::algebra::rings::types::field_prime_order::PrimeOrderField;
use crate::topology::simplicial::from::relation::DowkerComplex;
use crate::topology::simplicial::simplices::vector::{dimension_d_simplices_in_lexicographic_order_iter, dimension_d_simplices_in_reverse_lexicographic_order_iter};
use crate::topology::simplicial::simplices::vector::dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter;
use crate::utilities::sequences_and_ordinals::SortedVec;
use itertools::Itertools;
let min_homology_dimension = 0;
let max_homology_dimension = 2;
let ring_operator = PrimeOrderField::new(3);
let dowker_simplices: Vec<SortedVec<usize>>
= vec![
vec![0,1,2],
vec![0,3],
vec![1,3],
vec![2,3]
]
.into_iter()
.map( |x| SortedVec::new(x).unwrap() ) .collect_vec();
let boundary_matrix = DowkerComplex::new( dowker_simplices.clone(), ring_operator.clone() );
let triangles_ascending_order = dimension_d_simplices_in_lexicographic_order_iter( &dowker_simplices, 2);
let triangles_descending_order = dimension_d_simplices_in_reverse_lexicographic_order_iter( &dowker_simplices, 2);
let row_indices = boundary_matrix.simplices_in_row_reduction_order( max_homology_dimension );
let row_indices = dimension_0_through_d_simplices_in_ascending_dimension_descending_lexicographic_order_iter(&dowker_simplices, max_homology_dimension);
let factored = DifferentialUmatch::new(
boundary_matrix,
min_homology_dimension,
max_homology_dimension,
);
let homology_dimensions = factored.betti_numbers();
for dim in 0 .. 2 {
println!(
"The betti number in dimension {:?} is {:?}.",
dim, homology_dimensions .get( & dim ) .unwrap_or( & 0) );
}
println!("");
println!(
"The following are basis vectors for homology in dimensions 0 through {:?}",
max_homology_dimension,
);
for (cycle_number, cycle) in factored.homology_basis().enumerate() {
let cycle: Vec<_> = cycle.collect();
println!("Basis vector {:?} = {:?}", cycle_number, cycle);
}
}
}