/* Copyright (c) 1997-2006 Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany) http://www.math.tu-berlin.de/polymake, mailto:polymake@math.tu-berlin.de This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version: http://www.gnu.org/licenses/gpl.txt. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ #ifndef _POLYMAKE_CHAIN_COMPLEX_H #define _POLYMAKE_CHAIN_COMPLEX_H "$Project: polymake $$Id: ChainComplex.h 7315 2006-04-02 21:37:53Z gawrilow $" #include #include #include #include #include #ifdef POLY_DEBUG # include # include #endif namespace polymake { namespace topaz { template struct Homology { typedef std::list< std::pair > torsion_list; torsion_list torsion; int betti_number; }; template struct CycleGroup { typedef SparseMatrix coeff_matrix; typedef Array< Set > face_list; coeff_matrix coeffs; face_list faces; }; } } namespace pm { template struct spec_object_traits< polymake::topaz::Homology > : spec_object_traits { typedef polymake::topaz::Homology me; typedef cons elements; template static void visit_elements(Me& x, Visitor& v) { v << x.torsion << x.betti_number; } }; template struct spec_object_traits< polymake::topaz::CycleGroup > : spec_object_traits { typedef polymake::topaz::CycleGroup me; typedef cons elements; template static void visit_elements(Me& x, Visitor& v) { v << x.coeffs << x.faces; } }; } namespace polymake { namespace topaz { template class nothing_logger : public dummy_companion_logger { public: explicit nothing_logger(const nothing*, const nothing*, const nothing* =0, const nothing* =0) { } }; template class TransposedLogger< nothing_logger > : public dummy_companion_logger { }; template class elimination_logger { protected: typedef SparseMatrix matrix; matrix *Left, *Right_inv; // U is always unimodular, only the sign of det(U) can vary static bool det_pos(const SparseMatrix2x2& U) { return U.a_ii*U.a_jj > U.a_ij*U.a_ji; } static SparseMatrix2x2 inv(const SparseMatrix2x2& U) { return det_pos(U) ? SparseMatrix2x2(U.i, U.j, U.a_jj, -U.a_ij, -U.a_ji, U.a_ii) : SparseMatrix2x2(U.i, U.j, -U.a_jj, U.a_ij, U.a_ji, -U.a_ii); } static SparseMatrix2x2 inv(const Transposed< SparseMatrix2x2 >& U) { return det_pos(U) ? SparseMatrix2x2(U.i, U.j, U.a_jj, -U.a_ji, -U.a_ij, U.a_ii) : SparseMatrix2x2(U.i, U.j, -U.a_jj, U.a_ji, U.a_ij, -U.a_ii); } public: explicit elimination_logger(matrix *Left_arg, matrix *Right_inv_arg) : Left(Left_arg), Right_inv(Right_inv_arg) { } void from_left(const SparseMatrix2x2& U) { Left->multiply_from_left(U); } void from_right(const SparseMatrix2x2& U) { if (Right_inv) Right_inv->multiply_from_left(inv(U)); } }; template class smith_normal_form_logger : protected elimination_logger { typedef elimination_logger _super; protected: typedef typename _super::matrix matrix; matrix *Left2, *Right; public: smith_normal_form_logger(matrix *Left_arg, matrix *Left2_arg, matrix *Right_arg, matrix *Right_inv_arg) : elimination_logger(Left_arg, Right_inv_arg), Left2(Left2_arg), Right(Right_arg) { } void from_left(const SparseMatrix2x2& U) { _super::from_left(U); Left2->multiply_from_left(U); } void from_right(const SparseMatrix2x2& U) { _super::from_right(U); if (Right) Right->multiply_from_right(U); } }; template class TransposedLogger< smith_normal_form_logger > : protected smith_normal_form_logger { typedef smith_normal_form_logger _super; protected: TransposedLogger(); ~TransposedLogger(); public: void from_left(const SparseMatrix2x2& U) { if (this->Right_inv) this->Right_inv->multiply_from_left(T(_super::inv(U))); if (this->Right) this->Right->multiply_from_right(T(U)); } void from_right(const SparseMatrix2x2& U) { this->Left->multiply_from_left(T(U)); this->Left2->multiply_from_left(T(U)); } }; template class ChainComplex_iterator { public: typedef std::forward_iterator_tag iterator_category; typedef Homology value_type; typedef const value_type& reference; typedef const value_type* pointer; typedef ptrdiff_t difference_type; typedef ChainComplex_iterator iterator; typedef ChainComplex_iterator const_iterator; typedef SparseMatrix matrix; typedef typename pm::if_else::type companion_type; ChainComplex_iterator() { } ChainComplex_iterator(const BaseComplex& complex_arg, int d_start_arg, int d_end_arg) : complex(&complex_arg), d_cur(dual ? d_end_arg : d_start_arg+1), d_end(dual ? d_start_arg+1 : d_end_arg) { if (!at_end()) { first_step(); operator++(); } } reference operator* () const { return hom_cur; } pointer operator-> () const { return &hom_cur; } iterator& operator++ () { dual ? ++d_cur : --d_cur; if (!at_end()) { hom_cur=hom_next; step(); } return *this; } const iterator operator++ (int) { iterator copy=*this; operator++(); return copy; } bool operator== (const iterator& it) const { return d_cur==it.d_cur; } bool operator!= (const iterator& it) const { return !operator==(it); } bool at_end() const { return dual ? d_cur>d_end : d_cur hom_cur, hom_next; int rank_cur, rank_next; Bitset elim_rows, elim_cols; matrix delta; static const int R_inv_prev=0, L=1, LxR_prev=2, R_inv=3, companion_set=4; typedef matrix matrix_array[companion_set]; struct nothing_array : nothing { const nothing& operator[] (int) const { return *this; } nothing* operator+ (int) { return this; } }; typedef typename pm::if_else::type companion_array; companion_array companions; companion_type Cycles; typedef typename pm::if_else, nothing_logger >::type snf_logger_type; typedef typename pm::if_else, nothing_logger >::type elim_logger_type; void first_step(); void step(bool first=false); void init_companion(const nothing*, int) { } void init_companion(matrix* M, int n) { *M=unit_matrix(n); } void calculate_cycles(const nothing&) { } void calculate_cycles(matrix&); void prepare_LxR_prev(const nothing*) { } void prepare_LxR_prev(matrix*); void compress_torsion(); #ifdef POLY_DEBUG SparseMatrix r_delta, r_delta_next; void debug1(int d, const matrix& _delta, const SparseMatrix& _r_delta, const matrix* _companions) const { const SparseMatrix r_L(_companions[L]), r_R_inv(_companions[R_inv]); cout << "elim[" << d << "]:\n" << std::setw(3) << _delta; if (r_L * _r_delta * inv(r_R_inv) != _delta) cout << "WRONG!\n"; cout << "L:\n" << std::setw(3) << _companions[L]; if (! abs_equal(det(r_L), 1)) cout << "NOT UNIMODULAR!\n"; cout << "R_inv:\n" << std::setw(3) << _companions[R_inv]; if (! abs_equal(det(r_R_inv), 1)) cout << "NOT UNIMODULAR!\n"; cout << endl; } void debug2(const matrix*) const { cout << "cancel cols[" << d_cur << "]: " << elim_rows << endl; const SparseMatrix r_L(companions[L]), r_R_inv(companions[R_inv]); if (r_L * r_delta * inv(r_R_inv) != delta) cout << "WRONG!\n"; cout << "R_inv:\n" << std::setw(3) << companions[R_inv]; if (! abs_equal(det(r_R_inv), 1)) cout << "NOT UNIMODULAR!\n"; } void debug3(const matrix*) const { const SparseMatrix r_L(companions[L]), r_R_inv(companions[R_inv]); if (r_L * r_delta * inv(r_R_inv) != delta) cout << "WRONG!\n"; cout << "L:\n" << std::setw(3) << companions[L]; if (! abs_equal(det(r_L), 1)) cout << "NOT UNIMODULAR!\n"; cout << "R_inv:\n" << std::setw(3) << companions[R_inv]; if (! abs_equal(det(r_R_inv), 1)) cout << "NOT UNIMODULAR!\n"; cout << "LxR_prev:\n" << std::setw(3) << companions[LxR_prev]; } void debug1(int d, const matrix& _delta, const SparseMatrix& _r_delta, const nothing*) const { cout << "elim[" << d << "]:\n" << std::setw(3) << _delta << endl; } void debug2(const nothing*) const { cout << "cancel cols[" << d_cur << "]: " << elim_rows << endl; } void debug3(const nothing*) const { } #endif }; } } namespace pm { template struct check_iterator_feature, end_sensitive> : True { }; } namespace polymake { namespace topaz { template void ChainComplex_iterator::first_step() { if (dual) { delta=T(complex->template boundary_matrix(d_cur)); } else { delta=complex->template boundary_matrix(d_cur); } #ifdef POLY_DEBUG cout << (dual ? "dual delta[" : "delta[") << d_cur << "]:\n" << std::setw(3) << delta << endl; r_delta=SparseMatrix(delta); #endif init_companion(companions+L, delta.rows()); init_companion(companions+R_inv, delta.cols()); elim_logger_type elim_logger(companions+L, companions+R_inv); rank_cur=eliminate_ones(delta, elim_rows, elim_cols, elim_logger); companions[LxR_prev]=companions[L]; #ifdef POLY_DEBUG debug1(d_cur, delta, r_delta, companions+0); #endif step(true); } template void ChainComplex_iterator::step(bool first) { companion_array companions_next; matrix delta_next; companion_type *cLxR_prev=0, *cR_inv=0; int rank_next=0; if (d_cur!=d_end) { if (dual) { delta_next=T(complex->template boundary_matrix(d_cur+1)); } else { delta_next=complex->template boundary_matrix(d_cur-1); } #ifdef POLY_DEBUG cout << (dual ? "dual delta[" : "delta[") << (dual ? d_cur+1 : d_cur-1) << "]:\n" << std::setw(3) << delta_next << endl; r_delta_next=SparseMatrix(delta_next); cout << "cancel rows[" << (dual ? d_cur+1 : d_cur-1) << "]: " << elim_cols << endl; #endif delta_next.minor(elim_cols,All).clear(); init_companion(companions_next+LxR_prev, delta_next.rows()); init_companion(companions_next+R_inv, delta_next.cols()); elim_logger_type elim_logger(companions+R_inv, companions_next+R_inv); rank_next=eliminate_ones(delta_next, elim_rows, elim_cols, elim_logger); companions_next[L]=companions[R_inv]; #ifdef POLY_DEBUG debug1((dual ? d_cur+1 : d_cur-1), delta_next, r_delta_next, companions_next+0); #endif delta.minor(All,elim_rows).clear(); #ifdef POLY_DEBUG debug2(companions+0); #endif cLxR_prev=companions_next+LxR_prev; cR_inv=companions+R_inv; } snf_logger_type snf_logger(companions+L, companions+LxR_prev, cLxR_prev, cR_inv); rank_cur+=Smith_normal_form(delta, hom_next.torsion, snf_logger); #ifdef POLY_DEBUG cout << "snf[" << d_cur << "]:\n" << std::setw(3) << delta; if (cR_inv) debug3(companions+0); #endif hom_next.betti_number=-rank_cur; if (!first) { prepare_LxR_prev(cLxR_prev); hom_cur.betti_number+=delta.rows()-rank_cur; calculate_cycles(Cycles); compress_torsion(); } delta=delta_next; rank_cur=rank_next; #ifdef POLY_DEBUG r_delta=r_delta_next; #endif companions[R_inv_prev]=companions[R_inv]; companions[L]=companions_next[L]; companions[LxR_prev]=companions_next[LxR_prev]; companions[R_inv]=companions_next[R_inv]; } template inline void ChainComplex_iterator::prepare_LxR_prev(matrix *pLxR_prev) { if (pLxR_prev) for (typename Entire< Cols >::iterator c=entire(cols(delta)); !c.at_end(); ++c) if (!c->empty()) pLxR_prev->col(c.index()).clear(); } template void ChainComplex_iterator::calculate_cycles(matrix& C) { Cycles.resize(hom_cur.betti_number + hom_cur.torsion.size(), delta.rows()); typename Entire< Rows >::iterator r=entire(rows(Cycles)); for (typename Homology::torsion_list::iterator t=hom_cur.torsion.begin(), t_end=hom_cur.torsion.end(); t != t_end; ++t, ++r) *r = companions[R_inv_prev].row(t->second); typename Rows::iterator r_d=rows(delta).begin(); while (!r.at_end()) { while (! r_d->empty()) ++r_d; if (! companions[LxR_prev].row(r_d.index()).empty()) { *r = companions[L].row(r_d.index()); ++r; } ++r_d; } } template void ChainComplex_iterator::compress_torsion() { for (typename Homology::torsion_list::iterator t=hom_cur.torsion.begin(), t_end=hom_cur.torsion.end(); t != t_end; ++t) { t->second=1; typename Homology::torsion_list::iterator t2=t; ++t2; while (1) { if (t2 == t_end) return; if (t->first == t2->first) { ++t->second; t2=hom_cur.torsion.erase(t2); } else { break; } } } } template class ChainComplex { protected: const BaseComplex& complex; int dim_high, dim_low; public: explicit ChainComplex(const BaseComplex& complex_arg, int dim_high_arg=-1, int dim_low_arg=0) : complex(complex_arg), dim_high(dim_high_arg), dim_low(dim_low_arg) { int d=dim(); if (dim_high<0) dim_high+=d+1; if (dim_low<0) dim_low+=d+1; if (dim_highd || dim_low<0) throw std::runtime_error("ChainComplex - dimensions out of range"); } int dim() const { return complex.dim(); } int size() const { return dim_high-dim_low+1; } const BaseComplex& get_complex() const { return complex; } typedef Homology homology_type; typedef CycleGroup cycle_group_type; template class as_container; friend const as_container& homologies(const ChainComplex& cc) { return reinterpret_cast&>(cc); } friend const as_container& homologies_and_cycles(const ChainComplex& cc) { return reinterpret_cast&>(cc); } friend const as_container& cohomologies(const ChainComplex& cc) { return reinterpret_cast&>(cc); } friend const as_container& cohomologies_and_cocycles(const ChainComplex& cc) { return reinterpret_cast&>(cc); } }; template template class ChainComplex::as_container : public ChainComplex { protected: as_container(); ~as_container(); public: typedef ChainComplex_iterator iterator; typedef iterator const_iterator; typedef typename iterator::value_type value_type; typedef typename iterator::reference reference; typedef reference const_reference; iterator begin() const { return iterator(complex,dim_high,dim_low); } iterator end() const { return iterator(complex,dim_low-1,dim_low); } }; template inline ChainComplex make_chain_complex(const BaseComplex& complex, int dim_high=-1, int dim_low=1) { return ChainComplex(complex,dim_high,dim_low); } } } #endif // _POLYMAKE_CHAIN_COMPLEX_H // Local Variables: // mode:C++ // c-basic-offset:3 // End: