include/topology/lsvineyard.hpp
author Dmitriy Morozov <dmitriy@mrzv.org>
Thu, 04 Feb 2010 23:43:36 -0800
branchdev
changeset 196 5303ce3f1934
parent 195 8a6f3ef2c42d
child 199 2bde4c56101c
permissions -rw-r--r--
Fixed non-copyable temporary bug

#include <utilities/log.h>

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
namespace bl = boost::lambda;

#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/foreach.hpp>

#ifdef LOGGING
static rlog::RLogChannel* rlLSVineyard =            DEF_CHANNEL("lsvineyard/info", rlog::Log_Debug);
static rlog::RLogChannel* rlLSVineyardDebug =       DEF_CHANNEL("lsvineyard/debug", rlog::Log_Debug);
#endif // LOGGING

#ifdef COUNTERS
static Counter*  cVertexTransposition =                     GetCounter("lsfiltration/transposition");       // counts number of vertex transpositions
static Counter*  cAttachment =                              GetCounter("lsfiltration/attachment");          // counts the number of attachment changes
#endif


template<class V, class VE, class S, class F>
template<class VertexIterator>
LSVineyard<V,VE,S,F>::
LSVineyard(VertexIterator begin, VertexIterator end, 
           LSFiltration& fltr,
           const VertexEvaluator& veval):
    filtration_(fltr),
    vertices_(begin, end),
    persistence_(filtration_),
    veval_(veval), vcmp_(veval_), scmp_(vcmp_),
    pfmap_(persistence_.make_simplex_map(filtration_)),
    time_count_(0)
{
    vertices_.sort(KineticVertexComparison(vcmp_));     // sort vertices w.r.t. vcmp_
#if LOGGING    
    rLog(rlLSVineyardDebug, "Vertex order:");
    for (typename VertexContainer::iterator cur = vertices_.begin(); cur != vertices_.end(); ++cur)
        rLog(rlLSVineyardDebug, "  %d", cur->vertex());
#endif

    // Record Vertex -> LSFIndex map
    VertexLSFIndexMap vimap;
    for (LSFIndex i = filtration().begin(); i != filtration().end(); ++i)
    {
        const Simplex& s = *i;
        rLog(rlLSVineyardDebug, "Simplex: %s", tostring(*i).c_str());
        if (s.dimension() == 0)
            vimap[s.vertices().front()] = i;
    }

    // Assign vertex attachments and simplex_index
    OffsetMap<LSFIndex, iterator>   fpmap(filtration().begin(), persistence().begin());
    for (typename VertexContainer::iterator vi = vertices_.begin(); vi != vertices_.end(); ++vi)
    {
        LSFIndex i = vimap[vi->vertex()];
        const Simplex& s = *i;
        AssertMsg(s.vertices().front() == vi->vertex(), "In constructor, simplices and vertices must match.");
        vertices_.modify(vi,    b::bind(&KineticVertexType::set_simplex_index, bl::_1, i));    // vi->set_simplex_index(i)
        set_attachment(fpmap[i], vi);
        rLog(rlLSVineyardDebug, "%s attached to %d", tostring(*i).c_str(), vi->vertex());
    }

    // Assign attachments for all the simplices
    VertexAttachmentComparison  vacmp(vimap, *this);
    for (LSFIndex i = filtration().begin(); i != filtration().end(); ++i)
        set_attachment(fpmap[i], fpmap[vimap[*std::max_element(i->vertices().begin(), i->vertices().end(), vacmp)]]->attachment);

    // Order filtration_ and persistence_ based on attachment
    rLog(rlLSVineyardDebug, "Ordering the simplices");
   
    std::vector<SimplexPersistenceElementTuple> fporder
                (b::make_zip_iterator(b::make_tuple(filtration().begin(),    persistence().begin())),
                 b::make_zip_iterator(b::make_tuple(filtration().end(),      persistence().end())));
    std::sort(fporder.begin(), fporder.end(), AttachmentCmp());

    // Rearrage filtration
    std::vector< b::reference_wrapper<const Simplex> >                sv; 
    BOOST_FOREACH(const SimplexPersistenceElementTuple& t, fporder)   sv.push_back(b::get<0>(t));
    filtration_.rearrange (sv.begin());

    // Rearrange persistence
    std::vector< b::reference_wrapper<const typename Persistence::Element> >    pev; 
    BOOST_FOREACH(const SimplexPersistenceElementTuple& t, fporder)   pev.push_back(b::get<1>(t));
    persistence_.rearrange(pev.begin());

#if LOGGING
    rLog(rlLSVineyardDebug, "Simplices:");
    for(iterator i = persistence().begin(); i != persistence().end(); ++i)
        rLog(rlLSVineyardDebug, "  %s attached to %d", tostring(pfmap(i)).c_str(), i->attachment->vertex());
#endif

    // Pair simplices
    rLog(rlLSVineyardDebug, "Initializing LSVineyard");
    persistence_.pair_simplices();
    rLog(rlLSVineyardDebug, "Simplices paired");

    evaluator_ = new StaticEvaluator(*this, time_count_);
    vineyard_.set_evaluator(evaluator_);
    vineyard_.start_vines(persistence_.begin(), persistence_.end());
}

template<class V, class VE, class S, class F>
LSVineyard<V,VE,S,F>::
~LSVineyard()
{
    delete evaluator_;
}

template<class V, class VE, class S, class F_>
void                    
LSVineyard<V,VE,S,F_>::
compute_vineyard(const VertexEvaluator& veval, bool explicit_events)
{
    typedef Traits::Kinetic_kernel::Point_1                                 Point;
    typedef Traits::Kinetic_kernel::Function_kernel::Construct_function     CF; 
    typedef Traits::Kinetic_kernel::Motion_function                         F; 
    
    Traits tr(0,1);
    Simulator::Handle sp = tr.simulator_handle();
    ActivePointsTable::Handle apt = tr.active_points_1_table_handle();
    Sort sort(tr, SortVisitor(*this));
    
    // Setup the (linear) trajectories
    rLog(rlLSVineyard, "Setting up trajectories");
    CF cf; 
    kinetic_map_.clear();
    
    // The reverse order takes advantage of how the kinetic sort arranges its elements 
    // (inserts before the upper bound) to avoid problems with duplicates; it's unfortunate 
    // that such dependence is necessary, it would be nice to eventually get rid of it.
    for (VertexIndex cur = boost::prior(vertices_.end()); cur != boost::prior(vertices_.begin()); --cur)
    {
        VertexValue val0 = veval_(cur->vertex());
        VertexValue val1 = veval(cur->vertex());
        rLog(rlLSVineyardDebug, "Vertex %d: %f -> %f", cur->vertex(), val0, val1);
        F x = cf(F::NT(val0), F::NT(val1 - val0));          // x = val0 + (val1 - val0)*t = (1-t)*val0 + t*val1
        Point p(x);
        vertices_.modify(cur,   b::bind(&KineticVertexType::set_kinetic_key, bl::_1, apt->insert(p)));    // cur->set_kinetic_key(apt->insert(p))
        kinetic_map_[cur->kinetic_key()] = cur;
        rLog(rlLSVineyardDebug, "Scheduling: %d %s", cur->vertex(), tostring(x).c_str());
    }
    
    // Process all the events (compute the vineyard in the process)
    change_evaluator(new KineticEvaluator(*this, sp, apt, time_count_));
    if (explicit_events)
    {
        while (sp->next_event_time() < 1)
        {
            rLog(rlLSVineyardDebug, "Next event time: %f", sp->next_event_time());
            sp->set_current_event_number(sp->current_event_number() + 1);
            rLog(rlLSVineyardDebug, "Processed event");
        }
    } else
        sp->set_current_time(1.0);
    rLog(rlLSVineyard, "Processed %d events", sp->current_event_number());
    
    veval_ = veval;
    change_evaluator(new StaticEvaluator(*this, ++time_count_));
    vineyard_.record_diagram(persistence().begin(), persistence().end());
}
        
template<class V, class VE, class S, class F>
void                    
LSVineyard<V,VE,S,F>::
swap(Key a, Key b)
{
    rLog(rlLSVineyardDebug, "Entered swap");
    VertexIndex ao = kinetic_map_[a], bo = kinetic_map_[b];
    rLog(rlLSVineyardDebug, "Vertices: %d %d compare %d", ao->vertex(), bo->vertex(), vcmp_(ao->vertex(), bo->vertex()));
    AssertMsg(!vcmp_(bo->vertex(), ao->vertex()), "In swap(a,b), a must precede b");
    transpose_vertices(ao);
    // AssertMsg(vcmp_(bo->vertex(), ao->vertex()), "In swap(a,b), b must precede a after the transposition");
}

template<class V, class VE, class S, class F>
void
LSVineyard<V,VE,S,F>::
change_evaluator(Evaluator* eval)
{
    AssertMsg(evaluator_ != 0, "change_evaluator() assumes that existing evaluator is not null");
        
    delete evaluator_;
    evaluator_ = eval;
    vineyard_.set_evaluator(evaluator_);
}

template<class V, class VE, class S, class F>
bool
LSVineyard<V,VE,S,F>::
transpose_vertices(VertexIndex vi)
{
    Count(cVertexTransposition);
    rLog(rlLSVineyard, "Transposing vertices (%d:%d, %d:%d)", vi->vertex(),             (vi -  vertices_.begin()),
                                                              b::next(vi)->vertex(),    (b::next(vi) - vertices_.begin()));

    DimensionFromIterator                       dim(pfmap_);
    TranspositionVisitor                        visitor(*this);

    OffsetMap<LSFIndex, iterator>   fpmap(filtration().begin(), persistence().begin());
    iterator i = fpmap[vi->simplex_index()];
    iterator i_prev = b::prior(i);
    iterator i_next = fpmap[b::next(vi)->simplex_index()];
    iterator i_next_prev = b::prior(i_next);           // transpositions are done in terms of the first index in the pair
    iterator j = b::next(i_next);
    
    VertexIndex     vi_next = b::next(vi);
    const Vertex&   v = vi->vertex();
    
    bool result = false;        // has a switch in pairing occurred
    
    // First move the vertex --- this can be sped up if we devise special "vertex transpose" operation
    rLog(rlLSVineyardDebug, "Starting to move the vertex");
    while (i_next_prev != i_prev)                       
    { 
        rLog(rlLSVineyardDebug, "  Transposing %s %s", tostring(pfmap(i_next_prev)).c_str(),
                                                       tostring(pfmap(b::next(i_next_prev))).c_str());
        result |= persistence_.transpose(i_next_prev, dim, visitor);
        AssertMsg((i_next_prev  <= persistence().iterator_to(i_next_prev->pair)) == i_next_prev->sign(), "Pairing must respect order");
        AssertMsg((i_next       <= persistence().iterator_to(i_next->pair))      == i_next->sign(),      "Pairing must respect order");
        i_next_prev = b::prior(i_next);
    }
    rLog(rlLSVineyardDebug, "Done moving the vertex");

    // Second, move the simplices attached to it
    rLog(rlLSVineyardDebug, "Moving attached simplices");
    // rLog(rlLSVineyardDebug, "  Considering %s", tostring(pfmap(j)).c_str());
    // rLog(rlLSVineyardDebug, "    attachment %d", j->attachment->vertex());
    while (j != persistence_.end() && j->attachment == vi_next)
    {
        rLog(rlLSVineyardDebug, "  Considering %s", tostring(pfmap(j)).c_str());
        if (pfmap(j).contains(v))       // j becomes attached to v and does not move
        {
            Count(cAttachment);
            rLog(rlLSVineyardDebug, "  Attachment changed for %s to %d", tostring(pfmap(j)).c_str(), vi->vertex());
            set_attachment(j, vi);
            AssertMsg(fpmap[vi->simplex_index()] < j, "The simplex must be attached to a preceding vertex");
            ++j;
            continue;
        }   

        iterator j_prev = j; ++j;
        while ((--j_prev)->attachment != vi_next)                // i.e., until we have reached vi_next (and the simplices that follow it) again
        {
            rLog(rlLSVineyardDebug, "    Moving: %s, %s", 
                                      tostring(pfmap(j_prev)).c_str(),
                                      tostring(pfmap(b::next(j_prev))).c_str());
            AssertMsg(j_prev->attachment == vi, "Simplex preceding the one being moved must be attached to v");
            result |= persistence_.transpose(j_prev, dim, visitor);
            AssertMsg((j_prev  <= persistence().iterator_to(j_prev->pair)) == j_prev->sign(), "Pairing must respect order");
            --j_prev;
        }
    }
    rLog(rlLSVineyard, "Done moving attached simplices");
    vertices_.relocate(vi, vi_next);                    // swap vi and vi_next
    
#if LSVINEYARD_CONSISTENCY    
    AssertMsg(verify_pairing(), "Pairing must be correct after vertex transposition");
#endif

    return result;
}

template<class V, class VE, class S, class F>
bool
LSVineyard<V,VE,S,F>::
verify_pairing() const
{
    rLog(rlLSVineyardDebug, "Verifying pairing");
    StaticPersistence<> p(filtration());
    p.pair_simplices(false);
    iterator                        i     = persistence().begin();
    StaticPersistence<>::iterator   ip    = p.begin();
    StaticPersistence<>::SimplexMap<LSFiltration>       m = p.make_simplex_map(filtration());

    while (ip != p.end())
    {
        if (&pfmap(i) != &m[ip])
        {
            rError("DP: %s %s", tostring(pfmap(i)).c_str(), tostring(pfmap(i->pair)).c_str());
            rError("SP: %s %s", tostring(m[ip]).c_str(), tostring(m[ip->pair]).c_str());
            rError("The order must match");
            return false;
        }
        if (&pfmap(i->pair) != &m[ip->pair])
        {
            rError("DP: %s %s", tostring(pfmap(i)).c_str(), tostring(pfmap(i->pair)).c_str());
            rError("SP: %s %s", tostring(m[ip]).c_str(), tostring(m[ip->pair]).c_str());
            rError("The pairing must match");
            return false;
        }
        ++i; ++ip;
    }

    return true;
}


/* Evaluators */
template<class V, class VE, class S, class C>
class LSVineyard<V,VE,S,C>::StaticEvaluator: public Evaluator
{
    public:
                                StaticEvaluator(const LSVineyard& v, RealType time): 
                                    time_(time), vineyard_(v)                               {}

        virtual RealType        time() const                                                { return time_; }
        virtual RealType        operator()(Index i) const                                   { return vineyard_.simplex_value(vineyard_.pfmap(i)); }
        virtual Dimension       dimension(Index i) const                                    { return vineyard_.pfmap(i).dimension(); }
                                
    private:
        RealType                time_;
        const LSVineyard&       vineyard_;
};

template<class V, class VE, class S, class C>
class LSVineyard<V,VE,S,C>::KineticEvaluator: public Evaluator
{
    public:
                                KineticEvaluator(const LSVineyard& v, Simulator::Handle sp, ActivePointsTable::Handle apt, RealType time_offset): 
                                    vineyard_(v), sp_(sp), apt_(apt), time_offset_(time_offset)           {}

        virtual RealType        time() const                                                { return time_offset_ + CGAL::to_double(get_time()); }
        virtual RealType        operator()(Index i) const                                   
        {
            rLog(rlLSVineyard, "%s (attached to %d): %s(%f) = %f", tostring(vineyard_.pfmap(i)).c_str(),
                                                                   i->attachment->vertex(),
                                                                   tostring(apt_->at(i->attachment->kinetic_key()).x()).c_str(),
                                                                   get_time(),
                                                                   CGAL::to_double(apt_->at(i->attachment->kinetic_key()).x()(get_time())));
            return CGAL::to_double(apt_->at(i->attachment->kinetic_key()).x()(get_time())); 
        }
        virtual Dimension       dimension(Index i) const                                    { return vineyard_.pfmap(i).dimension(); }

    private:
        Simulator::Time         get_time() const                                            { return sp_->current_time(); }
        
        const LSVineyard&           vineyard_;
        Simulator::Handle           sp_;
        ActivePointsTable::Handle   apt_;
        RealType                    time_offset_;
};


template<class V, class VE, class S, class C>
class LSVineyard<V,VE,S,C>::VertexAttachmentComparison: 
    public std::binary_function<Vertex, Vertex, bool>
{
    public:
                                VertexAttachmentComparison(const VertexLSFIndexMap& vimap, 
                                                           const LSVineyard& vnrd):
                                    vimap_(vimap), vnrd_(vnrd)                              {}
        bool                    operator()(Vertex v1, Vertex v2) const                      
        { return vnrd_.filtration_attachment(vimap_.find(v1)->second) < vnrd_.filtration_attachment(vimap_.find(v2)->second); }

    private:
        const VertexLSFIndexMap&    vimap_;
        const LSVineyard&           vnrd_;
};


template<class V, class VE, class S, class C>
struct LSVineyard<V,VE,S,C>::AttachmentCmp: 
    public std::binary_function<const SimplexPersistenceElementTuple&, const SimplexPersistenceElementTuple&, bool>
{
    bool        operator()(const SimplexPersistenceElementTuple& t1, const SimplexPersistenceElementTuple& t2) const
    {
        if (b::get<1>(t1).get().attachment == b::get<1>(t2).get().attachment)
            return b::get<0>(t1).get().dimension() < b::get<0>(t2).get().dimension();
        else
            return b::get<1>(t1).get().attachment  < b::get<1>(t2).get().attachment;
    }
};