use crate::algebra::matrices::query::{MatrixAlgebra};
use crate::algebra::matrices::types::transpose::OrderAntiTranspose;
use crate::algebra::rings::traits::{ RingOperations, DivisionRingOperations, };
use crate::utilities::iterators::merge::hit::{IteratorsMergedInSortedOrder, hit_bulk_insert, hit_merge_by_predicate};
use crate::algebra::vectors::entries::{KeyValSet, KeyValGet};
use crate::algebra::vectors::operations::{Simplify, Scale, VectorOperations};
use crate::utilities::iterators::general::{TwoTypeIterator};
use crate::utilities::order::is_sorted_strictly;
use std::fmt::Debug;
use std::iter::Iterator;
use derive_getters::{Getters, Dissolve};
#[derive(Debug, Clone, PartialEq, Getters, Dissolve)]
pub struct TriangularSolveForRowVector<
ProblemVector,
Matrix,
>
where
ProblemVector: IntoIterator< Item = Matrix::RowEntry >, Matrix: MatrixAlgebra,
Matrix::RowEntry: KeyValSet,
{
matrix: Matrix, entries_to_eliminate_simplified_heap: Simplify<
IteratorsMergedInSortedOrder<
Scale<
TwoTypeIterator< Matrix::Row, ProblemVector::IntoIter,
>,
Matrix::RingOperator, >,
Matrix::OrderOperatorForRowEntries
>,
Matrix::RingOperator,
>,
}
impl <
ProblemVector,
Matrix,
Index,
>
TriangularSolveForRowVector<
ProblemVector,
Matrix,
>
where
ProblemVector: IntoIterator< Item = Matrix::RowEntry >, Matrix: MatrixAlgebra<
RowIndex = Index,
ColumnIndex = Index,
RingOperator: RingOperations,
>,
Matrix::RowEntry: KeyValSet,
Index: PartialEq,
{
pub fn solve(
b: ProblemVector,
a: Matrix,
)
->
Result<
TriangularSolveForRowVector<
Vec< Matrix::RowEntry >,
Matrix,
>,
(),
>
{
let b: Vec<_> = b.into_iter().collect(); let order_operator = (&a).order_operator_for_row_entries();
if ! is_sorted_strictly(&b, & order_operator) {
println!("\nThe input vector to `TriangularSolveForRowVector::solve( input_vector, matrix ) is not sorted in strictly ascending order by index.");
println!("\nThe vector is:\n{:?}\n", &b);
println!("\nRecall that order may be determined by a customized order operator.\n");
println!("\nThis error message was generated by OAT.\n");
return Err(())
}
let ring_operator = a.ring_operator();
let order_operator = a.order_operator_for_row_entries();
let b = TwoTypeIterator::Version2( b.into_iter() ); let b = b.scale_by( ring_operator.minus_one(), ring_operator.clone() );
let initial_list_of_vectors =std::iter::once( b );
let entries_to_eliminate_simplified_heap
= hit_merge_by_predicate( initial_list_of_vectors,
order_operator,
)
.simplify( ring_operator.clone() );
Ok( TriangularSolveForRowVector{
matrix: a,
entries_to_eliminate_simplified_heap,
} )
}
}
impl <
ProblemVector,
Matrix,
Index,
>
Iterator for
TriangularSolveForRowVector<
ProblemVector,
Matrix,
>
where
Index: PartialEq,
ProblemVector: IntoIterator< Item = Matrix::RowEntry >, Matrix: MatrixAlgebra<
RowIndex = Index,
ColumnIndex = Index,
RingOperator: DivisionRingOperations,
>,
Matrix::RowEntry: KeyValSet,
Index: Debug,
{
type Item = Matrix::RowEntry;
fn next( &mut self ) -> Option<Self::Item> {
if let Some( mut entry_to_eliminate ) = self.entries_to_eliminate_simplified_heap.next() {
let ring_operator = self.matrix.ring_operator();
let mut seed_of_eliminating_iterator = self.matrix.row( & entry_to_eliminate.key() ).into_iter();
let eliminating_entry = seed_of_eliminating_iterator.next().unwrap();
let scale_factor = ring_operator.negate(
ring_operator.divide( entry_to_eliminate.val(), eliminating_entry.val() )
);
let eliminating_iterator
= TwoTypeIterator::Version1( seed_of_eliminating_iterator )
.scale_by( scale_factor.clone(), ring_operator.clone() );
hit_bulk_insert( &mut self.entries_to_eliminate_simplified_heap.unsimplified, vec![eliminating_iterator] );
entry_to_eliminate.set_val( scale_factor );
Some( entry_to_eliminate )
} else {
None
}
}
}
pub struct TriangularSolveForColumnVectorReverse<
ProblemVector,
Matrix,
>
where
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{
antitranspose_solution: TriangularSolveForRowVector<
ProblemVector,
OrderAntiTranspose< Matrix >,
>,
}
impl<ProblemVector, Matrix>
Clone for
TriangularSolveForColumnVectorReverse
< ProblemVector, Matrix >
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix > >: Clone,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{
fn clone(&self) -> Self {
TriangularSolveForColumnVectorReverse{ antitranspose_solution: self.antitranspose_solution.clone() }
}
}
impl<ProblemVector, Matrix>
PartialEq for
TriangularSolveForColumnVectorReverse
< ProblemVector, Matrix >
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix > >: PartialEq,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{
fn eq(&self, other: &Self) -> bool {
self.antitranspose_solution == other.antitranspose_solution
}
}
impl<ProblemVector, Matrix>
Eq for
TriangularSolveForColumnVectorReverse
< ProblemVector, Matrix >
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix > >: Eq,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{}
impl<ProblemVector, Matrix>
PartialOrd for
TriangularSolveForColumnVectorReverse
< ProblemVector, Matrix >
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix > >: PartialOrd,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.antitranspose_solution.partial_cmp(&other.antitranspose_solution)
}
}
impl<ProblemVector, Matrix>
Ord for
TriangularSolveForColumnVectorReverse
< ProblemVector, Matrix >
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix > >: Ord,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra,
Matrix::ColumnEntry: KeyValSet,
{
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.antitranspose_solution.cmp(&other.antitranspose_solution)
}
}
impl <
ProblemVector,
Matrix,
Index,
>
TriangularSolveForColumnVectorReverse<
ProblemVector,
Matrix,
>
where
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra<
RowIndex = Index,
ColumnIndex = Index,
RingOperator: RingOperations,
>,
Matrix::ColumnEntry: KeyValSet,
Index: PartialEq,
{
pub fn solve(
b: ProblemVector,
a: Matrix,
)
->
Result<
TriangularSolveForColumnVectorReverse<
Vec<Matrix::ColumnEntry>, Matrix,
>,
(),
>
{
let b: Vec<_> = b.into_iter().collect(); let order_operator = (&a).order_operator_for_column_entries_reverse();
if ! is_sorted_strictly( &b, &order_operator) {
println!("Error: the entries of b must be sorted in strictly descending order of row index.");
return Err( () );
}
let antitranspose = OrderAntiTranspose::new(a);
match TriangularSolveForRowVector::solve( b, antitranspose, ) {
Err( () ) => {
println!("\n\nThe preceding error message originated when a call to `TriangularSolveForColumnVectorReverse::solve( input_vector, matrix )`");
println!("made a secondary call to `TriangularSolveForRowVector::solve( input_vector, antitranspose_of_matrix )`.");
println!("So the entries of the vector should appear in the REVERSE of the order that the matrix imposes on its row indices.");
return Err( () );
},
Ok( antitranspose_solution ) => {
return Ok( TriangularSolveForColumnVectorReverse{ antitranspose_solution } )
}
}
}
}
impl <
ProblemVector,
Matrix,
Index,
>
Iterator for
TriangularSolveForColumnVectorReverse<
ProblemVector,
Matrix,
>
where
TriangularSolveForRowVector< ProblemVector, OrderAntiTranspose< Matrix >, >: Iterator< Item = Matrix::ColumnEntry >,
ProblemVector: IntoIterator< Item = Matrix::ColumnEntry >, Matrix: MatrixAlgebra< RowIndex=Index, ColumnIndex=Index, RingOperator: DivisionRingOperations >,
Matrix::ColumnEntry: KeyValSet,
{
type Item = Matrix::ColumnEntry;
fn next( &mut self ) -> Option<Self::Item> {
self.antitranspose_solution.next()
}
}
#[cfg(test)]
mod doctring_tests {
use crate::algebra::matrices::operations::MatrixOracleOperations;
#[test]
fn test_docstring_solve_row() {
use crate::algebra::matrices::types::{ vec_of_vec::sorted::VecOfVec, packet::MatrixAlgebraPacket };
use crate::algebra::matrices::operations::solve::triangle::TriangularSolveForRowVector;
use crate::algebra::rings::types::field_prime_order::BooleanField;
use crate::utilities::order::{ OrderOperatorAuto, OrderOperatorByKey };
let matrix_data = VecOfVec::new(
vec![
vec![ (0,true), (1,true), (2,true) ],
vec![ (1,true), (2,true) ],
vec![ (2,true) ],
],
).ok().unwrap();
let matrix = MatrixAlgebraPacket{
matrix: & matrix_data,
ring_operator: BooleanField,
order_operator_for_row_entries: OrderOperatorByKey, order_operator_for_row_indices: OrderOperatorAuto,
order_operator_for_column_entries: OrderOperatorByKey, order_operator_for_column_indices: OrderOperatorAuto,
};
let b = vec![ (0,true), (1,true) ];
let solution = TriangularSolveForRowVector::solve(
b.iter().cloned(), & matrix, ).ok().unwrap();
let product: Vec<_> = matrix.multiply_with_row_vector( solution ).collect();
assert_eq!( product, b );
}
#[test]
fn test_docstring_solve_column() {
use crate::algebra::matrices::types::{ vec_of_vec::sorted::VecOfVec, packet::MatrixAlgebraPacket };
use crate::algebra::matrices::operations::solve::triangle::TriangularSolveForColumnVectorReverse;
use crate::algebra::rings::types::field_prime_order::BooleanField;
use crate::utilities::order::{ OrderOperatorAuto, OrderOperatorByKey };
let matrix_data = VecOfVec::new(
vec![
vec![ (0,true), (1,true), (2,true) ],
vec![ (1,true), (2,true) ],
vec![ (2,true) ],
],
).ok().unwrap();
let matrix = MatrixAlgebraPacket{
matrix: & matrix_data,
ring_operator: BooleanField,
order_operator_for_row_entries: OrderOperatorByKey, order_operator_for_row_indices: OrderOperatorAuto,
order_operator_for_column_entries: OrderOperatorByKey, order_operator_for_column_indices: OrderOperatorAuto,
};
let b = vec![ (2,true), (0,true) ];
let solution = TriangularSolveForColumnVectorReverse::solve(
b.iter().cloned(), & matrix, ).ok().unwrap();
let product: Vec<_> = matrix.multiply_with_column_vector_reverse(solution).collect(); assert_eq!( product, b );
}
}
#[cfg(test)]
mod tests {
use super::*;
use itertools::Itertools;
use crate::{algebra::matrices::types::vec_of_vec::sorted::VecOfVec, algebra::rings::types::field_prime_order::PrimeOrderField};
#[cfg(test)] fn test_triangular_solve< RingOperator >(
matrix: & VecOfVec< usize, RingOperator::Element >,
ring_operator: RingOperator,
matrix_size: usize
)
where RingOperator::Element: PartialEq + Ord + std::fmt::Debug,
RingOperator: DivisionRingOperations + Clone,
{
use crate::{algebra::matrices::types::packet::MatrixAlgebraPacket, utilities::order::{OrderOperatorAuto, OrderOperatorByKey}};
let matrix = MatrixAlgebraPacket{
matrix,
ring_operator: ring_operator.clone(),
order_operator_for_row_entries: OrderOperatorByKey, order_operator_for_row_indices: OrderOperatorAuto,
order_operator_for_column_entries: OrderOperatorByKey, order_operator_for_column_indices: OrderOperatorAuto,
};
for num_nonzeros in [0, 1, 2, 3, matrix_size].iter().map(|x| std::cmp::min(x, &matrix_size) ) {
for vector_support in (0 .. matrix_size).combinations( *num_nonzeros ) {
let mut vec = vector_support
.iter()
.cloned()
.map(|x| ( x, RingOperator::one() ))
.collect_vec();
vec.sort();
let solution = TriangularSolveForRowVector::solve(
vec.iter().cloned(),
& matrix,
)
.ok()
.unwrap()
.peekable()
.simplify( ring_operator.clone() )
.collect_vec();
assert!(
vec.into_iter()
.eq( solution.multiply_self_as_a_row_vector_with_matrix( & matrix ) )
);
}
}
}
#[test]
fn test_triangular_solve_on_specific_matrices() {
use num::rational::Ratio;
use crate::algebra::rings::types::native::RingOperatorForNativeRustNumberType;
type IntegerType = isize;
let modulus = 1049;
let r = Ratio::new;
let q = Ratio::from_integer;
let ring_operator_q = RingOperatorForNativeRustNumberType::< Ratio<IntegerType> >::new();
let ring_operator_p = PrimeOrderField::new(modulus);
let matrix = VecOfVec::new(
vec![
vec![ (0,q(1)), (1,q(1)), ],
vec![ (1,q(1)), ],
],
).ok().unwrap();
test_triangular_solve( &matrix, ring_operator_q, 2 );
let matrix = VecOfVec::new(
vec![
vec![ (0,q(1)), (1,q(-1)), (2,q(3)) ],
vec![ (1,q(-1)), (2,q(4)) ],
vec![ (2,q(5)) ],
],
).ok().unwrap();
test_triangular_solve( &matrix, ring_operator_q, 3 );
let matrix = VecOfVec::new(
vec![
vec![ (0,r(4,1)), (1,r(-6,1)) ],
vec![ (1,r(4,1)), (2,r(-2,1)) ],
vec![ (2,r(7,1)) ],
],
).ok().unwrap();
test_triangular_solve( &matrix, ring_operator_q, 3 );
let matrix = VecOfVec::new(
vec![
vec![ (0,r(2,1)), (1,r(6,1)), (3,r(8,1)), ],
vec![ (1,r(2,1)), (2,r(9,1)), ],
vec![ (2,r(2,1)), (3,r(-4,1)), ],
vec![ (3,r(4,1)), ],
],
).ok().unwrap();
test_triangular_solve( &matrix, ring_operator_q, 4 );
let matrix_size = 20;
let matrix = VecOfVec::random_mod_p_upper_triangular_invertible( matrix_size, modulus );
test_triangular_solve( &matrix, ring_operator_p, matrix_size );
}
}