core/indigo-core/graph/src/graph.cpp (682 lines of code) (raw):
/****************************************************************************
* Copyright (C) from 2009 to Present EPAM Systems.
*
* This file is part of Indigo toolkit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
#include <stdarg.h>
#include <stdio.h>
#include "base_c/defs.h"
#include "base_cpp/tlscont.h"
#include "graph/cycle_basis.h"
#include "graph/graph.h"
#include "graph/graph_decomposer.h"
#include "graph/spanning_tree.h"
using namespace indigo;
NeighborsAuto Vertex::neighbors() const
{
return NeighborsAuto(*this);
}
int Vertex::findNeiVertex(int idx) const
{
for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i))
if (neighbors_list[i].v == idx)
return i;
return -1;
}
int Vertex::findNeiEdge(int idx) const
{
for (int i = neighbors_list.begin(); i < neighbors_list.end(); i = neighbors_list.next(i))
if (neighbors_list[i].e == idx)
return i;
return -1;
}
IMPL_ERROR(Graph, "graph");
Graph::Graph()
{
_vertices = new ObjPool<Vertex>();
_neighbors_pool = new Pool<List<VertexEdge>::Elem>();
_sssr_pool = 0;
_components_valid = false;
}
Graph::~Graph()
{
delete _vertices;
delete _neighbors_pool;
if (_sssr_pool != 0)
{
_sssr_vertices.clear();
_sssr_edges.clear();
delete _sssr_pool;
}
}
int Graph::addVertex()
{
changed();
return _vertices->add(*_neighbors_pool);
}
int Graph::findEdgeIndex(int beg, int end) const
{
const Vertex& vbeg = getVertex(beg);
for (int i = vbeg.neiBegin(); i != vbeg.neiEnd(); i = vbeg.neiNext(i))
if (vbeg.neiVertex(i) == end)
return vbeg.neiEdge(i);
return -1;
}
bool Graph::haveEdge(int beg, int end) const
{
return findEdgeIndex(beg, end) != -1;
}
bool Graph::hasEdge(int idx) const
{
return _edges.hasElement(idx);
}
bool Graph::hasVertex(int idx) const
{
return _vertices->hasElement(idx);
}
int Graph::getEdgeEnd(int beg, int edge) const
{
const Edge& e = getEdge(edge);
if (e.beg == beg)
return e.end;
if (e.end == beg)
return e.beg;
return -1;
}
int Graph::addEdge(int beg, int end)
{
if (beg == end)
throw Error("can't have loop-edge on vertex %d", beg);
if (findEdgeIndex(beg, end) != -1)
throw Error("already have edge between vertices %d and %d", beg, end);
int edge_idx = _edges.add();
Vertex& vbeg = _vertices->at(beg);
Vertex& vend = _vertices->at(end);
int ve1_idx = vbeg.neighbors_list.add();
int ve2_idx = vend.neighbors_list.add();
VertexEdge& ve1 = vbeg.neighbors_list[ve1_idx];
VertexEdge& ve2 = vend.neighbors_list[ve2_idx];
ve1.v = end;
ve2.v = beg;
ve1.e = edge_idx;
ve2.e = edge_idx;
_edges[edge_idx].beg = beg;
_edges[edge_idx].end = end;
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
changed();
return edge_idx;
}
void Graph::swapEdgeEnds(int edge_idx)
{
std::swap(_edges[edge_idx].beg, _edges[edge_idx].end);
}
void Graph::removeEdge(int idx)
{
Edge edge = _edges[idx];
Vertex& beg = _vertices->at(edge.beg);
Vertex& end = _vertices->at(edge.end);
_edges.remove(idx);
beg.neighbors_list.remove(beg.findNeiEdge(idx));
end.neighbors_list.remove(end.findNeiEdge(idx));
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
changed();
}
void Graph::removeAllEdges()
{
for (int i = _vertices->begin(); i != _vertices->end(); i = _vertices->next(i))
_vertices->at(i).neighbors_list.clear();
_edges.clear();
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
changed();
}
void Graph::removeVertex(int idx)
{
QS_DEF(Array<int>, edges);
const Vertex& vertex = getVertex(idx);
int i;
edges.clear();
for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
edges.push(vertex.neiEdge(i));
for (i = 0; i < edges.size(); i++)
removeEdge(edges[i]);
_vertices->remove(idx);
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
changed();
}
const Vertex& Graph::getVertex(int idx) const
{
return _vertices->at(idx);
}
const Edge& Graph::getEdge(int idx) const
{
return _edges[idx];
}
bool Graph::isConnected(Graph& graph)
{
return graph.countComponents() == 1;
}
struct BfsState
{
int state;
int prev;
int edge;
};
/* Finds path, writes edge indices into path_out. Returns false if no path. */
bool Graph::findPath(int from, int where, Array<int>& path_out) const
{
path_out.clear();
QS_DEF(Array<int>, queue);
QS_DEF(Array<BfsState>, states);
queue.clear_resize(_vertices->size());
states.clear_resize(_vertices->end());
states.zerofill();
int top = 1, bottom = 0;
bool have_path = false;
states[where].state = 1;
queue[0] = where;
while (top != bottom)
{
if (queue[bottom] == from)
{
have_path = true;
break;
}
const Vertex& vertex = getVertex(queue[bottom]);
states[queue[bottom]].state = 2;
for (int i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
{
int other = vertex.neiVertex(i);
if (states[other].state == 0)
{
queue[top++] = other;
states[other].state = 1;
states[other].prev = queue[bottom];
states[other].edge = vertex.neiEdge(i);
}
}
bottom++;
}
if (have_path)
{
while (from != where)
{
path_out.push(states[from].edge);
from = states[from].prev;
}
return true;
}
return false;
}
VerticesAuto Graph::vertices()
{
return VerticesAuto(*this);
}
EdgesAuto Graph::edges()
{
return EdgesAuto(*this);
}
void Graph::changed()
{
}
void Graph::clear()
{
_vertices->clear();
_edges.clear();
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
changed();
}
bool Graph::isChain_AssumingConnected(const Graph& graph)
{
// ensure it is a tree
if (graph.vertexCount() - graph.edgeCount() != 1)
return false;
for (int i = graph.vertexBegin(); i < graph.vertexEnd(); i = graph.vertexNext(i))
if (graph.getVertex(i).degree() > 2)
return false;
return true;
}
bool Graph::isTree(Graph& graph)
{
if (!Graph::isConnected(graph))
return false;
if (graph.vertexCount() != graph.edgeCount() + 1)
return false;
return true;
}
void Graph::filterVertices(const Graph& graph, const int* filter, int filter_type, int filter_value, Array<int>& result)
{
result.clear();
for (int i = graph.vertexBegin(); i != graph.vertexEnd(); i = graph.vertexNext(i))
{
if (filter != 0)
{
if (filter_type == FILTER_EQ && filter_value != filter[i])
continue;
if (filter_type == FILTER_NEQ && filter_value == filter[i])
continue;
}
result.push(i);
}
}
void Graph::filterEdges(const Graph& graph, const int* filter, int filter_type, int filter_value, Array<int>& result)
{
result.clear();
for (int i = graph.edgeBegin(); i != graph.edgeEnd(); i = graph.edgeNext(i))
{
if (filter != 0)
{
if (filter_type == FILTER_EQ && filter_value != filter[i])
continue;
if (filter_type == FILTER_NEQ && filter_value == filter[i])
continue;
}
result.push(i);
}
}
void Graph::_mergeWithSubgraph(const Graph& other, const Array<int>& vertices, const Array<int>* edges, Array<int>* vertex_mapping, Array<int>* edge_mapping)
{
QS_DEF(Array<int>, tmp_mapping);
int i;
if (vertex_mapping == 0)
vertex_mapping = &tmp_mapping;
vertex_mapping->clear_resize(other.vertexEnd());
vertex_mapping->fffill();
if (edge_mapping != 0)
{
edge_mapping->clear_resize(other.edgeEnd());
edge_mapping->fffill();
}
for (i = 0; i < vertices.size(); i++)
{
int idx = vertices[i];
if (vertex_mapping->at(idx) != -1)
throw Error("makeSubgraph(): repeated vertex #%d", idx);
vertex_mapping->at(idx) = addVertex();
}
if (edges != 0)
{
for (i = 0; i != edges->size(); i++)
{
const Edge& edge = other.getEdge(edges->at(i));
int beg = vertex_mapping->at(edge.beg);
int end = vertex_mapping->at(edge.end);
if (beg == -1 || end == -1)
throw Error("_mergeWithSubgraph: edge %d maps to (%d, %d)", edges->at(i), beg, end);
int idx = addEdge(beg, end);
if (edge_mapping != 0)
edge_mapping->at(edges->at(i)) = idx;
}
}
else
for (i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i))
{
const Edge& edge = other.getEdge(i);
int beg = vertex_mapping->at(edge.beg);
int end = vertex_mapping->at(edge.end);
if (beg != -1 && end != -1)
{
int idx = addEdge(beg, end);
if (edge_mapping != 0)
edge_mapping->at(i) = idx;
}
}
}
void Graph::buildEdgeMapping(const Graph& other, Array<int>* mapping, Array<int>* edge_mapping)
{
for (int i = other.edgeBegin(); i < other.edgeEnd(); i = other.edgeNext(i))
{
const Edge& edge = other.getEdge(i);
int beg = mapping->at(edge.beg);
int end = mapping->at(edge.end);
if (beg != -1 && end != -1)
{
int idx = findEdgeIndex(beg, end);
if (edge_mapping != 0)
edge_mapping->at(i) = idx;
}
}
}
void Graph::mergeWith(const Graph& other, Array<int>* mapping)
{
QS_DEF(Array<int>, vertices);
int i;
vertices.clear();
for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
vertices.push(i);
_mergeWithSubgraph(other, vertices, 0, mapping, 0);
}
void Graph::makeSubgraph(const Graph& other, const Array<int>& vertices, Array<int>* vertex_mapping)
{
clear();
_mergeWithSubgraph(other, vertices, 0, vertex_mapping, 0);
}
void Graph::makeSubgraph(const Graph& other, const Array<int>& vertices, Array<int>* vertex_mapping, const Array<int>* edges, Array<int>* edge_mapping)
{
clear();
_mergeWithSubgraph(other, vertices, edges, vertex_mapping, edge_mapping);
}
void Graph::makeSubgraph(const Graph& other, const Filter& filter, Array<int>* mapping_out, Array<int>* inv_mapping)
{
QS_DEF(Array<int>, vertices);
if (mapping_out == 0)
mapping_out = &vertices;
filter.collectGraphVertices(other, *mapping_out);
makeSubgraph(other, *mapping_out, inv_mapping);
}
void Graph::cloneGraph(const Graph& other, Array<int>* mapping)
{
QS_DEF(Array<int>, vertices);
vertices.clear();
for (int i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i))
vertices.push(i);
makeSubgraph(other, vertices, mapping);
}
void Graph::makeEdgeSubgraph(const Graph& other, const Array<int>& vertices, const Array<int>& edges, Array<int>* v_mapping, Array<int>* e_mapping)
{
QS_DEF(Array<int>, tmp_mapping);
int i;
if (v_mapping == 0)
v_mapping = &tmp_mapping;
v_mapping->clear_resize(other.vertexEnd());
for (i = other.vertexBegin(); i < other.vertexEnd(); i = other.vertexNext(i))
v_mapping->at(i) = -1;
if (e_mapping != 0)
e_mapping->clear_resize(other.edgeEnd());
clear();
for (i = 0; i < vertices.size(); i++)
{
int idx = vertices[i];
if (v_mapping->at(idx) != -1)
throw Error("makeEdgeSubgraph(): repeated vertex #%d", idx);
v_mapping->at(idx) = addVertex();
}
for (i = 0; i < edges.size(); i++)
{
int edge_idx = edges[i];
const Edge& edge = other.getEdge(edge_idx);
int beg = v_mapping->at(edge.beg);
int end = v_mapping->at(edge.end);
int new_edge_idx = addEdge(beg, end);
if (e_mapping != 0)
e_mapping->at(edge_idx) = new_edge_idx;
}
}
int Graph::findMappedEdge(const Graph& graph, const Graph& mapped_graph, int edge_idx, const int* mapping)
{
const Edge& edge = graph.getEdge(edge_idx);
int beg = mapping[edge.beg];
int end = mapping[edge.end];
if (beg == -1 || end == -1)
return -1;
return mapped_graph.findEdgeIndex(beg, end);
}
int Graph::getEdgeTopology(int idx)
{
if (!_topology_valid)
_calculateTopology();
return _topology[idx];
}
bool Graph::vertexInRing(int idx)
{
const Vertex& vertex = getVertex(idx);
int i;
for (i = vertex.neiBegin(); i != vertex.neiEnd(); i = vertex.neiNext(i))
if (getEdgeTopology(vertex.neiEdge(i)) == TOPOLOGY_RING)
return true;
return false;
}
void Graph::_calculateTopology()
{
SpanningTree spt(*this, 0);
int i;
_topology.clear_resize(_edges.end());
for (i = _edges.begin(); i != _edges.end(); i = _edges.next(i))
_topology[i] = TOPOLOGY_CHAIN;
spt.markAllEdgesInCycles(_topology.ptr(), TOPOLOGY_RING);
_topology_valid = true;
}
void Graph::setEdgeTopology(int idx, int topology)
{
_topology.expandFill(idx + 1, -1);
_topology[idx] = topology;
}
void Graph::validateEdgeTopologies()
{
_topology_valid = true;
}
int Graph::vertexCountSSSR(int idx)
{
if (!_sssr_valid)
_calculateSSSR();
return _v_sssr_count[idx];
}
int Graph::vertexSmallestRingSize(int idx)
{
if (!_sssr_valid)
_calculateSSSR();
return _v_smallest_ring_size[idx];
}
int Graph::edgeSmallestRingSize(int idx)
{
if (!_sssr_valid)
_calculateSSSR();
return _e_smallest_ring_size[idx];
}
void Graph::_calculateSSSRInit()
{
_v_smallest_ring_size.clear_resize(vertexEnd());
_e_smallest_ring_size.clear_resize(edgeEnd());
_v_sssr_count.clear_resize(vertexEnd());
_v_smallest_ring_size.fffill();
_e_smallest_ring_size.fffill();
_v_sssr_count.zerofill();
if (_sssr_pool == 0)
_sssr_pool = new Pool<List<int>::Elem>();
_sssr_vertices.clear();
_sssr_edges.clear();
}
void Graph::_calculateSSSRByCycleBasis(CycleBasis& basis)
{
_calculateSSSRInit();
for (int i = 0; i < basis.getCyclesCount(); i++)
{
const Array<int>& cycle = basis.getCycle(i);
List<int>& vertices = _sssr_vertices.push(*_sssr_pool);
List<int>& edges = _sssr_edges.push(*_sssr_pool);
_calculateSSSRAddEdgesAndVertices(cycle, edges, vertices);
for (int j = vertices.begin(); j != vertices.end(); j = vertices.next(j))
{
int idx = vertices[j];
if (_v_smallest_ring_size[idx] == -1 || _v_smallest_ring_size[idx] > cycle.size())
_v_smallest_ring_size[idx] = cycle.size();
_v_sssr_count[idx]++;
}
for (int j = edges.begin(); j != edges.end(); j = edges.next(j))
{
int idx = edges[j];
if (_e_smallest_ring_size[idx] == -1 || _e_smallest_ring_size[idx] > cycle.size())
_e_smallest_ring_size[idx] = cycle.size();
}
}
for (int i = 0; i < _v_smallest_ring_size.size(); i++)
if (_v_smallest_ring_size[i] == -1)
_v_smallest_ring_size[i] = 0;
for (int i = 0; i < _e_smallest_ring_size.size(); i++)
if (_e_smallest_ring_size[i] == -1)
_e_smallest_ring_size[i] = 0;
_sssr_valid = true;
}
void Graph::_calculateSSSR()
{
// Note: function was split into smaller functions to reduce stack usage
QS_DEF(CycleBasis, basis);
basis.create(*this);
_calculateSSSRByCycleBasis(basis);
}
void Graph::_calculateComponents(const std::list<std::unordered_set<int>> external_neighbors)
{
GraphDecomposer decomposer(*this);
int i;
decomposer.decompose(NULL, NULL, &external_neighbors);
_component_numbers.clear_resize(vertexEnd());
for (i = vertexBegin(); i != vertexEnd(); i = vertexNext(i))
_component_numbers[i] = decomposer.getComponent(i);
_components_count = decomposer.getComponentsCount();
_component_vcount.clear_resize(_components_count);
_component_ecount.clear_resize(_components_count);
for (i = 0; i < _components_count; i++)
{
_component_vcount[i] = decomposer.getComponentVerticesCount(i);
_component_ecount[i] = decomposer.getComponentEdgesCount(i);
}
_components_valid = true;
}
int Graph::vertexComponent(int v_idx)
{
if (!_components_valid)
_calculateComponents();
return _component_numbers[v_idx];
}
int Graph::countComponents(const std::list<std::unordered_set<int>>& external_neighbors)
{
if (!_components_valid)
_calculateComponents(external_neighbors);
return _components_count;
}
int Graph::countComponentEdges(int comp_idx, const std::list<std::unordered_set<int>>& external_neighbors)
{
if (!_components_valid)
_calculateComponents(external_neighbors);
return _component_ecount[comp_idx];
}
int Graph::countComponentVertices(int comp_idx, const std::list<std::unordered_set<int>>& external_neighbors)
{
if (!_components_valid)
_calculateComponents(external_neighbors);
return _component_vcount[comp_idx];
}
int Graph::countComponents()
{
if (!_components_valid)
_calculateComponents();
return _components_count;
}
int Graph::countComponentEdges(int comp_idx)
{
if (!_components_valid)
_calculateComponents();
return _component_ecount[comp_idx];
}
int Graph::countComponentVertices(int comp_idx)
{
if (!_components_valid)
_calculateComponents();
return _component_vcount[comp_idx];
}
const Array<int>& Graph::getDecomposition()
{
if (!_components_valid)
_calculateComponents();
return _component_numbers;
}
List<int>& Graph::sssrEdges(int idx)
{
if (!_sssr_valid)
_calculateSSSR();
return _sssr_edges[idx];
}
List<int>& Graph::sssrVertices(int idx)
{
if (!_sssr_valid)
_calculateSSSR();
return _sssr_vertices[idx];
}
int Graph::sssrCount()
{
if (!_sssr_valid)
_calculateSSSR();
return _sssr_vertices.size();
}
void Graph::_cloneGraph_KeepIndices(const Graph& other)
{
if (vertexCount() > 0 || edgeCount() > 0)
throw Error("can not _clone_KeepIndices into a non-empty graph");
int i, j, i_prev;
int max_vertex_idx = -1;
int max_edge_idx = -1;
for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
if (max_vertex_idx < i)
max_vertex_idx = i;
for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i))
if (max_edge_idx < i)
max_edge_idx = i;
for (i = 0; i <= max_vertex_idx; i++)
if (addVertex() != i)
throw Error("_clone_KeepIndices: unexpected vertex index");
i_prev = -1;
for (i = other.vertexBegin(); i != other.vertexEnd(); i = other.vertexNext(i))
{
for (j = i_prev + 1; j < i; j++)
removeVertex(j);
i_prev = i;
}
if (vertexCount() != other.vertexCount())
throw Error("_clone_KeepIndices: internal");
for (i = 0; i <= max_edge_idx; i++)
if (_edges.add() != i)
throw Error("_clone_KeepIndices: unexpected edge index");
i_prev = -1;
for (i = other.edgeBegin(); i != other.edgeEnd(); i = other.edgeNext(i))
{
for (j = i_prev + 1; j < i; j++)
_edges.remove(j);
_edges[i].beg = other._edges[i].beg;
_edges[i].end = other._edges[i].end;
Vertex& vbeg = _vertices->at(_edges[i].beg);
Vertex& vend = _vertices->at(_edges[i].end);
int ve1_idx = vbeg.neighbors_list.add();
int ve2_idx = vend.neighbors_list.add();
VertexEdge& ve1 = vbeg.neighbors_list[ve1_idx];
VertexEdge& ve2 = vend.neighbors_list[ve2_idx];
ve1.v = _edges[i].end;
ve2.v = _edges[i].beg;
ve1.e = i;
ve2.e = i;
i_prev = i;
}
if (edgeCount() != other.edgeCount())
throw Error("_clone_KeepIndices: internal");
_topology_valid = false;
_sssr_valid = false;
_components_valid = false;
}
void Graph::_calculateSSSRAddEdgesAndVertices(const Array<int>& cycle, List<int>& edges, List<int>& vertices)
{
int prev_beg = -1;
int prev_end = -1;
for (int j = 0; j < cycle.size(); j++)
{
const Edge& edge = getEdge(cycle[j]);
edges.add(cycle[j]);
if (j != cycle.size() - 1)
{
if (edge.beg != prev_beg && edge.beg != prev_end)
vertices.add(edge.beg);
if (edge.end != prev_beg && edge.end != prev_end)
vertices.add(edge.end);
}
prev_beg = edge.beg;
prev_end = edge.end;
}
}
bool Graph::isTerminalVertex(int v_idx) const
{
return getVertex(v_idx).degree() == 1;
}
bool Graph::isTerminalEdge(int e_idx) const
{
const auto& edge = getEdge(e_idx);
return isTerminalVertex(edge.beg) || isTerminalVertex(edge.end);
}