more graph cleaning
This commit is contained in:
@@ -366,10 +366,8 @@ cc_test(
|
||||
srcs = ["ebert_graph_test.cc"],
|
||||
deps = [
|
||||
":ebert_graph",
|
||||
"//ortools/base",
|
||||
"//ortools/base:gmock_main",
|
||||
"//ortools/util:permutation",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/random:distributions",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_benchmark//:benchmark",
|
||||
@@ -617,6 +615,17 @@ cc_test(
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "line_graph_test",
|
||||
srcs = ["line_graph_test.cc"],
|
||||
deps = [
|
||||
":graph",
|
||||
":line_graph",
|
||||
"//ortools/base:gmock_main",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
],
|
||||
)
|
||||
|
||||
# Linear Assignment with full-featured interface and efficient
|
||||
# implementation.
|
||||
cc_library(
|
||||
@@ -717,6 +726,12 @@ cc_library(
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "line_graph",
|
||||
hdrs = ["line_graph.h"],
|
||||
deps = ["@com_google_absl//absl/log"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "rooted_tree_test",
|
||||
srcs = ["rooted_tree_test.cc"],
|
||||
|
||||
@@ -1718,53 +1718,6 @@ class AnnotatedGraphBuildManager
|
||||
num_nodes, num_arcs, sort_arcs) {}
|
||||
};
|
||||
|
||||
// Builds a directed line graph for 'graph' (see "directed line graph" in
|
||||
// http://en.wikipedia.org/wiki/Line_graph). Arcs of the original graph
|
||||
// become nodes and the new graph contains only nodes created from arcs in the
|
||||
// original graph (we use the notation (a->b) for these new nodes); the index
|
||||
// of the node (a->b) in the new graph is exactly the same as the index of the
|
||||
// arc a->b in the original graph.
|
||||
// An arc from node (a->b) to node (c->d) in the new graph is added if and only
|
||||
// if b == c in the original graph.
|
||||
// This method expects that 'line_graph' is an empty graph (it has no nodes
|
||||
// and no arcs).
|
||||
// Returns false on an error.
|
||||
template <typename GraphType>
|
||||
bool BuildLineGraph(const GraphType& graph, GraphType* const line_graph) {
|
||||
if (line_graph == nullptr) {
|
||||
LOG(DFATAL) << "line_graph must not be NULL";
|
||||
return false;
|
||||
}
|
||||
if (line_graph->num_nodes() != 0) {
|
||||
LOG(DFATAL) << "line_graph must be empty";
|
||||
return false;
|
||||
}
|
||||
typedef typename GraphType::ArcIterator ArcIterator;
|
||||
typedef typename GraphType::OutgoingArcIterator OutgoingArcIterator;
|
||||
// Sizing then filling.
|
||||
typename GraphType::ArcIndex num_arcs = 0;
|
||||
for (ArcIterator arc_iterator(graph); arc_iterator.Ok();
|
||||
arc_iterator.Next()) {
|
||||
const typename GraphType::ArcIndex arc = arc_iterator.Index();
|
||||
const typename GraphType::NodeIndex head = graph.Head(arc);
|
||||
for (OutgoingArcIterator iterator(graph, head); iterator.Ok();
|
||||
iterator.Next()) {
|
||||
++num_arcs;
|
||||
}
|
||||
}
|
||||
line_graph->Reserve(graph.num_arcs(), num_arcs);
|
||||
for (ArcIterator arc_iterator(graph); arc_iterator.Ok();
|
||||
arc_iterator.Next()) {
|
||||
const typename GraphType::ArcIndex arc = arc_iterator.Index();
|
||||
const typename GraphType::NodeIndex head = graph.Head(arc);
|
||||
for (OutgoingArcIterator iterator(graph, head); iterator.Ok();
|
||||
iterator.Next()) {
|
||||
line_graph->AddArc(arc, iterator.Index());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef DEFINE_STL_ITERATOR_FUNCTIONS
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -17,13 +17,11 @@
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/random/distributions.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/macros.h"
|
||||
#include "ortools/util/permutation.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -1092,90 +1090,6 @@ TEST(ForwardEbertGraphTest, ImpossibleBuildTailArray) {
|
||||
EXPECT_FALSE(g.BuildTailArray());
|
||||
}
|
||||
|
||||
// Testing line graph creation.
|
||||
|
||||
// Empty fixture templates to collect the types of graphs on which we want to
|
||||
// base the shortest paths template instances that we test.
|
||||
template <typename GraphType>
|
||||
class LineGraphDeathTest : public testing::Test {};
|
||||
template <typename GraphType>
|
||||
class LineGraphTest : public testing::Test {};
|
||||
|
||||
typedef testing::Types<StarGraph, ForwardStarGraph>
|
||||
GraphTypesForLineGraphTesting;
|
||||
|
||||
TYPED_TEST_SUITE(LineGraphDeathTest, GraphTypesForLineGraphTesting);
|
||||
TYPED_TEST_SUITE(LineGraphTest, GraphTypesForLineGraphTesting);
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, NullLineGraph) {
|
||||
TypeParam graph;
|
||||
#ifndef NDEBUG
|
||||
EXPECT_DEATH(BuildLineGraph<TypeParam>(graph, nullptr),
|
||||
"line_graph must not be NULL");
|
||||
#else
|
||||
EXPECT_FALSE(BuildLineGraph<TypeParam>(graph, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, NonEmptyLineGraph) {
|
||||
TypeParam graph;
|
||||
TypeParam line_graph(1, 1);
|
||||
line_graph.AddArc(0, 0);
|
||||
#ifndef NDEBUG
|
||||
EXPECT_DEATH(BuildLineGraph<TypeParam>(graph, &line_graph),
|
||||
"line_graph must be empty");
|
||||
#else
|
||||
EXPECT_FALSE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
#endif
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, LineGraphOfEmptyGraph) {
|
||||
TypeParam graph;
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(0, line_graph.num_nodes());
|
||||
EXPECT_EQ(0, line_graph.num_arcs());
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphTest, LineGraphOfSingleton) {
|
||||
TypeParam graph(1, 1);
|
||||
graph.AddArc(0, 0);
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(1, line_graph.num_nodes());
|
||||
EXPECT_EQ(1, line_graph.num_arcs());
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphTest, LineGraph) {
|
||||
const NodeIndex kNodes = 4;
|
||||
const ArcIndex kArcs[][2] = {{0, 1}, {0, 2}, {1, 2}, {2, 0}, {2, 3}};
|
||||
const ArcIndex kExpectedLineArcs[][2] = {{0, 2}, {2, 3}, {3, 0}, {3, 1},
|
||||
{2, 4}, {1, 4}, {1, 3}};
|
||||
TypeParam graph(kNodes, ABSL_ARRAYSIZE(kArcs));
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(kArcs); ++i) {
|
||||
graph.AddArc(kArcs[i][0], kArcs[i][1]);
|
||||
}
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(ABSL_ARRAYSIZE(kArcs), line_graph.num_nodes());
|
||||
EXPECT_EQ(ABSL_ARRAYSIZE(kExpectedLineArcs), line_graph.num_arcs());
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(kExpectedLineArcs); ++i) {
|
||||
const NodeIndex expected_tail = kExpectedLineArcs[i][0];
|
||||
const NodeIndex expected_head = kExpectedLineArcs[i][1];
|
||||
bool found = false;
|
||||
for (typename TypeParam::OutgoingArcIterator out_iterator(line_graph,
|
||||
expected_tail);
|
||||
out_iterator.Ok(); out_iterator.Next()) {
|
||||
const ArcIndex arc = out_iterator.Index();
|
||||
if (line_graph.Head(arc) == expected_head) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found) << expected_tail << " " << expected_head;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename GraphType, bool sort_arcs>
|
||||
static void BM_RandomArcs(benchmark::State& state) {
|
||||
const int kRandomSeed = 0;
|
||||
|
||||
62
ortools/graph/line_graph.h
Normal file
62
ortools/graph/line_graph.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2010-2024 Google LLC
|
||||
// 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.
|
||||
|
||||
#ifndef OR_TOOLS_GRAPH_LINE_GRAPH_H_
|
||||
#define OR_TOOLS_GRAPH_LINE_GRAPH_H_
|
||||
|
||||
#include "ortools/base/logging.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
// Builds a directed line graph for `graph` (see "directed line graph" in
|
||||
// http://en.wikipedia.org/wiki/Line_graph). Arcs of the original graph
|
||||
// become nodes and the new graph contains only nodes created from arcs in the
|
||||
// original graph (we use the notation (a->b) for these new nodes); the index
|
||||
// of the node (a->b) in the new graph is exactly the same as the index of the
|
||||
// arc a->b in the original graph.
|
||||
// An arc from node (a->b) to node (c->d) in the new graph is added if and only
|
||||
// if b == c in the original graph.
|
||||
// This method expects that `line_graph` is an empty graph (it has no nodes
|
||||
// and no arcs).
|
||||
// Returns false on an error.
|
||||
template <typename GraphType>
|
||||
bool BuildLineGraph(const GraphType& graph, GraphType* const line_graph) {
|
||||
if (line_graph == nullptr) {
|
||||
LOG(DFATAL) << "line_graph must not be NULL";
|
||||
return false;
|
||||
}
|
||||
if (line_graph->num_nodes() != 0) {
|
||||
LOG(DFATAL) << "line_graph must be empty";
|
||||
return false;
|
||||
}
|
||||
// Sizing then filling.
|
||||
using NodeIndex = typename GraphType::NodeIndex;
|
||||
using ArcIndex = typename GraphType::ArcIndex;
|
||||
ArcIndex num_arcs = 0;
|
||||
for (const ArcIndex arc : graph.AllForwardArcs()) {
|
||||
const NodeIndex head = graph.Head(arc);
|
||||
num_arcs += graph.OutDegree(head);
|
||||
}
|
||||
line_graph->Reserve(graph.num_arcs(), num_arcs);
|
||||
for (const ArcIndex arc : graph.AllForwardArcs()) {
|
||||
const NodeIndex head = graph.Head(arc);
|
||||
for (const ArcIndex outgoing_arc : graph.OutgoingArcs(head)) {
|
||||
line_graph->AddArc(arc, outgoing_arc);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_GRAPH_LINE_GRAPH_H_
|
||||
107
ortools/graph/line_graph_test.cc
Normal file
107
ortools/graph/line_graph_test.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2010-2024 Google LLC
|
||||
// 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 "ortools/graph/line_graph.h"
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/graph/graph.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace {
|
||||
|
||||
// Empty fixture templates to collect the types of graphs on which we want to
|
||||
// base the shortest paths template instances that we test.
|
||||
template <typename GraphType>
|
||||
class LineGraphDeathTest : public testing::Test {};
|
||||
template <typename GraphType>
|
||||
class LineGraphTest : public testing::Test {};
|
||||
|
||||
typedef testing::Types<::util::ListGraph<>, ::util::ReverseArcListGraph<>>
|
||||
GraphTypesForLineGraphTesting;
|
||||
|
||||
TYPED_TEST_SUITE(LineGraphDeathTest, GraphTypesForLineGraphTesting);
|
||||
TYPED_TEST_SUITE(LineGraphTest, GraphTypesForLineGraphTesting);
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, NullLineGraph) {
|
||||
TypeParam graph;
|
||||
#ifndef NDEBUG
|
||||
EXPECT_DEATH(BuildLineGraph<TypeParam>(graph, nullptr),
|
||||
"line_graph must not be NULL");
|
||||
#else
|
||||
EXPECT_FALSE(BuildLineGraph<TypeParam>(graph, nullptr));
|
||||
#endif
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, NonEmptyLineGraph) {
|
||||
TypeParam graph;
|
||||
TypeParam line_graph(1, 1);
|
||||
line_graph.AddArc(0, 0);
|
||||
#ifndef NDEBUG
|
||||
EXPECT_DEATH(BuildLineGraph<TypeParam>(graph, &line_graph),
|
||||
"line_graph must be empty");
|
||||
#else
|
||||
EXPECT_FALSE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
#endif
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphDeathTest, LineGraphOfEmptyGraph) {
|
||||
TypeParam graph;
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(0, line_graph.num_nodes());
|
||||
EXPECT_EQ(0, line_graph.num_arcs());
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphTest, LineGraphOfSingleton) {
|
||||
TypeParam graph(1, 1);
|
||||
graph.AddArc(0, 0);
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(1, line_graph.num_nodes());
|
||||
EXPECT_EQ(1, line_graph.num_arcs());
|
||||
}
|
||||
|
||||
TYPED_TEST(LineGraphTest, LineGraph) {
|
||||
const typename TypeParam::NodeIndex kNodes = 4;
|
||||
const typename TypeParam::ArcIndex kArcs[][2] = {
|
||||
{0, 1}, {0, 2}, {1, 2}, {2, 0}, {2, 3}};
|
||||
const typename TypeParam::ArcIndex kExpectedLineArcs[][2] = {
|
||||
{0, 2}, {2, 3}, {3, 0}, {3, 1}, {2, 4}, {1, 4}, {1, 3}};
|
||||
TypeParam graph(kNodes, ABSL_ARRAYSIZE(kArcs));
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(kArcs); ++i) {
|
||||
graph.AddArc(kArcs[i][0], kArcs[i][1]);
|
||||
}
|
||||
TypeParam line_graph;
|
||||
EXPECT_TRUE(BuildLineGraph<TypeParam>(graph, &line_graph));
|
||||
EXPECT_EQ(ABSL_ARRAYSIZE(kArcs), line_graph.num_nodes());
|
||||
EXPECT_EQ(ABSL_ARRAYSIZE(kExpectedLineArcs), line_graph.num_arcs());
|
||||
for (int i = 0; i < ABSL_ARRAYSIZE(kExpectedLineArcs); ++i) {
|
||||
const typename TypeParam::NodeIndex expected_tail = kExpectedLineArcs[i][0];
|
||||
const typename TypeParam::NodeIndex expected_head = kExpectedLineArcs[i][1];
|
||||
bool found = false;
|
||||
for (typename TypeParam::OutgoingArcIterator out_iterator(line_graph,
|
||||
expected_tail);
|
||||
out_iterator.Ok(); out_iterator.Next()) {
|
||||
const typename TypeParam::ArcIndex arc = out_iterator.Index();
|
||||
if (line_graph.Head(arc) == expected_head) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found) << expected_tail << " " << expected_head;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace operations_research
|
||||
Reference in New Issue
Block a user