Skip to content

Commit

Permalink
graph: remove ebert_graph usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Mizux committed Jan 8, 2025
1 parent 847e205 commit a5adf42
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 104 deletions.
4 changes: 0 additions & 4 deletions ortools/graph/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ cc_library(
srcs = ["minimum_vertex_cover.cc"],
hdrs = ["minimum_vertex_cover.h"],
deps = [
":ebert_graph",
":max_flow",
"@com_google_absl//absl/log:check",
],
Expand Down Expand Up @@ -398,7 +397,6 @@ cc_test(
srcs = ["shortest_paths_test.cc"],
tags = ["noasan"], # Times out occasionally in ASAN mode.
deps = [
":ebert_graph",
":graph",
":shortest_paths",
":strongly_connected_components",
Expand Down Expand Up @@ -471,7 +469,6 @@ cc_test(
srcs = ["max_flow_test.cc"],
data = ["//ortools/graph/testdata:max_flow_test1.pb.txt"],
deps = [
":ebert_graph",
":flow_problem_cc_proto",
":max_flow",
"//ortools/base:gmock_main",
Expand Down Expand Up @@ -681,7 +678,6 @@ cc_library(
srcs = ["dag_shortest_path.cc"],
hdrs = ["dag_shortest_path.h"],
deps = [
":ebert_graph",
":graph",
":topologicalsorter",
"@com_google_absl//absl/algorithm:container",
Expand Down
2 changes: 0 additions & 2 deletions ortools/graph/csharp/graph.i
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@

%include "ortools/base/base.i"

%import "ortools/graph/ebert_graph.h"

%{
#include "ortools/graph/assignment.h"
#include "ortools/graph/max_flow.h"
Expand Down
7 changes: 3 additions & 4 deletions ortools/graph/dag_shortest_path.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/graph.h"
#include "ortools/graph/topologicalsorter.h"

Expand Down Expand Up @@ -109,7 +108,7 @@ std::vector<PathWithLength> KShortestPathsOnDag(
const ShortestPathOnDagProblem problem =
ReadProblem(num_nodes, arcs_with_length);

KShortestPathsOnDagWrapper<util::StaticGraph<>> shortest_paths_on_dag(
KShortestPathsOnDagWrapper<GraphType> shortest_paths_on_dag(
&problem.graph, &problem.arc_lengths, problem.topological_order,
path_count);
shortest_paths_on_dag.RunKShortestPathOnDag({source});
Expand All @@ -119,9 +118,9 @@ std::vector<PathWithLength> KShortestPathsOnDag(
}

std::vector<double> lengths = shortest_paths_on_dag.LengthsTo(destination);
std::vector<std::vector<ArcIndex>> arc_paths =
std::vector<std::vector<GraphType::ArcIndex>> arc_paths =
shortest_paths_on_dag.ArcPathsTo(destination);
std::vector<std::vector<NodeIndex>> node_paths =
std::vector<std::vector<GraphType::NodeIndex>> node_paths =
shortest_paths_on_dag.NodePathsTo(destination);
std::vector<PathWithLength> paths;
paths.reserve(lengths.size());
Expand Down
2 changes: 0 additions & 2 deletions ortools/graph/java/graph.i
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@

%include "ortools/base/base.i"

%import "ortools/graph/ebert_graph.h"

%{
#include "ortools/graph/assignment.h"
#include "ortools/graph/max_flow.h"
Expand Down
92 changes: 52 additions & 40 deletions ortools/graph/k_shortest_paths.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/graph/bounded_dijkstra.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/shortest_paths.h"

namespace operations_research {
Expand All @@ -82,14 +81,15 @@ namespace operations_research {
// The paths in `paths` start with `origin` and end at `destination`.
//
// If the computations are unsuccessful for any reason, the vectors are empty.
template <class GraphType>
struct KShortestPaths {
// The paths are stored as vectors of nodes, like the other graph algorithms.
// TODO(user): what about vectors of arcs? That might be faster
// (potentially, add a function to transform it into a vector of nodes if the
// user really needs it). It would also have the nice benefit of removing the
// need for `distances` (compute it on the fly), with a reference to the graph
// and the costs.
std::vector<std::vector<NodeIndex>> paths;
std::vector<std::vector<typename GraphType::NodeIndex>> paths;
std::vector<PathDistance> distances;
};

Expand All @@ -113,10 +113,10 @@ struct KShortestPaths {
// Science. 17 (11): 712–716, 1971.
// https://doi.org/10.1287%2Fmnsc.17.11.712
template <class GraphType>
KShortestPaths YenKShortestPaths(const GraphType& graph,
const std::vector<PathDistance>& arc_lengths,
NodeIndex source, NodeIndex destination,
unsigned k);
KShortestPaths<GraphType> YenKShortestPaths(
const GraphType& graph, const std::vector<PathDistance>& arc_lengths,
typename GraphType::NodeIndex source,
typename GraphType::NodeIndex destination, unsigned k);

// End of the interface. Below is the implementation.

Expand All @@ -137,23 +137,26 @@ const PathDistance kDisconnectedDistance =
// In a multigraph, this function returns an index for one of the edges between
// the source and the destination.
template <class GraphType>
ArcIndex FindArcIndex(const GraphType& graph, const NodeIndex source,
const NodeIndex destination) {
typename GraphType::ArcIndex FindArcIndex(
const GraphType& graph, const typename GraphType::NodeIndex source,
const typename GraphType::NodeIndex destination) {
const auto outgoing_arcs_iter = graph.OutgoingArcs(source);
const auto arc =
std::find_if(outgoing_arcs_iter.begin(), outgoing_arcs_iter.end(),
[&graph, destination](const ArcIndex arc) {
return graph.Head(arc) == destination;
});
const auto arc = std::find_if(
outgoing_arcs_iter.begin(), outgoing_arcs_iter.end(),
[&graph, destination](const typename GraphType::ArcIndex arc) {
return graph.Head(arc) == destination;
});
return (arc != outgoing_arcs_iter.end()) ? *arc : GraphType::kNilArc;
}

// Determines the shortest path from the given source and destination, returns a
// tuple with the path (as a vector of node indices) and its cost.
template <class GraphType>
std::tuple<std::vector<NodeIndex>, PathDistance> ComputeShortestPath(
const GraphType& graph, const std::vector<PathDistance>& arc_lengths,
const NodeIndex source, const NodeIndex destination) {
std::tuple<std::vector<typename GraphType::NodeIndex>, PathDistance>
ComputeShortestPath(const GraphType& graph,
const std::vector<PathDistance>& arc_lengths,
const typename GraphType::NodeIndex source,
const typename GraphType::NodeIndex destination) {
BoundedDijkstraWrapper<GraphType, PathDistance> dijkstra(&graph,
&arc_lengths);
dijkstra.RunBoundedDijkstra(source, kMaxDistance);
Expand All @@ -165,25 +168,29 @@ std::tuple<std::vector<NodeIndex>, PathDistance> ComputeShortestPath(
// This case only happens when some arcs have an infinite length (i.e.
// larger than `kMaxDistance`): `BoundedDijkstraWrapper::NodePathTo` fails
// to return a path, even empty.
return {std::vector<NodeIndex>{}, kDisconnectedDistance};
return {std::vector<typename GraphType::NodeIndex>{},
kDisconnectedDistance};
}

if (std::vector<NodeIndex> path = std::move(dijkstra.NodePathTo(destination));
if (std::vector<typename GraphType::NodeIndex> path =
std::move(dijkstra.NodePathTo(destination));
!path.empty()) {
return {std::move(path), path_length};
} else {
return {std::vector<NodeIndex>{}, kDisconnectedDistance};
return {std::vector<typename GraphType::NodeIndex>{},
kDisconnectedDistance};
}
}

// Computes the total length of a path.
template <class GraphType>
PathDistance ComputePathLength(const GraphType& graph,
const absl::Span<const PathDistance> arc_lengths,
const absl::Span<const NodeIndex> path) {
PathDistance ComputePathLength(
const GraphType& graph, const absl::Span<const PathDistance> arc_lengths,
const absl::Span<const typename GraphType::NodeIndex> path) {
PathDistance distance = 0;
for (NodeIndex i = 0; i < path.size() - 1; ++i) {
const ArcIndex arc = internal::FindArcIndex(graph, path[i], path[i + 1]);
for (typename GraphType::NodeIndex i = 0; i < path.size() - 1; ++i) {
const typename GraphType::ArcIndex arc =
internal::FindArcIndex(graph, path[i], path[i + 1]);
DCHECK_NE(arc, GraphType::kNilArc);
distance += arc_lengths[arc];
}
Expand All @@ -192,8 +199,11 @@ PathDistance ComputePathLength(const GraphType& graph,

// Stores a path with a priority (typically, the distance), with a comparison
// operator that operates on the priority.
template <class GraphType>
class PathWithPriority {
public:
using NodeIndex = typename GraphType::NodeIndex;

PathWithPriority(PathDistance priority, std::vector<NodeIndex> path)
: path_(std::move(path)), priority_(priority) {}
bool operator<(const PathWithPriority& other) const {
Expand Down Expand Up @@ -265,10 +275,12 @@ class UnderlyingContainerAdapter : public Container {
// spur paths, the cheapest being:
// S_1^2 = B - E - F - G - H
template <class GraphType>
KShortestPaths YenKShortestPaths(const GraphType& graph,
const std::vector<PathDistance>& arc_lengths,
NodeIndex source, NodeIndex destination,
unsigned k) {
KShortestPaths<GraphType> YenKShortestPaths(
const GraphType& graph, const std::vector<PathDistance>& arc_lengths,
typename GraphType::NodeIndex source,
typename GraphType::NodeIndex destination, unsigned k) {
using NodeIndex = typename GraphType::NodeIndex;

CHECK_GT(internal::kDisconnectedDistance, internal::kMaxDistance);

CHECK_GE(k, 0) << "k must be nonnegative. Input value: " << k;
Expand All @@ -289,7 +301,7 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,
<< destination
<< ". Number of nodes in the input graph: " << graph.num_nodes();

KShortestPaths paths;
KShortestPaths<GraphType> paths;

// First step: compute the shortest path.
{
Expand All @@ -306,7 +318,7 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,

// Generate variant paths.
internal::UnderlyingContainerAdapter<
std::priority_queue<internal::PathWithPriority>>
std::priority_queue<internal::PathWithPriority<GraphType>>>
variant_path_queue;

// One path has already been generated (the shortest one). Only k-1 more
Expand Down Expand Up @@ -364,7 +376,7 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,
previous_path.begin() + root_path.length());
if (!has_same_prefix_as_root_path) continue;

const ArcIndex after_spur_node_arc =
const typename GraphType::ArcIndex after_spur_node_arc =
internal::FindArcIndex(graph, previous_path[spur_node_position],
previous_path[spur_node_position + 1]);
VLOG(4) << " after_spur_node_arc: " << graph.Tail(after_spur_node_arc)
Expand Down Expand Up @@ -417,8 +429,8 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,
// coincide at the spur node).
const bool root_path_leads_to_spur_path = absl::c_any_of(
graph.OutgoingArcs(root_path.back()),
[&graph, node_after_spur_in_spur_path =
*(spur_path.begin() + 1)](const ArcIndex arc_index) {
[&graph, node_after_spur_in_spur_path = *(spur_path.begin() + 1)](
const typename GraphType::ArcIndex arc_index) {
return graph.Head(arc_index) == node_after_spur_in_spur_path;
});
CHECK(root_path_leads_to_spur_path);
Expand Down Expand Up @@ -471,12 +483,12 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,
// filter by fingerprints? Due to the probability of error with
// fingerprints, still use this slow-but-exact code, but after
// filtering.
const bool is_new_path_already_known =
std::any_of(variant_path_queue.container().cbegin(),
variant_path_queue.container().cend(),
[&new_path](const internal::PathWithPriority& element) {
return element.path() == new_path;
});
const bool is_new_path_already_known = std::any_of(
variant_path_queue.container().cbegin(),
variant_path_queue.container().cend(),
[&new_path](const internal::PathWithPriority<GraphType>& element) {
return element.path() == new_path;
});
if (is_new_path_already_known) continue;

const PathDistance path_length =
Expand All @@ -498,7 +510,7 @@ KShortestPaths YenKShortestPaths(const GraphType& graph,
// this iteration found no shorter one.
if (variant_path_queue.empty()) break;

const internal::PathWithPriority& next_shortest_path =
const internal::PathWithPriority<GraphType>& next_shortest_path =
variant_path_queue.top();
VLOG(5) << "> New path generated: "
<< absl::StrJoin(next_shortest_path.path(), " - ") << " ("
Expand Down
25 changes: 15 additions & 10 deletions ortools/graph/k_shortest_paths_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ TEST(KShortestPathsYenTest, ReducesToShortestPath) {
(void)graph.Build();
std::vector<PathDistance> lengths{1, 1};

const KShortestPaths paths = YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/1);
const KShortestPaths<StaticGraph<>> paths =
YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/1);
EXPECT_THAT(paths.paths, ElementsAre(std::vector<int>{0, 1, 2}));
EXPECT_THAT(paths.distances, ElementsAre(2));
}
Expand All @@ -141,8 +142,9 @@ TEST(KShortestPathsYenTest, OnlyHasOnePath) {
(void)graph.Build();
std::vector<PathDistance> lengths{1, 1};

const KShortestPaths paths = YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/10);
const KShortestPaths<StaticGraph<>> paths =
YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/10);
EXPECT_THAT(paths.paths, ElementsAre(std::vector<int>{0, 1, 2}));
EXPECT_THAT(paths.distances, ElementsAre(2));
}
Expand All @@ -155,8 +157,9 @@ TEST(KShortestPathsYenTest, HasTwoPaths) {
(void)graph.Build();
std::vector<PathDistance> lengths{1, 30, 1};

const KShortestPaths paths = YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/10);
const KShortestPaths<StaticGraph<>> paths =
YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/10);
EXPECT_THAT(paths.paths,
ElementsAre(std::vector<int>{0, 1, 2}, std::vector<int>{0, 2}));
EXPECT_THAT(paths.distances, ElementsAre(2, 30));
Expand All @@ -172,8 +175,9 @@ TEST(KShortestPathsYenTest, HasTwoPathsWithLongerPath) {
(void)graph.Build();
std::vector<PathDistance> lengths{1, 30, 1, 1, 1};

const KShortestPaths paths = YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/4, /*k=*/10);
const KShortestPaths<StaticGraph<>> paths =
YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/4, /*k=*/10);
EXPECT_THAT(paths.paths, ElementsAre(std::vector<int>{0, 1, 2, 3, 4},
std::vector<int>{0, 4}));
EXPECT_THAT(paths.distances, ElementsAre(4, 30));
Expand All @@ -190,8 +194,9 @@ TEST(KShortestPathsYenTest, ReturnsTheRightNumberOfPaths) {
(void)graph.Build();
std::vector<PathDistance> lengths{1, 1, 1, 1, 1};

const KShortestPaths paths = YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/2);
const KShortestPaths<StaticGraph<>> paths =
YenKShortestPaths(graph, lengths, /*source=*/0,
/*destination=*/2, /*k=*/2);
EXPECT_THAT(paths.paths,
ElementsAre(std::vector<int>{0, 2}, std::vector<int>{0, 1, 2}));
EXPECT_THAT(paths.distances, ElementsAre(1, 2));
Expand Down
1 change: 0 additions & 1 deletion ortools/graph/max_flow_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
#include "gtest/gtest.h"
#include "ortools/base/gmock.h"
#include "ortools/base/path.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/flow_problem.pb.h"
#include "ortools/util/file_util.h"

Expand Down
5 changes: 2 additions & 3 deletions ortools/graph/minimum_vertex_cover.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <vector>

#include "absl/log/check.h"
#include "ortools/graph/ebert_graph.h"
#include "ortools/graph/max_flow.h"

namespace operations_research {
Expand All @@ -31,7 +30,7 @@ std::vector<bool> BipartiteMinimumVertexCover(
// alternating matched/unmatched edges to find a minimum vertex cover.
SimpleMaxFlow max_flow;
const int num_left = left_to_right_arcs.size();
std::vector<ArcIndex> arcs;
std::vector<SimpleMaxFlow::ArcIndex> arcs;
for (int i = 0; i < num_left; ++i) {
for (const int right_node : left_to_right_arcs[i]) {
DCHECK_GE(right_node, num_left);
Expand All @@ -56,7 +55,7 @@ std::vector<bool> BipartiteMinimumVertexCover(
}
CHECK(max_flow.Solve(source, sink) == SimpleMaxFlow::OPTIMAL);
std::vector<int> maximum_matching(num_left + num_right, -1);
for (const ArcIndex arc : arcs) {
for (const SimpleMaxFlow::ArcIndex arc : arcs) {
if (max_flow.Flow(arc) > 0) {
maximum_matching[max_flow.Tail(arc)] = max_flow.Head(arc);
maximum_matching[max_flow.Head(arc)] = max_flow.Tail(arc);
Expand Down
2 changes: 1 addition & 1 deletion ortools/graph/samples/assignment_min_flow.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ void AssignmentMinFlow() {
// [END solve]

// [START print_solution]
if (status == MinCostFlow::OPTIMAL) {
if (status == SimpleMinCostFlow::OPTIMAL) {
LOG(INFO) << "Total cost: " << min_cost_flow.OptimalCost();
LOG(INFO) << "";
for (std::size_t i = 0; i < min_cost_flow.NumArcs(); ++i) {
Expand Down
Loading

0 comments on commit a5adf42

Please sign in to comment.