Program Listing for File neg_cycle.hpp

Return to documentation for file (netoptim/neg_cycle.hpp)

// -*- coding: utf-8 -*-
#pragma once

#include <cassert>
#include <py2cpp/py2cpp.hpp>
#include <vector>

template <typename Graph> //
class negCycleFinder
{
    using node_t = typename Graph::node_t;
    using edge_t = typename Graph::edge_t;

    py::dict<node_t, node_t> _pred {};
    py::dict<node_t, edge_t> _edge {};

  private:
    const Graph& _G; // const???

  public:
    explicit negCycleFinder(const Graph& G)
        : _G {G}
    {
    }


    template <typename Container, typename WeightFn>
    auto find_neg_cycle(Container&& dist, WeightFn&& get_weight)
        -> std::vector<edge_t>
    {
        this->_pred.clear();
        this->_edge.clear();

        while (this->_relax(dist, get_weight))
        {
            const auto v = this->_find_cycle();
            if (v != this->_G.null_vertex())
            {
                assert(this->_is_negative(v, dist, get_weight));
                return this->_cycle_list(v);
            }
        }
        return std::vector<edge_t> {}; // ???
    }

  private:
    auto _find_cycle() -> node_t
    {
        auto visited = py::dict<node_t, node_t> {};

        for (auto&& v : this->_G)
        {
            if (visited.contains(v))
            {
                continue;
            }
            auto u = v;
            while (true)
            {
                visited[u] = v;
                if (!this->_pred.contains(u))
                {
                    break;
                }
                u = this->_pred[u];
                if (visited.contains(u))
                {
                    if (visited[u] == v)
                    {
                        // if (this->_is_negative(u)) {
                        // should be "yield u";
                        return u;
                        // }
                    }
                    break;
                }
            }
        }

        return this->_G.null_vertex();
    }

    template <typename Container, typename WeightFn>
    auto _relax(Container&& dist, WeightFn&& get_weight) -> bool
    {
        auto changed = false;
        for (auto&& e : this->_G.edges())
        {
            const auto vs = this->_G.end_points(e);
            const auto& u = vs.first;
            const auto& v = vs.second;
            const auto wt = get_weight(e);
            const auto d = dist[u] + wt;

            if (dist[v] > d)
            {
                this->_pred[v] = u;
                this->_edge[v] = e; // ???
                dist[v] = d;
                changed = true;
            }
        }
        return changed;
    }

    auto _cycle_list(const node_t& handle) -> std::vector<edge_t>
    {
        auto v = handle;
        auto cycle = std::vector<edge_t> {}; // ???
        do
        {
            const auto& u = this->_pred[v];
            cycle.push_back(this->_edge[v]); // ???
            v = u;
        } while (v != handle);
        return cycle;
    }

    template <typename Container, typename WeightFn>
    auto _is_negative(const node_t& handle, const Container& dist,
        WeightFn&& get_weight) -> bool
    {
        auto v = handle;
        do
        {
            const auto u = this->_pred[v];
            const auto e = this->_edge[v];
            const auto wt = get_weight(e); // ???
            if (dist[v] > dist[u] + wt)
            {
                return true;
            }
            v = u;
        } while (v != handle);

        return false;
    }
};