transitive_closure.hpp

Back to Functional transitive closure

pastel/sys/graph/transitive_closure/

#ifndef PASTELSYS_TRANSITIVE_CLOSURE_HPP
#define PASTELSYS_TRANSITIVE_CLOSURE_HPP

#include "pastel/sys/graph/transitive_closure.h"
#include "pastel/sys/ensure.h"

#include <unordered_map>
#include <vector>

namespace Pastel
{

    template <
        typename Domain, 
        typename Codomain,
        typename ForEachDomain,
        typename ForEachRelated,
        typename Function,
        typename CodomainOperator, 
        typename Closure_Output,
        typename Domain_Hash>
    class TransitiveClosure
    {
    public:
        TransitiveClosure(
            const Codomain& identity_,
            const ForEachDomain& forEachDomain_,
            const ForEachRelated& forEachRelated_,
            const Function& function_,
            const CodomainOperator& codomainOperator_,
            const Closure_Output& report_,
            bool reflexiveClosure_,
            const Domain_Hash& domainHash_)
            : identity(identity_)
            , forEachDomain(forEachDomain_)
            , forEachRelated(forEachRelated_)
            , domainHash(domainHash_)
            , function(function_)
            , codomainOperator(codomainOperator_)
            , report(report_)
            , reflexiveClosure(reflexiveClosure_)
        {
        }

        void work()
        {
            auto visitIt = 
                [&](const Domain& x)
            {
                if (!progressSet.count(x))
                {
                    // This node has not been visited
                    // yet. Visit it now.

                    this->traverse(x);
                }
            };

            forEachDomain(visitIt);

            // Report the closure function, but only
            // for those nodes which are in the domain.
            auto reportIt = 
                [&](const Domain& x)
            {
                auto iter = progressSet.find(x);
                ASSERT(iter != progressSet.end());

                report(
                    iter->first,
                    std::move(iter->second.value));
            };

            forEachDomain(reportIt);
        }

    private:
        void traverse(const Domain& x)
        {
            integer ClosureValueReady = -1;
            integer depth = workSet.size();

            // Give the node the identity value of the monoid. 
            // If this node does not refer to any nodes (not 
            // even itself), then it will end up having the identity 
            // value.
            auto result = progressSet.insert(
                std::make_pair(x, Progress(depth, identity)));

            // The traverse() function should only
            // be called once for each 'x', so that
            // there should not be an existing entry for
            // the 'x'.
            ASSERT(result.second);

            Progress& xProgress = result.first->second;

            // Push the node to the list of to-be-handled nodes.
            // Essentially, this list stores the elements of the
            // strongly connected components, with each component
            // being an interval in this list. However, there are
            // also singular nodes in this list which are not
            // strongly connected components (the node must be
            // related to itself to be a strongly connected 
            // component).
            workSet.push_back(x);

            auto dealWithIt = [&](const Domain& y)
            {
                if (x == y)
                {
                    // The node x is related to itself.
                    // Add the function-value to the
                    // closure-value.
                    xProgress.value = codomainOperator(
                        std::move(xProgress.value), 
                        this->function(x));
                    xProgress.reflexive = true;
                    return;
                }

                // If 'y' has not beed traversed yet...
                if (!progressSet.count(y))
                {
                    // ... traverse it now.
                    this->traverse(y);
                }

                const Progress& yProgress = progressSet[y];
                if (yProgress.depth >= 0)
                {
                    // After finishing visiting the related nodes,
                    // the depth of node x is the minimum of the depths 
                    // encountered in children.
                    if (yProgress.depth < xProgress.depth)
                    {
                        xProgress.depth = yProgress.depth;
                    }
                }
                else
                {
                    // If the node y has a negative depth value, then
                    // this means that its closure-value has already
                    // been computed.

                    ASSERT(x != y);

                    // Add the closure-value of node y to the 
                    // closure-value of node x.
                    xProgress.value = codomainOperator(
                        std::move(xProgress.value), 
                        yProgress.value);

                    if (!yProgress.reflexive)
                    {
                        // If the function-value of node y is not
                        // part of the closure-value of node y, then 
                        // add it now to the closure-value of node x.
                        xProgress.value = codomainOperator(
                            std::move(xProgress.value), 
                            this->function(y));
                    }
                }
            };

            forEachRelated(x, dealWithIt);

            // Force a reflexive relation if necessary.
            if (reflexiveClosure && !xProgress.reflexive)
            {
                dealWithIt(x);
            }

            if (xProgress.depth == depth)
            {
                // If the minimum encountered depth-value is the
                // same as the depth-value of node x, then all the
                // nodes after 'x' in the 'workSet' belong to the 
                // same strongly connected component as node x.

                bool moreThanOneElement =
                    (workSet.back() != x);

                if (moreThanOneElement)
                {
                    // We have a strongly connected component
                    // with at least two elements. These elements
                    // all have the same closure-value, which is
                    // the combination of the functions-values of
                    // the elements. Compute this shared closure-value
                    // incrementally.

                    auto iter = workSet.end();
                    do
                    {
                        --iter;
                        const Domain& y = *iter;
                        Progress& yProgress = progressSet[y];

                        if (!yProgress.reflexive)
                        {
                            // If a node in the component is not reflexive,
                            // then make it so.
                            yProgress.value = codomainOperator(
                                std::move(yProgress.value), function(y));
                            yProgress.reflexive = true;
                        }

                        if (x != y)
                        {
                            // Add the closure-value of node y to the 
                            // closure-value of node x.
                            xProgress.value = codomainOperator(
                                std::move(xProgress.value), yProgress.value);
                        }
                    }
                    while(*iter != x);

                    // Note that the closure-values are not correct yet.
                    // They need to copied from node x to the other nodes.
                    // This is done next.
                }

                bool ready = false;
                while(!ready)
                {
                    const Domain& y = workSet.back();
                    Progress& yProgress = progressSet[y];

                    // Mark the node as having a fully-computed
                    // closure-value.
                    yProgress.depth = ClosureValueReady;

                    if (y == x)
                    {
                        // No need to copy the closure-value of 
                        // node x to itself.
                        ready = true;
                    }
                    else
                    {
                        // Copy the closure-value inside the
                        // strongly connected component.
                        yProgress.value = xProgress.value;
                    }

                    // Remove the node from the work-set.
                    workSet.pop_back();
                }
            }
        }

        class Progress
        {
        public:
            Progress()
                : depth(0)
                , value()
                , reflexive(false)
            {
            }

            Progress(
                integer depth_,
                const Codomain& value_)
                : depth(depth_)
                , value(value_)
                , reflexive(false)
            {
            }

            Progress(
                integer depth_,
                Codomain&& value_)
                : depth(depth_)
                , value(value_)
                , reflexive(false)
            {
            }

            integer depth;
            Codomain value;
            bool reflexive;
        };

        const Codomain& identity;
        const ForEachDomain& forEachDomain;
        const ForEachRelated& forEachRelated;
        const Domain_Hash& domainHash;
        const Function& function;
        const CodomainOperator& codomainOperator;
        const Closure_Output& report;
        bool reflexiveClosure;

        using ProgressSet = std::unordered_map<Domain, Progress, Domain_Hash>;
        using Progress_Iterator = typename ProgressSet::iterator;
        using Progress_ConstIterator = typename ProgressSet::iterator;

        ProgressSet progressSet;
        std::vector<Domain> workSet;
    };

    template <
        typename Domain, 
        typename Codomain,
        typename ForEachDomain,
        typename ForEachRelated,
        typename Function,
        typename CodomainOperator, 
        typename Closure_Output,
        typename Domain_Hash>
    void transitiveClosure(
        const NoDeduction<Codomain>& identity,
        const ForEachDomain& forEachDomain,
        const ForEachRelated& forEachRelated,
        const Function& function,
        const CodomainOperator& codomainOperator,
        const Closure_Output& report,
        bool reflexiveClosure,
        const Domain_Hash& domainHash)
    {
        TransitiveClosure<Domain, Codomain, 
            ForEachDomain, ForEachRelated, 
            Function, CodomainOperator,
            Closure_Output, Domain_Hash> algorithm(
                identity,
                forEachDomain,
                forEachRelated,
                function,
                codomainOperator,
                report,
                reflexiveClosure,
                domainHash);

        algorithm.work();
    }

    template <
        typename Domain, 
        typename Codomain,
        typename ForEachDomain,
        typename ForEachRelated,
        typename Function,
        typename CodomainOperator, 
        typename Closure_Output>
    void transitiveClosure(
        const NoDeduction<Codomain>& identity,
        const ForEachDomain& forEachDomain,
        const ForEachRelated& forEachRelated,
        const Function& function,
        const CodomainOperator& codomainOperator,
        const Closure_Output& report,
        bool reflexiveClosure)
    {
        return Pastel::transitiveClosure<Domain, Codomain>(
            identity,
            forEachDomain,
            forEachRelated,
            function,
            codomainOperator,
            report,
            reflexiveClosure,
            std::hash<Domain>());
    }

}

#endif