use num::Signed;
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::wrap_pyfunction;
use pyo3::types::{PyDict, PyTuple};
use oat_rust::algebra::chains::factored::FactoredBoundaryMatrix;
use oat_rust::algebra::chains::factored::factor_boundary_matrix;
use oat_rust::algebra::matrices::display::print_indexed_major_views;
use oat_rust::algebra::matrices::types::third_party::IntoCSR;
use oat_rust::algebra::vectors::entries::{KeyValSet, KeyValNew};
use oat_rust::algebra::matrices::operations::multiply::vector_matrix_multiply_minor_descend_simplified;
use oat_rust::algebra::matrices::{operations::umatch::row_major::Umatch, query::{ViewRowAscend, ViewColDescend, IndicesAndCoefficients}};
use oat_rust::algebra::rings::operator_traits::{Semiring, Ring, DivisionRing};
use oat_rust::algebra::rings::operator_structs::ring_native::{FieldRationalSize, DivisionRingNative};
use oat_rust::utilities::iterators::general::{RequireStrictAscent, RequireStrictAscentWithPanic};
use oat_rust::utilities::order::{JudgePartialOrder, ReverseOrder};
use oat_rust::utilities::order::{OrderOperatorAuto, OrderOperatorByKey, OrderOperatorByKeyCutsom, IntoReverseOrder};
use oat_rust::algebra::vectors::operations::VectorOperations;
use oat_rust::topology::simplicial::simplices::unfiltered::Simplex;
use oat_rust::topology::simplicial::simplices::filtered::OrderOperatorTwistSimplexFiltered;
use oat_rust::algebra::chains::barcode::Bar;
use oat_rust::utilities::optimization::minimize_l1::minimize_l1;
use oat_rust::utilities::order::JudgeOrder;
use oat_rust::utilities::iterators::general::PeekUnqualified;
use oat_rust::topology::simplicial::simplices::filtered::SimplexFiltered;
use oat_rust::topology::simplicial::from::graph_weighted::{ChainComplexVrFiltered, };
use crate::dowker::unique_row_indices;
use crate::export::{Export, ForExport};
use crate::import::import_sparse_matrix;
use crate::simplex_filtered::{SimplexFilteredPy, BarPySimplexFilteredRational, BarcodePySimplexFilteredRational, };
use itertools::Itertools;
use num::rational::Ratio;
use ordered_float::OrderedFloat;
use sprs::{CsMatBase, TriMatBase};
use std::collections::HashMap;
use std::f32::consts::E;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::Cloned;
use std::sync::Arc;
type FilVal = OrderedFloat<f64>;
type RingElt = Ratio<isize>;
#[pyclass]
pub struct FactoredBoundaryMatrixVr{
factored: FactoredBoundaryMatrix<
Arc< ChainComplexVrFiltered<
Arc< CsMatBase< FilVal, usize, Vec<usize>, Vec<usize>, Vec<FilVal> > >,
FilVal,
RingElt,
DivisionRingNative<RingElt>
> >,
DivisionRingNative< RingElt >, OrderOperatorByKeyCutsom<
SimplexFiltered< FilVal >,
RingElt,
(SimplexFiltered<FilVal>, RingElt),
OrderOperatorAuto,
>,
SimplexFiltered<FilVal>,
( SimplexFiltered<FilVal>, RingElt ),
Vec<SimplexFiltered<OrderedFloat<f64>>>,
>
}
#[pymethods]
impl FactoredBoundaryMatrixVr{
#[new]
pub fn new(
py: Python<'_>,
dissimilarity_matrix: & PyAny,
homology_dimension_max: Option< isize >,
)
-> FactoredBoundaryMatrixVr
{
let dissimilarity_matrix = import_sparse_matrix(py, dissimilarity_matrix).ok().unwrap();
let npoints = dissimilarity_matrix.rows();
let dissimilarity_matrix = Arc::new( dissimilarity_matrix );
let dissimilarity_max = OrderedFloat( f64::INFINITY );
let dissimilarity_min = OrderedFloat( - f64::INFINITY );
let ring_operator = FieldRationalSize::new();
let chain_complex_data = ChainComplexVrFiltered::new( dissimilarity_matrix, npoints, dissimilarity_max, dissimilarity_min, ring_operator.clone() );
let chain_complex = Arc::new( chain_complex_data );
let keymaj_vec = chain_complex.cliques_in_order( homology_dimension_max.unwrap_or(1) );
let factored = factor_boundary_matrix(
chain_complex,
ring_operator,
OrderOperatorAuto,
keymaj_vec,
);
return FactoredBoundaryMatrixVr{ factored } }
pub fn indices_boundary_matrix( &self )
-> ForExport<
Vec<
SimplexFiltered<OrderedFloat<f64>>
>
>
{
let mut row_indices = self.factored.row_indices().clone();
let matching = self.factored.umatch().matching_ref();
if row_indices.len() > 0 {
let mut lhs = 0;
let mut lhs_dim = row_indices[lhs].dimension();
for rhs in 0 .. row_indices.len() {
let rhs_dim = row_indices[rhs].dimension();
if lhs_dim < rhs_dim { let subset = &mut row_indices[lhs..rhs];
subset.reverse(); lhs_dim = rhs_dim; lhs = rhs;
}
}
let rhs = row_indices.len();
let subset = &mut row_indices[lhs..rhs];
subset.reverse(); }
if matching.num_pairs() > 0 {
let max_row_dimension = row_indices.last().unwrap().dimension();
let matched_columns = matching.bimap_min_ref().ord_to_val_vec(); let max_col_dimension = matched_columns.last().unwrap().dimension();
if max_row_dimension < max_col_dimension { let mut new_simplices = matched_columns.iter().filter(|x| x.dimension() == max_col_dimension).cloned().collect_vec();
new_simplices.sort(); row_indices.extend(new_simplices); }
}
return row_indices.export()
}
pub fn indices_emerson_escolar(
&self,
birth_simplex: Vec< u16 >,
) ->
ForExport<
Vec<
SimplexFiltered<OrderedFloat<f64>>
>
>
{
let array_matching = self.factored.umatch().matching_ref();
let order_operator = self.factored.umatch().order_operator_major_reverse();
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let obj_fn = |x: &SimplexFiltered<FilVal> | x.filtration().into_inner();
let a = |k: &SimplexFiltered<FilVal>| self.factored.jordan_basis_vector(k.clone());
let diam = self.factored.umatch().mapping_ref().diameter(&birth_simplex).unwrap();
let birth_column = SimplexFiltered{ vertices: birth_simplex.clone(), filtration: diam };
let dimension = birth_column.dimension();
let b = self.factored.jordan_basis_vector( birth_column.clone() );
let column_indices =
self.factored
.indices_escolar_hiraoka( & birth_column, dim_fn, );
return column_indices.export()
}
pub fn boundary_matrix( &self, ) ->
ForExport<
CsMatBase< Ratio<isize>, usize, Vec<usize>, Vec<usize>, Vec<Ratio<isize>> >
>
{
let row_indices = self.indices_boundary_matrix().data;
let inverse_bijection: HashMap<_,_> = row_indices.iter().cloned().enumerate().map(|(x,y)| (y,x) ).collect();
let mapping = self.factored.umatch().mapping_ref();
let shape = (row_indices.len(), row_indices.len());
let mut indices_row = Vec::new();
let mut indices_col = Vec::new();
let mut vals = Vec::new();
for index_row in row_indices.iter().cloned() {
for ( index_col, coefficient ) in mapping.view_major_ascend(index_row.clone()) {
if inverse_bijection.contains_key( &index_col ) { indices_row.push( inverse_bijection[&index_row.clone()].clone() );
indices_col.push( inverse_bijection[&index_col ].clone() );
vals.push( coefficient );
}
}
}
let mat = TriMatBase::from_triplets(shape, indices_row, indices_col, vals);
let mat = mat.to_csr();
return mat.export()
}
pub fn comb_domain( &self, ) ->
ForExport<
CsMatBase< Ratio<isize>, usize, Vec<usize>, Vec<usize>, Vec<Ratio<isize>> >
>
{
let row_indices = self.indices_boundary_matrix().data;
let inverse_bijection: HashMap<_,_> = row_indices.iter().cloned().enumerate().map(|(x,y)| (y,x) ).collect();
let comb = self.factored.umatch().comb_domain();
let shape = (row_indices.len(), row_indices.len());
let mut indices_row = Vec::new();
let mut indices_col = Vec::new();
let mut vals = Vec::new();
for index_row in row_indices.iter().cloned() {
for ( index_col, coefficient ) in comb.view_major_ascend(index_row.clone()) {
if inverse_bijection.contains_key( &index_col ) { indices_row.push( inverse_bijection[&index_row.clone()].clone() );
indices_col.push( inverse_bijection[&index_col ].clone() );
vals.push( coefficient );
}
}
}
let mat = TriMatBase::from_triplets(shape, indices_row, indices_col, vals);
let mat = mat.to_csr();
return mat.export()
}
pub fn jordan_basis_vector(
&self,
column_index: Vec< u16 >,
)
->
ForExport<
Vec<
(
SimplexFiltered< OrderedFloat< f64 > >,
Ratio< isize >,
)
>
>
{
let diam = self.factored.umatch().mapping_ref().diameter(&column_index).unwrap(); let column_index = SimplexFiltered{ vertices: column_index, filtration: diam }; self.factored.jordan_basis_vector( column_index ).collect_vec().export()
}
pub fn homology(
&self,
py: Python<'_>,
return_cycle_representatives: bool,
return_bounding_chains: bool,
)
-> Py<PyAny>
{
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let fil_fn = |x: &SimplexFiltered<FilVal> | x.filtration();
let barcode = oat_rust::algebra::chains::barcode::barcode(
self.factored.umatch(),
self.factored.row_indices().iter().cloned(),
dim_fn,
fil_fn,
return_cycle_representatives,
return_bounding_chains
);
let dict = PyDict::new(py);
dict.set_item( "id",
barcode.bars().iter().map(|x| x.id_number() ).collect_vec() ).ok().unwrap();
dict.set_item( "dimension",
barcode.bars().iter().map(|x| x.birth_column().dimension() ).collect_vec() ).ok().unwrap();
dict.set_item( "birth",
barcode.bars().iter().map(|x| x.birth_f64() ).collect_vec() ).ok().unwrap();
dict.set_item( "death",
barcode.bars().iter().map(|x| x.death_f64() ).collect_vec() ).ok().unwrap();
dict.set_item( "birth simplex",
barcode.bars().iter().map(|x| x.birth_column().vertices() ).collect_vec() ).ok().unwrap();
dict.set_item( "death simplex",
barcode.bars().iter().map(|x| x.death_column().clone().map(|x| x.vertices().clone() ) ).collect_vec()).ok().unwrap();
if return_cycle_representatives {
dict.set_item( "cycle representative",
barcode.bars().iter().map(|x| x.cycle_representative().as_ref().unwrap().clone().export() ).collect_vec() ).ok().unwrap();
dict.set_item( "cycle nnz",
barcode.bars().iter().map(|x| x.cycle_representative().as_ref().map(|x| x.len() ) ).collect_vec() ).ok().unwrap();
}
if return_bounding_chains {
dict.set_item( "bounding chain",
barcode.bars().iter().map(|x| x.bounding_chain().as_ref().map(|x| x.clone().export()) ).collect_vec() ).ok().unwrap();
dict.set_item( "bounding nnz",
barcode.bars().iter().map(|x| x.bounding_chain().as_ref().map(|x| x.len() ) ).collect_vec() ).ok().unwrap();
}
let pandas = py.import("pandas").ok().unwrap();
let df: Py<PyAny> = pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::into).ok().unwrap();
df.call_method( py, "set_index", ( "id", ), None)
.map(Into::into).ok().unwrap()
}
#[pyo3(signature = (birth_simplex, problem_type,))]
pub fn optimize_cycle< 'py >(
&self,
birth_simplex: Vec< u16 >,
problem_type: Option< &str >,
py: Python< 'py >,
) -> Option< Py<PyAny> > {
let array_matching = self.factored.umatch().matching_ref();
let order_operator = self.factored.umatch().order_operator_major_reverse();
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let obj_fn = |x: &SimplexFiltered<FilVal> | x.filtration().into_inner();
let a = |k: &SimplexFiltered<FilVal>| self.factored.jordan_basis_vector(k.clone());
let diam = self.factored.umatch().mapping_ref().diameter(&birth_simplex).unwrap();
let birth_column = SimplexFiltered{ vertices: birth_simplex.clone(), filtration: diam };
let dimension = birth_column.dimension();
let b = self.factored.jordan_basis_vector( birth_column.clone() );
let column_indices = match problem_type.unwrap_or("preserve PH basis") {
"preserve homology class" => {
self.factored
.indices_boundary() .filter(|x| x.dimension()==dimension ) .collect_vec()
}
"preserve homology basis (once)" => {
self.factored
.indices_cycle() .filter(|x| (x.dimension()==dimension) && (x.vertices() != &birth_simplex) ) .collect_vec()
}
"preserve PH basis (once)" => {
let mut filtration_order = |x: &SimplexFiltered<OrderedFloat<f64>>,y: &SimplexFiltered<OrderedFloat<f64>>| { x.filtration().cmp(&y.filtration()) };
self.factored
.indices_escolar_hiraoka_relaxed( & birth_column, dim_fn, filtration_order, ) }
"preserve PH basis" => {
self.factored
.indices_escolar_hiraoka( & birth_column, dim_fn, ) }
_ => {
println!("");
println!("");
println!("Error: problem_type must be one of the following: `preserve homology class`, `preserve homology basis (once)`, `preserve PH basis (once)`, or `preserve PH basis`.");
println!("This error message is generated by OAT.");
println!("");
println!("");
return None
}
};
let optimized = oat_rust::utilities::optimization::minimize_l1::minimize_l1(a, b, obj_fn, column_indices).unwrap();
let to_ratio = |x: f64| -> Ratio<isize> {
let frac = Ratio::<isize>::approximate_float(x);
if frac == None { println!("unconvertible float: {:?}", x); }
frac.unwrap()
};
let format_chain = |x: Vec<_>| {
let mut r = x
.into_iter()
.map(|(k,v): (SimplexFiltered<_>,f64) | (k,to_ratio(v)))
.collect_vec();
r.sort_by( |a,b| order_operator.judge_cmp(a, b) );
r
};
let x = format_chain( optimized.x().clone() );
let cycle_optimal = format_chain( optimized.y().clone() );
let cycle_initial = optimized.b().clone();
let mut bounding_difference =
x.iter()
.cloned()
.filter( |x| array_matching.contains_keymaj( &x.0) ) .map(|(k,v)| (array_matching.keymaj_to_keymin( &k ).clone().unwrap(),v) )
.multiply_matrix_packet_minor_descend( self.factored.jordan_basis_matrix_packet() )
.collect_vec();
bounding_difference.reverse();
let mut essential_difference =
x.iter().cloned()
.filter( |x| array_matching.lacks_keymaj( &x.0 ) ) .multiply_matrix_packet_minor_descend( self.factored.jordan_basis_matrix_packet() )
.collect_vec();
essential_difference.reverse();
let objective_old = optimized.cost_b().clone();
let objective_min = optimized.cost_y().clone();
let ring_operator = self.factored.umatch().ring_operator();
let order_operator = ReverseOrder::new( OrderOperatorByKey::new() );
let y = RequireStrictAscentWithPanic::new(
cycle_optimal.iter().cloned(), order_operator, );
let z = RequireStrictAscentWithPanic::new(
cycle_initial.iter().cloned(), order_operator, );
let ax0 = RequireStrictAscentWithPanic::new(
essential_difference.iter().cloned(), order_operator, );
let ax1
= RequireStrictAscentWithPanic::new(
bounding_difference
.iter()
.cloned()
.multiply_matrix_packet_minor_descend(self.factored.umatch().mapping_ref_packet()), order_operator, );
let ax_plus_z_minus_y
= RequireStrictAscentWithPanic::new(
ax0.peekable()
.add(
ax1.peekable(),
ring_operator,
order_operator,
)
.peekable()
.add(
z.into_iter().peekable(),
ring_operator,
order_operator,
)
.peekable()
.subtract(
y.into_iter().peekable(),
ring_operator,
order_operator,
),
order_operator,
)
.collect_vec();
let dict = PyDict::new(py);
dict.set_item(
"type of chain",
vec![
"initial cycle",
"optimal cycle",
"difference in bounding chains",
"difference in essential chains",
"Ax + z - y"
]
).ok().unwrap();
dict.set_item(
"cost",
vec![
Some(objective_old),
Some(objective_min),
None,
None,
None,
]
).ok().unwrap();
dict.set_item(
"nnz",
vec![
cycle_initial.len(),
cycle_optimal.len(),
bounding_difference.len(),
essential_difference.len(),
ax_plus_z_minus_y.len(),
]
).ok().unwrap();
dict.set_item(
"chain",
vec![
cycle_initial.clone().export(),
cycle_optimal.clone().export(),
bounding_difference.clone().export(),
essential_difference.clone().export(),
ax_plus_z_minus_y.clone().export(),
]
).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
let dict = pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::< Py<PyAny> >::into).ok().unwrap();
let kwarg = vec![("inplace", true)].into_py_dict(py);
dict.call_method( py, "set_index", ( "type of chain", ), Some(kwarg)).ok().unwrap();
return Some( dict )
}
#[pyo3(signature = (birth_simplex,))]
pub fn optimize_bounding_chain< 'py >(
&self,
birth_simplex: Vec< u16 >,
py: Python< 'py >,
) -> Option< Py<PyAny> > {
let array_mapping = self.factored.umatch().mapping_ref_packet();
let array_matching = self.factored.umatch().matching_ref();
let order_operator = self.factored.umatch().order_operator_major_reverse();
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let obj_fn = |x: &SimplexFiltered<FilVal> | x.filtration().into_inner();
let a = |k: &SimplexFiltered<FilVal>| self.factored.jordan_basis_vector(k.clone());
let diam = self.factored.umatch().mapping_ref().diameter(&birth_simplex).unwrap();
let birth_column = SimplexFiltered{ vertices: birth_simplex.clone(), filtration: diam };
if array_matching.lacks_keymaj( &birth_column ) {
println!("\n\nError: the birth simplex provided has no corresponding death simplex.\nThis message is generated by OAT.\n\n");
return None
}
let death_column = array_matching.keymaj_to_keymin( &birth_column ).unwrap();
let death_dimension = death_column.dimension();
let death_filtration = death_column.filtration();
let b = self.factored.jordan_basis_vector( death_column.clone() );
let column_indices
= self.factored.umatch().mapping_ref()
.cliques_in_lexicographic_order_fixed_dimension( death_dimension as isize )
.filter(
|x|
( x.filtration() <= death_filtration )
&&
( ! array_matching.contains_keymin(&x) ) );
let optimized = oat_rust::utilities::optimization::minimize_l1::minimize_l1(a, b, obj_fn, column_indices).unwrap();
let to_ratio = |x: f64| -> Ratio<isize> { Ratio::<isize>::approximate_float(x).unwrap() };
let format_chain = |x: Vec<_>| {
let mut r = x
.into_iter()
.map(|(k,v): (SimplexFiltered<_>,f64) | (k,to_ratio(v)))
.collect_vec();
r.sort_by( |a,b| order_operator.judge_cmp(a, b) );
r
};
let chain_optimal = format_chain( optimized.y().clone() );
let mut chain_initial = optimized.b().clone();
chain_initial.sort();
let objective_old = optimized.cost_b().clone();
let objective_min = optimized.cost_y().clone();
let boundary_initial = chain_initial.iter().cloned().multiply_matrix_packet_minor_descend( array_mapping.clone() ).collect_vec();
let boundary_optimal = chain_optimal.iter().cloned().multiply_matrix_packet_minor_descend( array_mapping.clone() ).collect_vec();
let diff = boundary_initial.iter().cloned().peekable().subtract(
boundary_optimal.iter().cloned().peekable(),
self.factored.umatch().ring_operator(),
self.factored.umatch().order_operator_major_reverse(),
)
.map( |x| x.1.abs() )
.max();
println!("max difference in boundaries: {:?}", diff);
let dict = PyDict::new(py);
dict.set_item(
"type of chain",
vec![
"initial bounding chain",
"optimal bounding chain",
]
).ok().unwrap();
dict.set_item(
"cost",
vec![
Some(objective_old),
Some(objective_min),
]
).ok().unwrap();
dict.set_item(
"nnz",
vec![
chain_initial.len(),
chain_optimal.len(),
]
).ok().unwrap();
dict.set_item(
"chain",
vec![
chain_initial.clone().export(),
chain_optimal.clone().export(),
]
).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
let dict = pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::< Py<PyAny> >::into).ok().unwrap();
let kwarg = vec![("inplace", true)].into_py_dict(py);
dict.call_method( py, "set_index", ( "type of chain", ), Some(kwarg)).ok().unwrap();
return Some( dict )
}
#[pyo3(signature = (birth_simplex, problem_type,))]
pub fn optimize_bounding_chain_kernel< 'py >(
&self,
birth_simplex: Vec< u16 >,
problem_type: Option< &str >,
py: Python< 'py >,
) -> Option< Py<PyAny> > {
let array_mapping = self.factored.umatch().mapping_ref_packet();
let array_matching = self.factored.umatch().matching_ref();
let order_operator = self.factored.umatch().order_operator_major_reverse();
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let obj_fn = |x: &SimplexFiltered<FilVal> | x.filtration().into_inner();
let a = |k: &SimplexFiltered<FilVal>| array_mapping.matrix.view_minor_descend( k.clone() );
let diam = self.factored.umatch().mapping_ref().diameter(&birth_simplex).unwrap();
let birth_column = SimplexFiltered{ vertices: birth_simplex.clone(), filtration: diam };
if array_matching.lacks_keymaj( &birth_column ) {
println!("\n\nError: the birth simplex provided has no corresponding death simplex.\nThis message is generated by OAT.\n\n");
return None
}
let death_column = array_matching.keymaj_to_keymin( &birth_column ).unwrap();
let death_dimension = death_column.dimension();
let death_filtration = death_column.filtration();
let b = self.factored.jordan_basis_vector( death_column.clone() ).collect_vec();
let column_indices = match problem_type.unwrap_or("preserve PH basis") {
"preserve PH basis" => {
Some(
self.factored.umatch().mapping_ref()
.cliques_in_lexicographic_order_fixed_dimension( death_dimension as isize )
.filter(
|x|
( x.filtration() <= death_filtration )
&&
( x != &death_column )
)
)
}
_ => {
println!("");
println!("");
println!("Error: problem_type must be one of the following: `preserve homology class`, `preserve homology basis (once)`, `preserve PH basis (once)`, or `preserve PH basis`.");
println!("This error message is generated by OAT.");
println!("");
println!("");
None
}
}?;
let optimized = oat_rust::utilities::optimization::minimize_l1::minimize_l1_kernel(a, b, obj_fn, column_indices).unwrap();
let to_ratio = |x: f64| -> Ratio<isize> { Ratio::<isize>::approximate_float(x).unwrap() };
let format_chain = |x: Vec<_>| {
let mut r = x
.into_iter()
.map(|(k,v): (SimplexFiltered<_>,f64) | (k,to_ratio(v)))
.collect_vec();
r.sort_by( |a,b| order_operator.judge_cmp(a, b) );
r
};
let x = format_chain( optimized.x().clone() );
let chain_optimal = format_chain( optimized.y().clone() );
let mut chain_initial = optimized.b().clone();
chain_initial.sort();
let objective_old = optimized.cost_b().clone();
let objective_min = optimized.cost_y().clone();
let dict = PyDict::new(py);
dict.set_item(
"type of chain",
vec![
"initial bounding chain",
"optimal bounding chain",
"difference in bounding chains",
]
).ok().unwrap();
dict.set_item(
"cost",
vec![
Some(objective_old),
Some(objective_min),
None,
]
).ok().unwrap();
dict.set_item(
"nnz",
vec![
chain_initial.len(),
chain_optimal.len(),
x.len(),
]
).ok().unwrap();
dict.set_item(
"chain",
vec![
chain_initial.clone().export(),
chain_optimal.clone().export(),
x.clone().export(),
]
).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
let dict = pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::< Py<PyAny> >::into).ok().unwrap();
let kwarg = vec![("inplace", true)].into_py_dict(py);
dict.call_method( py, "set_index", ( "type of chain", ), Some(kwarg)).ok().unwrap();
return Some( dict )
}
pub fn jordan_block_indices< 'py >( &self, py: Python< 'py >, ) -> Py<PyAny> {
let dim_fn = |x: &SimplexFiltered<FilVal> | x.dimension() as isize;
let fil_fn = |x: &SimplexFiltered<FilVal> | x.filtration();
let matching = self.factored.umatch().matching_ref();
let births = self.factored.indices_cycle();
let mut death_simplex_opt: Option< SimplexFiltered<OrderedFloat<f64>> >;
let mut dimension = Vec::new();
let mut birth_vertices = Vec::new();
let mut birth_filtration = Vec::new();
let mut death_vertices_opt = Vec::new();
let mut death_filtration_opt = Vec::new();
let mut lifetime = Vec::new();
for keymaj in self.factored.indices_cycle() {
dimension.push( keymaj.dimension() );
birth_vertices.push( keymaj.vertices().clone() );
birth_filtration.push( keymaj.filtration().into_inner() );
death_simplex_opt = matching.keymaj_to_keymin( & keymaj).map(|x| x.clone()); death_vertices_opt.push( death_simplex_opt.clone().map(|x| x.vertices().clone() ) );
death_filtration_opt.push( death_simplex_opt.map(|x| x.filtration().into_inner() ) );
lifetime.push(
match death_filtration_opt[ death_filtration_opt.len() - 1] {
Some( f ) => { f - birth_filtration[birth_filtration.len()-1] }
None => { std::f64::INFINITY }
}
);
}
let dict = PyDict::new(py);
dict.set_item( "dimension", dimension ).ok().unwrap();
dict.set_item( "lifetime", lifetime ).ok().unwrap();
dict.set_item( "birth simplex", birth_vertices ).ok().unwrap();
dict.set_item( "birth filtration", birth_filtration ).ok().unwrap();
dict.set_item( "death simplex", death_vertices_opt ).ok().unwrap();
dict.set_item( "death filtration", death_filtration_opt ).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::into).ok().unwrap()
}
}
#[cfg(test)]
mod tests {
use pyo3::Python;
use oat_rust::algebra::matrices::types::third_party::IntoCSR;
}