use std::cmp::Ordering;
use std::collections::HashMap;
use pyo3::exceptions::PyTypeError;
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
use pyo3::types::PyDict;
use pyo3::wrap_pyfunction;
use itertools::Itertools;
use num::rational::Ratio;
use ordered_float::OrderedFloat;
use oat_rust::topology::simplicial::simplices::filtered::SimplexFiltered;
use oat_rust::algebra::chains::barcode::Bar;
use oat_rust::topology::simplicial::from::relation::BoundaryMatrixDowker;
use oat_rust::topology::simplicial::from::relation::dowker_boundary_diagnostic;
use oat_rust::topology::simplicial::simplices::vector::OrderOperatorTwistSimplex;
use oat_rust::algebra::chains::factored::FactoredBoundaryMatrix;
use oat_rust::algebra::chains::factored::factor_boundary_matrix;
use oat_rust::algebra::matrices::operations::umatch::row_major::Umatch;
use oat_rust::algebra::vectors::operations::VectorOperations;
use oat_rust::algebra::matrices::query::ViewRowAscend;
use oat_rust::algebra::matrices::query::ViewColDescend;
use oat_rust::algebra::rings::operator_structs::ring_native::FieldRationalSize;
use oat_rust::algebra::rings::operator_structs::ring_native::DivisionRingNative;
use oat_rust::utilities::iterators::general::RequireStrictAscentWithPanic;
use oat_rust::utilities::order::JudgeOrder;
use oat_rust::utilities::order::{OrderOperatorByKey, OrderOperatorByKeyCutsom};
use oat_rust::utilities::order::ReverseOrder;
use oat_rust::utilities::order::is_sorted_strictly;
use oat_rust::utilities::sequences_and_ordinals::SortedVec;
use crate::export::Export;
type FilVal = OrderedFloat<f64>;
type RingElt = Ratio<isize>;
type Vertex = isize;
type RingOperator = DivisionRingNative< Ratio<isize> >;
type RingElement = Ratio<isize>;
trait Dimension{
fn dimension(&self) -> isize;
}
impl < T > Dimension for Vec< T > {
fn dimension(&self) -> isize {
self.len() as isize - 1
}
}
fn chain_to_dataframe<'py>(
chain: Vec< ( Vec< isize >, Ratio< isize > ) >,
py: Python< 'py >,
) -> Py<PyAny> {
let (simplices,coefficients): (Vec<_>,Vec<_>)
= chain.into_iter().map(|(s,z)| (s, z.export() ) )
.unzip();
let dict = PyDict::new(py);
dict.set_item( "simplex", simplices ).ok().unwrap();
dict.set_item( "coefficient", coefficients ).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::into).ok().unwrap()
}
#[pyclass]
#[derive(Clone)]
pub struct FactoredBoundaryMatrixDowker{
factored: FactoredBoundaryMatrix<
BoundaryMatrixDowker
< Vertex, RingOperator, RingElement >,
DivisionRingNative< RingElt >, OrderOperatorByKeyCutsom<
Vec<Vertex>,
RingElt,
(Vec<Vertex>, RingElt),
OrderOperatorTwistSimplex,
>,
Vec< Vertex >,
( Vec< Vertex >, RingElt ),
Vec< Vec< Vertex > >,
>,
max_homology_dimension: isize,
}
#[pymethods]
impl FactoredBoundaryMatrixDowker {
#[new]
pub fn new(
dowker_simplices: Vec< Vec< Vertex > >,
max_homology_dimension: isize,
) ->
PyResult< Self >
{
let dowker_simplices: Result< Vec<_>, _> = dowker_simplices.into_iter().map(|x| SortedVec::new(x) ).collect();
match dowker_simplices {
Ok( dowker_simplices ) => {
let ring_operator = FieldRationalSize::new();
let boundary_matrix = BoundaryMatrixDowker::new( dowker_simplices, ring_operator );
let row_indices = boundary_matrix.row_indices_in_descending_order(max_homology_dimension).collect_vec();
let factored = factor_boundary_matrix(
boundary_matrix,
ring_operator,
OrderOperatorTwistSimplex::new(),
row_indices,
);
return Ok( FactoredBoundaryMatrixDowker{ factored, max_homology_dimension } )
}, Err( message ) => {
Err(PyErr::new::<PyTypeError, _>( message ))
}
}
}
pub fn max_homology_dimension( &self ) -> isize {
self.max_homology_dimension.clone()
}
pub fn row_indices(
& self,
)
-> Vec< Vec< Vertex > > {
self.factored.row_indices()
}
pub fn row_indices_in_descending_order_beyond_matrix(
& self,
max_simplex_dimension: isize,
)
-> Vec< Vec< Vertex > > {
self.factored.umatch().mapping_ref().row_indices_in_descending_order( max_simplex_dimension ).collect_vec()
}
pub fn homology_indices(
& self,
) -> Vec< Vec< Vertex > >
{
self.factored.indices_harmonic().collect_vec()
}
pub fn jordan_column_for_simplex<'py>(
& self,
keymaj: Vec< Vertex >,
py: Python< 'py >,
) -> Py<PyAny> {
chain_to_dataframe( self.factored.jordan_basis_vector(keymaj).collect_vec(), py)
}
pub fn boundary<'py>(
& self,
index: Vec< Vertex >,
py: Python< 'py >,
) -> Py<PyAny> {
chain_to_dataframe(
self.factored.umatch().mapping_ref().view_minor_descend(index).collect_vec(),
py
)
}
pub fn coboundary<'py>(
& self,
index: Vec< Vertex >,
py: Python< 'py >,
) -> Py<PyAny> {
chain_to_dataframe(
self.factored.umatch().mapping_ref().view_major_ascend(index).collect_vec(),
py
)
}
pub fn get_matched_column(
& self,
index: Vec< Vertex >
) -> Option< Vec<isize> > {
return self.factored.umatch().matching_ref().keymaj_to_keymin(&index).clone()
}
pub fn get_matched_row(
& self,
index: Vec< Vertex >
) -> Option< Vec<isize> > {
return self.factored.umatch().matching_ref().keymin_to_keymaj(&index).clone()
}
pub fn betti<'py>( & self, py: Python<'py> ) -> PyResult< Py<PyAny> > {
let dim_fn = |x: Vec<_>| x.len() as isize - 1;
let cycles = self.factored.cycle_numbers(dim_fn);
let bounds = self.factored.boundary_numbers(dim_fn);
let cycles = (0 .. self.max_homology_dimension + 1 ).map( |x| cycles.get(&x).cloned().unwrap_or(0) ).collect_vec();
let bounds = (0 .. self.max_homology_dimension + 1 ).map( |x| bounds.get(&x).cloned().unwrap_or(0) ).collect_vec();
let bettis = (0 .. self.max_homology_dimension as usize + 1 ).map( |x| cycles[x] - bounds[x] ).collect_vec();
let chains = cycles.iter().cloned().enumerate()
.map(|(degree, c)| match degree == 0 { true=>{c},false=>{c+bounds[degree-1]} }).collect_vec();
let dict = PyDict::new(py);
dict.set_item( "homology", bettis ).ok().unwrap();
dict.set_item( "space of chains", chains ).ok().unwrap();
dict.set_item( "space of cycles", cycles ).ok().unwrap();
dict.set_item( "space of boundaries", bounds ).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
let df: Py<PyAny> = pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::into).ok().unwrap();
let index = df.getattr( py, "index", )?;
index.setattr( py, "name", "dimension", )?;
return Ok(df)
}
pub fn diagnostic( &self, maxdim: isize ) {
let dowker_simplices
= self.factored.umatch().mapping_ref().dowker_simplices().iter().map(|x| x.vec().clone()).collect_vec();
dowker_boundary_diagnostic( dowker_simplices, maxdim );
}
pub fn homology<'py>( &self, py: Python<'py>) -> Py<PyAny> {
let dict = PyDict::new(py);
let harmonic_indices = self.homology_indices();
let mut birth_simplices = Vec::new();
let mut chains = Vec::new();
let mut nnz = Vec::new();
let mut dims = Vec::new();
for x in harmonic_indices {
let chain = self.factored.jordan_basis_vector(x.clone()).collect_vec();
birth_simplices.push( x.clone() );
dims.push( x.dimension() );
nnz.push( chain.len() );
chains.push( chain.export() );
}
dict.set_item( "dimension", dims ).ok().unwrap();
dict.set_item( "birth simplex", birth_simplices ).ok().unwrap();
dict.set_item( "cycle representative", chains ).ok().unwrap();
dict.set_item( "nnz", nnz ).ok().unwrap();
let pandas = py.import("pandas").ok().unwrap();
pandas.call_method("DataFrame", ( dict, ), None)
.map(Into::into).ok().unwrap()
}
#[pyo3(signature = (birth_simplex, problem_type, ))]
pub fn optimize_cycle< 'py >(
&self,
birth_simplex: Vec< isize >,
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: & Vec< isize > | x.len() as isize - 1 ;
let obj_fn = |x: & Vec< isize > | 1.;
let a = |k: & Vec< isize >| self.factored.jordan_basis_vector(k.clone());
let dimension = dim_fn( & birth_simplex );
let b = self.factored.jordan_basis_vector( birth_simplex.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 != &birth_simplex) ) .collect_vec()
}
_ => {
println!("\n\nError: Invaid input supplied for the `problem_type` keyword argument.\nThis message is generated by OAT.\n\n");
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> { Ratio::<isize>::approximate_float(x).unwrap() };
let format_chain = |x: Vec<_>| {
let mut r = x
.into_iter()
.map(|(k,v): (Vec<_>,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 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();
let essential_difference =
x.iter().cloned()
.filter( |x| array_matching.lacks_keymin( &x.0 ) ) .multiply_matrix_packet_minor_descend( self.factored.jordan_basis_matrix_packet() )
.collect_vec();
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 )
}
}
#[pyfunction]
pub fn transpose_listlist( vecvec: Vec<Vec<usize>>) -> PyResult<Vec<Vec<usize>>> {
let ncol = vecvec.iter().flatten().max().unwrap_or(&0).clone() + 1;
let mut transposed = vec![vec![]; ncol];
for (rowind, row) in vecvec.iter().enumerate() {
for colind in row {
transposed[*colind].push(rowind)
}
}
Ok(transposed)
}
pub fn unique_row_indices_helper( vecvec:& Vec<Vec<usize>>) -> Vec<usize> {
let mut uindices = Vec::new();
let mut include;
for (rowind, row) in vecvec.iter().enumerate() {
include = true;
for priorind in uindices.iter() {
if row == &vecvec[*priorind] { include = false; break }
}
if include { uindices.push(rowind) };
}
uindices
}
#[pyfunction]
pub fn unique_row_indices( vecvec: Vec<Vec<usize>>) -> PyResult<Vec<usize>> {
Ok(unique_row_indices_helper( & vecvec))
}
#[pyfunction]
pub fn unique_rows( vecvec: Vec<Vec<usize>>) -> PyResult<Vec<Vec<usize>>> {
let uindices = unique_row_indices_helper(&vecvec);
let urows = uindices.iter().map(|x| vecvec[*x].clone() );
Ok(urows.collect())
}