2020-04-09 17:44:17 +02:00
|
|
|
/*
|
|
|
|
* Copyright © 2009-2020 Dynare Team
|
|
|
|
*
|
|
|
|
* This file is part of Dynare.
|
|
|
|
*
|
|
|
|
* Dynare 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 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Dynare 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2021-06-09 16:52:20 +02:00
|
|
|
* along with Dynare. If not, see <https://www.gnu.org/licenses/>.
|
2020-04-09 17:44:17 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#include "VariableDependencyGraph.hh"
|
|
|
|
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
|
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
|
|
|
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
|
|
#include <boost/graph/strong_components.hpp>
|
|
|
|
#include <boost/graph/topological_sort.hpp>
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
|
|
|
|
using namespace boost;
|
|
|
|
|
|
|
|
VariableDependencyGraph::VariableDependencyGraph(int n) : base(n)
|
|
|
|
{
|
|
|
|
/* It is necessary to manually initialize the vertex_index property since
|
|
|
|
this graph uses listS and not vecS as underlying vertex container */
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
put(v_index, vertex(i, *this), i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VariableDependencyGraph::suppress(vertex_descriptor vertex_to_eliminate)
|
|
|
|
{
|
|
|
|
clear_vertex(vertex_to_eliminate, *this);
|
|
|
|
remove_vertex(vertex_to_eliminate, *this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VariableDependencyGraph::suppress(int vertex_num)
|
|
|
|
{
|
|
|
|
suppress(vertex(vertex_num, *this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VariableDependencyGraph::eliminate(vertex_descriptor vertex_to_eliminate)
|
|
|
|
{
|
|
|
|
if (in_degree(vertex_to_eliminate, *this) > 0 && out_degree(vertex_to_eliminate, *this) > 0)
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it_in, in_end] = in_edges(vertex_to_eliminate, *this); it_in != in_end; ++it_in)
|
|
|
|
for (auto [it_out, out_end] = out_edges(vertex_to_eliminate, *this); it_out != out_end; ++it_out)
|
|
|
|
if (auto [ed, exist] = edge(source(*it_in, *this), target(*it_out, *this), *this);
|
|
|
|
!exist)
|
|
|
|
add_edge(source(*it_in, *this), target(*it_out, *this), *this);
|
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
suppress(vertex_to_eliminate);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::hasCycleDFS(vertex_descriptor u, color_t &color, vector<int> &circuit_stack) const
|
|
|
|
{
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
color[u] = gray_color;
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [vi, vi_end] = out_edges(u, *this); vi != vi_end; ++vi)
|
|
|
|
if (color[target(*vi, *this)] == white_color
|
|
|
|
&& hasCycleDFS(target(*vi, *this), color, circuit_stack))
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
|
|
|
// cycle detected, return immediately
|
|
|
|
circuit_stack.push_back(v_index[target(*vi, *this)]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (color[target(*vi, *this)] == gray_color)
|
|
|
|
{
|
|
|
|
// *vi is an ancestor!
|
|
|
|
circuit_stack.push_back(v_index[target(*vi, *this)]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
color[u] = black_color;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::hasCycle() const
|
|
|
|
{
|
|
|
|
// Initialize color map to white
|
|
|
|
color_t color;
|
|
|
|
vector<int> circuit_stack;
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [vi, vi_end] = vertices(*this); vi != vi_end; ++vi)
|
2020-04-09 17:44:17 +02:00
|
|
|
color[*vi] = white_color;
|
|
|
|
|
|
|
|
// Perform depth-first search
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [vi, vi_end] = vertices(*this); vi != vi_end; ++vi)
|
2020-04-09 17:44:17 +02:00
|
|
|
if (color[*vi] == white_color && hasCycleDFS(*vi, color, circuit_stack))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
VariableDependencyGraph::print() const
|
|
|
|
{
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
cout << "Graph\n"
|
|
|
|
<< "-----\n";
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it, it_end] = vertices(*this); it != it_end; ++it)
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
|
|
|
cout << "vertex[" << v_index[*it] + 1 << "] <-";
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it_in, in_end] = in_edges(*it, *this); it_in != in_end; ++it_in)
|
2020-04-09 17:44:17 +02:00
|
|
|
cout << v_index[source(*it_in, *this)] + 1 << " ";
|
|
|
|
cout << "\n ->";
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it_out, out_end] = out_edges(*it, *this); it_out != out_end; ++it_out)
|
2020-04-09 17:44:17 +02:00
|
|
|
cout << v_index[target(*it_out, *this)] + 1 << " ";
|
|
|
|
cout << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VariableDependencyGraph
|
2020-04-29 18:12:22 +02:00
|
|
|
VariableDependencyGraph::extractSubgraph(const vector<int> &select_index) const
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
|
|
|
int n = select_index.size();
|
|
|
|
VariableDependencyGraph G(n);
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
auto v_index1_G = get(vertex_index1, G); // Maps new vertices to original indices
|
|
|
|
map<int, int> reverse_index; // Maps orig indices to new ones
|
2020-04-29 18:12:22 +02:00
|
|
|
for (int i = 0; i < n; i++)
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
2020-04-29 18:12:22 +02:00
|
|
|
reverse_index[select_index[i]] = i;
|
|
|
|
v_index1_G[vertex(i, G)] = select_index[i];
|
2020-04-09 17:44:17 +02:00
|
|
|
}
|
2020-04-29 18:12:22 +02:00
|
|
|
for (int i = 0; i < n; i++)
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
2020-04-29 18:12:22 +02:00
|
|
|
auto vi = vertex(select_index[i], *this);
|
|
|
|
for (auto [it_out, out_end] = out_edges(vi, *this); it_out != out_end; ++it_out)
|
|
|
|
if (auto it = reverse_index.find(v_index[target(*it_out, *this)]);
|
|
|
|
it != reverse_index.end())
|
|
|
|
add_edge(vertex(i, G), vertex(it->second, G), G);
|
2020-04-09 17:44:17 +02:00
|
|
|
}
|
|
|
|
return G;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::vertexBelongsToAClique(vertex_descriptor vertex) const
|
|
|
|
{
|
|
|
|
vector<vertex_descriptor> liste;
|
|
|
|
bool agree = true;
|
2020-04-29 18:12:22 +02:00
|
|
|
auto [it_in, in_end] = in_edges(vertex, *this);
|
|
|
|
auto [it_out, out_end] = out_edges(vertex, *this);
|
2020-04-09 17:44:17 +02:00
|
|
|
while (it_in != in_end && it_out != out_end && agree)
|
|
|
|
{
|
|
|
|
agree = (source(*it_in, *this) == target(*it_out, *this)
|
|
|
|
&& source(*it_in, *this) != target(*it_in, *this)); //not a loop
|
|
|
|
liste.push_back(source(*it_in, *this));
|
|
|
|
++it_in;
|
|
|
|
++it_out;
|
|
|
|
}
|
|
|
|
if (agree)
|
|
|
|
{
|
|
|
|
if (it_in != in_end || it_out != out_end)
|
|
|
|
agree = false;
|
|
|
|
int i = 1;
|
|
|
|
while (i < static_cast<int>(liste.size()) && agree)
|
|
|
|
{
|
|
|
|
int j = i + 1;
|
|
|
|
while (j < static_cast<int>(liste.size()) && agree)
|
|
|
|
{
|
2020-04-29 18:12:22 +02:00
|
|
|
auto [ed1, exist1] = edge(liste[i], liste[j], *this);
|
|
|
|
auto [ed2, exist2] = edge(liste[j], liste[i], *this);
|
2020-04-21 23:06:51 +02:00
|
|
|
agree = exist1 && exist2;
|
2020-04-09 17:44:17 +02:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return agree;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::eliminationOfVerticesWithOneOrLessIndegreeOrOutdegree()
|
|
|
|
{
|
|
|
|
bool something_has_been_done = false;
|
|
|
|
bool not_a_loop;
|
|
|
|
int i;
|
2020-04-29 18:12:22 +02:00
|
|
|
vertex_iterator it, ita, it_end;
|
2020-04-09 17:44:17 +02:00
|
|
|
for (tie(it, it_end) = vertices(*this), i = 0; it != it_end; ++it, i++)
|
|
|
|
{
|
|
|
|
int in_degree_n = in_degree(*it, *this);
|
|
|
|
int out_degree_n = out_degree(*it, *this);
|
|
|
|
if (in_degree_n <= 1 || out_degree_n <= 1)
|
|
|
|
{
|
|
|
|
not_a_loop = true;
|
|
|
|
if (in_degree_n >= 1 && out_degree_n >= 1) // Do not eliminate a vertex if it loops on itself!
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it_in, in_end] = in_edges(*it, *this); it_in != in_end; ++it_in)
|
|
|
|
if (source(*it_in, *this) == target(*it_in, *this))
|
|
|
|
not_a_loop = false;
|
2020-04-09 17:44:17 +02:00
|
|
|
if (not_a_loop)
|
|
|
|
{
|
|
|
|
eliminate(*it);
|
|
|
|
something_has_been_done = true;
|
|
|
|
if (i > 0)
|
|
|
|
it = ita;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tie(it, it_end) = vertices(*this);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ita = it;
|
|
|
|
}
|
|
|
|
return something_has_been_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::eliminationOfVerticesBelongingToAClique()
|
|
|
|
{
|
2020-04-29 18:12:22 +02:00
|
|
|
vertex_iterator it, ita, it_end;
|
2020-04-09 17:44:17 +02:00
|
|
|
bool something_has_been_done = false;
|
|
|
|
int i;
|
|
|
|
for (tie(it, it_end) = vertices(*this), i = 0; it != it_end; ++it, i++)
|
|
|
|
{
|
|
|
|
if (vertexBelongsToAClique(*it))
|
|
|
|
{
|
|
|
|
eliminate(*it);
|
|
|
|
something_has_been_done = true;
|
|
|
|
if (i > 0)
|
|
|
|
it = ita;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tie(it, it_end) = vertices(*this);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ita = it;
|
|
|
|
}
|
|
|
|
return something_has_been_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
VariableDependencyGraph::suppressionOfVerticesWithLoop(set<int> &feed_back_vertices)
|
|
|
|
{
|
|
|
|
bool something_has_been_done = false;
|
2020-04-29 18:12:22 +02:00
|
|
|
vertex_iterator ita;
|
2020-04-09 17:44:17 +02:00
|
|
|
int i = 0;
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it, it_end] = vertices(*this); it != it_end; ++it, i++)
|
2020-04-09 17:44:17 +02:00
|
|
|
{
|
2020-04-29 18:12:22 +02:00
|
|
|
auto [ed, exist] = edge(*it, *it, *this);
|
2020-04-09 17:44:17 +02:00
|
|
|
if (exist)
|
|
|
|
{
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
feed_back_vertices.insert(v_index[*it]);
|
|
|
|
suppress(*it);
|
|
|
|
something_has_been_done = true;
|
|
|
|
if (i > 0)
|
|
|
|
it = ita;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tie(it, it_end) = vertices(*this);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ita = it;
|
|
|
|
}
|
|
|
|
return something_has_been_done;
|
|
|
|
}
|
|
|
|
|
2020-04-17 19:21:37 +02:00
|
|
|
set<int>
|
2020-04-09 17:44:17 +02:00
|
|
|
VariableDependencyGraph::minimalSetOfFeedbackVertices() const
|
|
|
|
{
|
|
|
|
set<int> feed_back_vertices;
|
|
|
|
VariableDependencyGraph G(*this);
|
|
|
|
while (num_vertices(G) > 0)
|
|
|
|
{
|
2020-04-21 23:06:51 +02:00
|
|
|
bool something_has_been_done = true;
|
2020-04-09 17:44:17 +02:00
|
|
|
while (something_has_been_done && num_vertices(G) > 0)
|
|
|
|
{
|
2020-04-21 23:06:51 +02:00
|
|
|
something_has_been_done = G.eliminationOfVerticesWithOneOrLessIndegreeOrOutdegree();
|
|
|
|
something_has_been_done = G.eliminationOfVerticesBelongingToAClique() || something_has_been_done;
|
|
|
|
something_has_been_done = G.suppressionOfVerticesWithLoop(feed_back_vertices) || something_has_been_done;
|
2020-04-09 17:44:17 +02:00
|
|
|
}
|
2020-04-21 23:06:51 +02:00
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
if (!G.hasCycle())
|
2020-04-21 23:06:51 +02:00
|
|
|
return feed_back_vertices;
|
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
if (num_vertices(G) > 0)
|
|
|
|
{
|
|
|
|
/* If nothing has been done in the five previous rule then cut the
|
|
|
|
vertex with the maximum in_degree+out_degree */
|
|
|
|
int max_degree = 0, num = 0;
|
2020-04-29 18:12:22 +02:00
|
|
|
vertex_iterator max_degree_index;
|
|
|
|
for (auto [it, it_end] = vertices(G); it != it_end; ++it, num++)
|
2020-04-09 17:44:17 +02:00
|
|
|
if (static_cast<int>(in_degree(*it, G) + out_degree(*it, G)) > max_degree)
|
|
|
|
{
|
|
|
|
max_degree = in_degree(*it, G) + out_degree(*it, G);
|
|
|
|
max_degree_index = it;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto v_index = get(vertex_index, G);
|
|
|
|
feed_back_vertices.insert(v_index[*max_degree_index]);
|
|
|
|
G.suppress(*max_degree_index);
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 19:21:37 +02:00
|
|
|
return feed_back_vertices;
|
2020-04-09 17:44:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
vector<int>
|
|
|
|
VariableDependencyGraph::reorderRecursiveVariables(const set<int> &feedback_vertices) const
|
|
|
|
{
|
|
|
|
vector<int> reordered_vertices;
|
|
|
|
VariableDependencyGraph G(*this);
|
2020-04-29 18:46:15 +02:00
|
|
|
auto v_index = get(vertex_index, G);
|
2020-04-21 23:06:51 +02:00
|
|
|
|
|
|
|
// Suppress feedback vertices, in decreasing order
|
|
|
|
for (auto it = feedback_vertices.rbegin(); it != feedback_vertices.rend(); ++it)
|
|
|
|
G.suppress(*it);
|
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
bool something_has_been_done = true;
|
|
|
|
while (something_has_been_done)
|
|
|
|
{
|
|
|
|
something_has_been_done = false;
|
|
|
|
vertex_iterator it, it_end, ita;
|
2020-04-21 23:06:51 +02:00
|
|
|
int i;
|
2020-04-09 17:44:17 +02:00
|
|
|
for (tie(it, it_end) = vertices(G), i = 0; it != it_end; ++it, i++)
|
|
|
|
{
|
|
|
|
if (in_degree(*it, G) == 0)
|
|
|
|
{
|
|
|
|
reordered_vertices.push_back(v_index[*it]);
|
|
|
|
G.suppress(*it);
|
|
|
|
something_has_been_done = true;
|
|
|
|
if (i > 0)
|
|
|
|
it = ita;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tie(it, it_end) = vertices(G);
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ita = it;
|
|
|
|
}
|
|
|
|
}
|
2020-04-21 23:06:51 +02:00
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
if (num_vertices(G))
|
|
|
|
cout << "Error in the computation of feedback vertex set\n";
|
2020-04-21 23:06:51 +02:00
|
|
|
|
2020-04-09 17:44:17 +02:00
|
|
|
return reordered_vertices;
|
|
|
|
}
|
|
|
|
|
|
|
|
pair<int, vector<int>>
|
|
|
|
VariableDependencyGraph::sortedStronglyConnectedComponents() const
|
|
|
|
{
|
|
|
|
vector<int> vertex2scc(num_vertices(*this));
|
|
|
|
auto v_index = get(vertex_index, *this);
|
|
|
|
|
|
|
|
// Compute SCCs and create mapping from vertices to unordered SCCs
|
|
|
|
int num_scc = strong_components(static_cast<base>(*this), make_iterator_property_map(vertex2scc.begin(), v_index));
|
|
|
|
|
|
|
|
// Create directed acyclic graph (DAG) associated to the SCCs
|
|
|
|
adjacency_list<vecS, vecS, directedS> dag(num_scc);
|
|
|
|
for (int i = 0; i < static_cast<int>(num_vertices(*this)); i++)
|
|
|
|
{
|
|
|
|
auto vi = vertex(i, *this);
|
2020-04-29 18:12:22 +02:00
|
|
|
for (auto [it_out, out_end] = out_edges(vi, *this); it_out != out_end; ++it_out)
|
|
|
|
if (int t_b = vertex2scc[v_index[target(*it_out, *this)]],
|
|
|
|
s_b = vertex2scc[v_index[source(*it_out, *this)]];
|
|
|
|
s_b != t_b)
|
|
|
|
add_edge(s_b, t_b, dag);
|
2020-04-09 17:44:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Compute topological sort of DAG (ordered list of unordered SCC)
|
|
|
|
Note: the order is reversed. */
|
|
|
|
vector<int> reverseOrdered2unordered;
|
|
|
|
topological_sort(dag, back_inserter(reverseOrdered2unordered));
|
|
|
|
|
|
|
|
// Construct mapping from unordered SCC to ordered SCC
|
|
|
|
vector<int> unordered2ordered(num_scc);
|
|
|
|
for (int j = 0; j < num_scc; j++)
|
|
|
|
unordered2ordered[reverseOrdered2unordered[num_scc-j-1]] = j;
|
|
|
|
|
|
|
|
// Update the mapping of vertices to (now sorted) SCCs
|
|
|
|
for (int i = 0; i < static_cast<int>(num_vertices(*this)); i++)
|
|
|
|
vertex2scc[i] = unordered2ordered[vertex2scc[i]];
|
|
|
|
|
|
|
|
return { num_scc, vertex2scc };
|
|
|
|
}
|