/* 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_BENEATH_BEYOND_TCC #define _POLYMAKE_BENEATH_BEYOND_TCC "$Project: polymake $$Id: beneath_beyond.tcc 7315 2006-04-02 21:37:53Z gawrilow $" namespace polymake { namespace polytope { template template void beneath_beyond_algo::compute(Iterator perm) { if (perm.at_end()) return; // the first point int d2=points.cols()-2, p1=*perm; ++perm; null_space(entire(item2container(points[p1])), black_hole(), black_hole(), AH); // look for the second point different from the first one while (true) { if (perm.at_end()) { // the special case: a single point triang_size=1; triangulation.push_back(scalar2set(p1)); return; } int p2=*perm; ++perm; null_space(entire(item2container(points[p2])), black_hole(), black_hole(), AH); if (AH.rows()==d2) { // two different points found: initialize the polytope start_with_points(p1,p2); break; } if (!already_VERTICES) interior_points += p2; } // as long as the affine hull is not empty... while (AH.rows() && !perm.at_end()) { add_point_low_dim(*perm); #ifdef POLY_DEBUG if (debug==do_check) { check_p(*perm); if (!pm::identical::value) points_so_far+=*perm; } #endif ++perm; } // then take the shortcut while (!perm.at_end()) { add_point_full_dim(*perm); #ifdef POLY_DEBUG if (debug==do_check) { check_p(*perm); if (!pm::identical::value) points_so_far+=*perm; } #endif ++perm; } if (!facet_normals_valid) { facet_normals_low_dim(); facet_normals_valid=true; } // sweep out the unneeded facets from the recycling list dual_graph.squeeze(); if (dual_graph.nodes()==2) dual_graph.delete_edge(0,1); // special case of dimension==1: the dual graph is not connected free_facet=-1; #ifdef POLY_DEBUG active_facets=sequence(0,dual_graph.nodes()); cout << "final "; dump(); #endif } template void beneath_beyond_algo::start_with_points(int p1, int p2) { new_facet(); dual_graph.node(0).vertices=scalar2set(p1); new_facet(); dual_graph.node(1).vertices=scalar2set(p2); dual_graph.edge(0,1); vertices_so_far=scalar2set(p1)+scalar2set(p2); triangulation.push_back(vertices_so_far); triang_size=1; dual_graph.node(0).simplices.push_back(incident_simplex(triangulation.front(),p2)); dual_graph.node(1).simplices.push_back(incident_simplex(triangulation.front(),p1)); valid_facet=0; #ifdef POLY_DEBUG cout << "starting points: " << p1 << ' ' << p2 << "\nAH:\n" << AH << endl; #endif if ((facet_normals_valid=!AH.rows())) { // dimension==1, will need the facet normals immediately dual_graph.node(0).coord_full_dim(*this); dual_graph.node(1).coord_full_dim(*this); } } /** @param p the new point @param f the facet index to start from @retval index of the violated/incident facet or -1 if nothing found */ template int beneath_beyond_algo::descend_to_violated_facet(int f, int p) { visited_facets+=f; E fxp= dual_graph.node(f).normal * points[p]; if ((dual_graph.node(f).orientation=sign(fxp))<=0) return f; // starting facet stays valid in this step: let's look for another one violated by p. // The search is performed in the dual graph, following the steepest descend of the // (square of the) distance between p and the facets if (!already_VERTICES) vertices_this_step += dual_graph.node(f).vertices; fxp=fxp*fxp/dual_graph.node(f).sqr_normal; // square of the distance from p to the facet int nextf; do { #ifdef POLY_DEBUG cout << " *" << f << '(' << fxp << ')'; #endif nextf=-1; for (typename Entire::iterator neighbor=entire(dual_graph.neighbors(f)); !neighbor.at_end(); ++neighbor) { int f2=*neighbor; if (visited_facets.contains(f2)) continue; visited_facets+=f2; E f2xp= dual_graph.node(f2).normal * points[p]; if ((dual_graph.node(f2).orientation=sign(f2xp))<=0) return f2; if (!already_VERTICES) vertices_this_step += dual_graph.node(f2).vertices; f2xp=f2xp*f2xp/dual_graph.node(f2).sqr_normal; #ifdef POLY_DEBUG cout << ' ' << f2 << '(' << f2xp << ')'; #endif if (f2xp<=fxp) { nextf=f2; fxp=f2xp; } } } while ((f=nextf)>=0); return f; // -1 : local minimum of sqr(distance) reached } template static inline int first_or_none(const Set& set) { typename Entire::const_iterator s=entire(set); return s.at_end() ? -1 : *s; } template void beneath_beyond_algo::add_point_full_dim(int p) { #ifdef POLY_DEBUG cout << "point " << p << "=[ " << points[p] << " ] : valid facets"; #endif // reset the working variables visited_facets.clear(); if (!already_VERTICES) vertices_this_step.clear(); // first try the facet added last in the previous step int f=valid_facet; do { if ((f=descend_to_violated_facet(f,p))>=0) { update_facets(f,p); return; } f=first_or_none(active_facets-visited_facets); } while (f>=0); // no violated facet found: p must be a redundant point if (!already_VERTICES) { interior_points += p; #ifdef POLY_DEBUG if (debug>=do_dump) cout << "\ninterior points: " << interior_points << "\n=======================================" << endl; #endif } } template void beneath_beyond_algo::facet_normals_low_dim() { // facets must be orthogonal to the affine hull facet_nullspace=unit_matrix(points.cols()); null_space(entire(rows( zero_vector(AH.rows()) | AH.minor(All,range(1,points.cols()-1)) )), black_hole(), black_hole(), facet_nullspace); for (typename Entire::iterator f=entire(select(nodes(dual_graph), active_facets)); !f.at_end(); ++f) { f->coord_low_dim(*this); #ifdef POLY_DEBUG if (debug>=do_dump) cout << f.index() << ": =[" << f->normal << " ]\n"; #endif } } template void beneath_beyond_algo::add_point_low_dim(int p) { // update the affine hull int codim=AH.rows(); null_space(entire(item2container(points[p])), black_hole(), black_hole(), AH); if (AH.rows()::iterator simplex=entire(triangulation); !simplex.at_end(); ++simplex) { *simplex += p; nf.simplices.push_back(incident_simplex(*simplex,p)); } for (typename Entire< Edges >::iterator e=entire(edges(dual_graph)); !e.at_end(); ++e) *e += p; facet_normals_valid=!AH.rows(); for (typename Entire::iterator f=entire(select(nodes(dual_graph), active_facets)); !f.at_end(); ++f) { // for all facets, except the new one if (f.index() != nf_index) { f.out_edges().insert(nf_index,f->vertices); f->vertices+=p; } if (facet_normals_valid) // the polytope became full-dimensional: will need the facet coordinates the whole rest of the time f->coord_full_dim(*this); } #ifdef POLY_DEBUG cout << "point " << p << ": dim increased\nAH:\n" << AH << endl; #endif } else { // point set dimension not increased if (!facet_normals_valid) { // the polytope was a simplex, the facet coordinates are still not computed; // now we need them for the visibility region search. facet_normals_low_dim(); facet_normals_valid=true; } add_point_full_dim(p); } } template inline void beneath_beyond_algo::update_facets(int f, int p) { #ifdef POLY_DEBUG cout << "\nupdating:"; #endif // queue for BFS std::list Q; Q.push_back(f); if (!already_VERTICES) interior_points_this_step.clear(); std::list incident_facets; if (dual_graph.node(f).orientation==0) { dual_graph.node(f).vertices += p; generic_position=false; incident_facets.push_back(f); } /* BFS in the visible hemisphere. We visit all facets violated by or incident with p. Incident facets are important since they can contain redundant points not discovered before this iteration. */ while (!Q.empty()) { f=Q.front(); Q.pop_front(); int f_orientation=dual_graph.node(f).orientation; // remember the position where the new simplices will end typename Triangulation::iterator new_simplex_end=triangulation.begin(); if (f_orientation<0) { // the facet is violated #ifdef POLY_DEBUG cout << " -" << f; #endif if (!already_VERTICES) interior_points_this_step += dual_graph.node(f).vertices; // build new triangulation simplices using the triangulation of the facet for (typename Entire::iterator is=entire(dual_graph.node(f).simplices); !is.at_end(); ++is) { triangulation.push_front(*is->simplex); ++triang_size; // just take the existing simplex and replace the vertex behind the facet by the new point (triangulation.front() -= is->opposite_vertex) += p; } #ifdef POLY_DEBUG } else { cout << " " << f; #endif } // check the neighbor facets for (typename Entire::iterator e=entire(dual_graph.out_edges(f)); !e.at_end(); ++e) { int f2=e.to_node(); facet_info& nbf=dual_graph.node(f2); if (!visited_facets.contains(f2)) { visited_facets+=f2; nbf.orientation=sign(nbf.normal * points[p]); if (nbf.orientation==0) { // incident facet nbf.vertices += p; generic_position=false; incident_facets.push_back(f2); } if (nbf.orientation<=0) Q.push_back(f2); else if (!already_VERTICES) vertices_this_step += nbf.vertices; } if (f_orientation<0) { if (nbf.orientation>0) { // found a ridge on the visibility border: create a new facet int nf_index=new_facet(); facet_info& nf=dual_graph.node(nf_index); nf.vertices=*e + p; if (AH.rows()) nf.coord_low_dim(*this); else nf.coord_full_dim(*this); #ifdef POLY_DEBUG if (debug==do_check) check_f(nf_index, p); #endif dual_graph.edge(nf_index,f2)=*e; incident_facets.push_back(nf_index); nf.add_incident_simplices(triangulation.begin(), new_simplex_end); } else if (nbf.orientation==0) { nbf.add_incident_simplices(triangulation.begin(), new_simplex_end); } } else if (nbf.orientation==0) { *e += p; // include the point into the edge, since it's incident to both facets } } if (f_orientation<0) delete_facet(f); } if (!already_VERTICES) { if (interior_points_this_step.empty()) { // = no violated facets visited interior_points += p; #ifdef POLY_DEBUG if (debug>=do_dump) cout << "\ninterior points: " << interior_points << "\n=======================================" << endl; #endif return; } else { interior_points_this_step -= vertices_this_step; interior_points += interior_points_this_step; } } valid_facet=incident_facets.front(); update_dual_graph(select(nodes(dual_graph),incident_facets)); if (AH.rows()) vertices_so_far+=p; #ifdef POLY_DEBUG if (debug>=do_dump) { dump(); cout << "\ninterior points: " << interior_points << "\n=======================================" << endl; } #endif } template template void beneath_beyond_algo::update_dual_graph (FacetSubset facets) { int min_ridge=points.cols()-AH.rows()-2; for (typename Entire::iterator f=entire(facets); !f.at_end(); ++f) { const bool vis=visited_facets.contains(f.index()); typename Entire::iterator f2=f; for (++f2; !f2.at_end(); ++f2) { // if both facets are incident to p, they could already have a connecting edge if (vis && visited_facets.contains(f2.index()) && f.out_edges().exists(f2.index())) continue; const Set ridge= f->vertices * f2->vertices; if (ridge.size()>=min_ridge) { bool add=true; typename Entire::iterator e=entire(f.out_edges()); while (!e.at_end()) { int inc=incl(*e,ridge); if (inc==2) { ++e; } else { if (inc<=0) f.out_edges().erase(e++); if (inc>=0) { add=false; break; } } } if (add) f.out_edges().insert(f2.index(),ridge); } } } } template inline void beneath_beyond_algo::facet_info::coord_full_dim (const beneath_beyond_algo& A) { normal=rows(null_space(A.points.minor(vertices,All))).front(); if (normal * A.points[(A.vertices_so_far - vertices).front()] < 0) inv_sign(normal); sqr_normal=sqr(normal); } template void beneath_beyond_algo::facet_info::coord_low_dim (const beneath_beyond_algo& A) { ListMatrix< SparseVector > Fn=A.facet_nullspace; null_space(entire(rows(A.points.minor(vertices,All))), black_hole(), black_hole(), Fn); normal=rows(Fn).front(); if (normal * A.points[(A.vertices_so_far - vertices).front()] < 0) inv_sign(normal); sqr_normal=sqr(normal); } template inline int single_or_nothing(const GenericSet& s) { int x=-1; typename Entire::const_iterator e=entire(s); if (!e.at_end()) { x=*e; ++e; if (!e.at_end()) x=-1; } return x; } template template inline void beneath_beyond_algo::facet_info::add_incident_simplices(Iterator s, Iterator s_end) { for (; s != s_end; ++s) { int opv=single_or_nothing(*s-vertices); if (opv>=0) simplices.push_back(incident_simplex(*s,opv)); } } template int beneath_beyond_algo::new_facet() { int f=free_facet; if (f>=0) { free_facet=dual_graph.node(f).orientation; } else { f=dual_graph.nodes(); dual_graph.resize(f+1); } active_facets+=f; return f; } template void beneath_beyond_algo::delete_facet(int f) { active_facets-=f; facet_info& F=dual_graph.node(f); dual_graph.out_edges(f).clear(); F.normal.clear(); F.vertices.clear(); F.simplices.clear(); F.orientation=free_facet; free_facet=f; } #ifdef POLY_DEBUG template void beneath_beyond_algo::dump() const { cout << "dual_graph:\n"; const bool show_normals= debug==do_full_dump && (!AH.rows() || facet_nullspace.rows()); for (typename Entire::const_iterator f=entire(select(nodes(dual_graph), active_facets)); !f.at_end(); ++f) { cout << f.index() << ": " << f->vertices; if (show_normals) cout << "=[ " << f->normal << " ]"; if (debug==do_full_dump) { cout << ' ' << f.out_edges(); cout << " <<"; for (typename Entire::const_iterator s=entire(f->simplices); !s.at_end(); ++s) cout << ' ' << *s->simplex << '-' << s->opposite_vertex; cout << " >>" << endl; } else { cout << ' ' << f.neighbors() << endl; } } } template inline void beneath_beyond_algo::check_fp(int f_index, const facet_info& f, int p, std::ostringstream& errors) const { E prod= points[p] * f.normal; if (f.vertices.contains(p)) { if (prod) errors << "facet(" << f_index << ") * incident vertex(" << p << ")=" << prod << endl; } else { if (prod<=0) errors << "facet(" << f_index << ") * non-incident vertex(" << p << ")=" << prod << endl; } } // various consistency checks template void beneath_beyond_algo::check_p(int p) const { if (!AH.rows() || facet_nullspace.rows()) { std::ostringstream errors; for (typename Entire::const_iterator f=entire(select(nodes(dual_graph), active_facets)); !f.at_end(); ++f) check_fp(f.index(), *f, p, errors); if (!errors.str().empty()) throw std::runtime_error("beneath_beyond_algo - consistency checks failed:\n" + errors.str()); } } template void beneath_beyond_algo::check_f(int f, int last_p) const { std::ostringstream errors; const facet_info& fi=dual_graph.node(f); if (points_so_far.empty()) { for (typename Entire::const_iterator p=entire(range(0,last_p)); !p.at_end(); ++p) check_fp(f, fi, *p, errors); } else { for (typename Entire::const_iterator p=entire(points_so_far); !p.at_end(); ++p) check_fp(f, fi, *p, errors); } if (!errors.str().empty()) throw std::runtime_error("beneath_beyond_algo - consistency checks failed:\n" + errors.str()); } template void beneath_beyond_algo::dump_p(int p) const { if (!AH.rows() || facet_nullspace.rows()) { for (typename Entire::const_iterator f=entire(select(nodes(dual_graph), active_facets)); !f.at_end(); ++f) if (f.degree()) { E prod= points[p] * f->normal; cout << "facet(" << f.index() << "): prod=" << prod << ", sqr_dist=" << double(prod*prod/f->sqr_normal) << '\n'; } } } #endif // POLY_DEBUG } } #endif // _POLYMAKE_BENEATH_BEYOND_TCC // Local Variables: // mode:C++ // c-basic-offset:3 // End: