more work on dags
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
@@ -214,6 +215,9 @@ class ConstrainedShortestPathsOnDagWrapper {
|
||||
absl::Span<const NodeIndex> destinations_;
|
||||
const int num_resources_;
|
||||
|
||||
// Set to a node if and only if this node is in both `sources_` and
|
||||
// `destinations_`.
|
||||
std::optional<NodeIndex> source_is_destination_ = std::nullopt;
|
||||
// Data about *reachable* sub-graphs split in two for bidirectional search.
|
||||
// Reachable nodes are nodes that can be reached given the resources
|
||||
// constraints, i.e., for each resource, the sum of the minimum resource to
|
||||
@@ -334,13 +338,15 @@ ConstrainedShortestPathsOnDagWrapper<GraphType>::
|
||||
<< absl::StrFormat(
|
||||
"max_resource cannot be negative not +inf nor NaN");
|
||||
}
|
||||
std::vector<bool> is_source(graph->num_nodes(), false);
|
||||
for (const NodeIndex source : sources) {
|
||||
is_source[source] = true;
|
||||
}
|
||||
for (const NodeIndex destination : destinations) {
|
||||
CHECK(!is_source[destination])
|
||||
<< "A node cannot be both a source and destination";
|
||||
}
|
||||
std::vector<bool> is_source(graph->num_nodes(), false);
|
||||
for (const NodeIndex source : sources) {
|
||||
is_source[source] = true;
|
||||
}
|
||||
for (const NodeIndex destination : destinations) {
|
||||
if (is_source[destination]) {
|
||||
source_is_destination_ = destination;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +548,10 @@ template <class GraphType>
|
||||
#endif
|
||||
GraphPathWithLength<GraphType> ConstrainedShortestPathsOnDagWrapper<
|
||||
GraphType>::RunConstrainedShortestPathOnDag() {
|
||||
if (source_is_destination_.has_value()) {
|
||||
return {
|
||||
.length = 0, .arc_path = {}, .node_path = {*source_is_destination_}};
|
||||
}
|
||||
// Assign lengths on sub-relevant graphs.
|
||||
std::vector<double> sub_arc_lengths[2];
|
||||
for (const Direction dir : {FORWARD, BACKWARD}) {
|
||||
|
||||
@@ -90,6 +90,19 @@ TEST(ConstrainedShortestPathOnDagTest, SimpleGraph) {
|
||||
/*node_path=*/ElementsAre(source, b, destination)));
|
||||
}
|
||||
|
||||
TEST(ConstrainedShortestPathOnDagTest, GraphWithNoArcs) {
|
||||
EXPECT_THAT(ConstrainedShortestPathsOnDag(
|
||||
/*num_nodes=*/1, /*arcs_with_length_and_resources=*/{},
|
||||
/*source=*/0, /*destination=*/0, /*max_resources=*/{7.0}),
|
||||
FieldsAre(/*length=*/0, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/ElementsAre(0)));
|
||||
EXPECT_THAT(ConstrainedShortestPathsOnDag(
|
||||
/*num_nodes=*/2, /*arcs_with_length_and_resources=*/{},
|
||||
/*source=*/0, /*destination=*/1, /*max_resources=*/{7.0}),
|
||||
FieldsAre(/*length=*/kInf, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/IsEmpty()));
|
||||
}
|
||||
|
||||
TEST(ConstrainedShortestPathOnDagTest, SimpleGraphTwoPaths) {
|
||||
const int source = 0;
|
||||
const int destination = 1;
|
||||
@@ -818,17 +831,6 @@ TEST(ConstrainedShortestPathOnDagTest, NegativeMaxResource) {
|
||||
"negative");
|
||||
}
|
||||
|
||||
TEST(ConstrainedShortestPathOnDagTest, SourceIsDestination) {
|
||||
const int source = 0;
|
||||
const int num_nodes = 1;
|
||||
|
||||
EXPECT_DEATH(
|
||||
ConstrainedShortestPathsOnDag(
|
||||
num_nodes, /*arcs_with_length_and_resources=*/{}, source, source,
|
||||
/*max_resources=*/{0.0}),
|
||||
"source and destination");
|
||||
}
|
||||
|
||||
TEST(ConstrainedShortestPathsOnDagWrapperTest, ValidateTopologicalOrder) {
|
||||
const int source = 0;
|
||||
const int destination = 1;
|
||||
|
||||
@@ -366,7 +366,6 @@ ShortestPathsOnDagWrapper<GraphType>::ShortestPathsOnDagWrapper(
|
||||
CHECK(graph_ != nullptr);
|
||||
CHECK(arc_lengths_ != nullptr);
|
||||
CHECK_GT(graph_->num_nodes(), 0) << "The graph is empty: it has no nodes";
|
||||
CHECK_GT(graph_->num_arcs(), 0) << "The graph is empty: it has no arcs";
|
||||
#ifndef NDEBUG
|
||||
CHECK_EQ(arc_lengths_->size(), graph_->num_arcs());
|
||||
for (const double arc_length : *arc_lengths_) {
|
||||
@@ -489,7 +488,6 @@ KShortestPathsOnDagWrapper<GraphType>::KShortestPathsOnDagWrapper(
|
||||
CHECK(graph_ != nullptr);
|
||||
CHECK(arc_lengths_ != nullptr);
|
||||
CHECK_GT(graph_->num_nodes(), 0) << "The graph is empty: it has no nodes";
|
||||
CHECK_GT(graph_->num_arcs(), 0) << "The graph is empty: it has no arcs";
|
||||
CHECK_GT(path_count_, 0) << "path_count must be greater than 0";
|
||||
#ifndef NDEBUG
|
||||
CHECK_EQ(arc_lengths_->size(), graph_->num_arcs());
|
||||
|
||||
@@ -78,12 +78,6 @@ TEST(ShortestPathOnDagTest, EmptyGraph) {
|
||||
"num_nodes\\(\\) > 0");
|
||||
}
|
||||
|
||||
TEST(ShortestPathOnDagTest, NoArcGraph) {
|
||||
EXPECT_DEATH(ShortestPathsOnDag(/*num_nodes=*/1, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/0),
|
||||
"num_arcs\\(\\) > 0");
|
||||
}
|
||||
|
||||
TEST(ShortestPathOnDagTest, NonExistingSourceBecauseNegative) {
|
||||
EXPECT_DEATH(
|
||||
ShortestPathsOnDag(/*num_nodes=*/2, /*arcs_with_length=*/{{0, 1, 0.0}},
|
||||
@@ -137,6 +131,17 @@ TEST(ShortestPathOnDagTest, SimpleGraph) {
|
||||
/*node_path=*/ElementsAre(source, a, destination)));
|
||||
}
|
||||
|
||||
TEST(ShortestPathOnDagTest, GraphsWithNoArcs) {
|
||||
EXPECT_THAT(ShortestPathsOnDag(/*num_nodes=*/1, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/0),
|
||||
FieldsAre(/*length=*/0, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/ElementsAre(0)));
|
||||
EXPECT_THAT(ShortestPathsOnDag(/*num_nodes=*/2, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/1),
|
||||
FieldsAre(/*length=*/kInf, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/IsEmpty()));
|
||||
}
|
||||
|
||||
TEST(ShortestPathOnDagTest, SourceIsDestination) {
|
||||
const int source = 0;
|
||||
const int destination = 1;
|
||||
@@ -632,13 +637,6 @@ TEST(KShortestPathOnDagTest, EmptyGraph) {
|
||||
"num_nodes\\(\\) > 0");
|
||||
}
|
||||
|
||||
TEST(KShortestPathOnDagTest, NoArcGraph) {
|
||||
EXPECT_DEATH(
|
||||
KShortestPathsOnDag(/*num_nodes=*/1, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/0, /*path_count=*/2),
|
||||
"num_arcs\\(\\) > 0");
|
||||
}
|
||||
|
||||
TEST(KShortestPathOnDagTest, NonExistingSourceBecauseNegative) {
|
||||
EXPECT_DEATH(
|
||||
KShortestPathsOnDag(/*num_nodes=*/2, /*arcs_with_length=*/{{0, 1, 0.0}},
|
||||
@@ -689,6 +687,19 @@ TEST(KShortestPathOnDagTest, OnlyHasOnePath) {
|
||||
/*node_path=*/ElementsAre(source, a, destination))));
|
||||
}
|
||||
|
||||
TEST(KShortestPathOnDagTest, GraphsWithNoArcs) {
|
||||
EXPECT_THAT(
|
||||
KShortestPathsOnDag(/*num_nodes=*/1, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/0, /*path_count=*/2),
|
||||
ElementsAre(FieldsAre(/*length=*/0, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/ElementsAre(0))));
|
||||
EXPECT_THAT(
|
||||
KShortestPathsOnDag(/*num_nodes=*/2, /*arcs_with_length=*/{},
|
||||
/*source=*/0, /*destination=*/1, /*path_count=*/2),
|
||||
ElementsAre(FieldsAre(/*length=*/kInf, /*arc_path=*/IsEmpty(),
|
||||
/*node_path=*/IsEmpty())));
|
||||
}
|
||||
|
||||
TEST(KShortestPathOnDagTest, SourceIsDestination) {
|
||||
const int source = 0;
|
||||
const int destination = 1;
|
||||
|
||||
@@ -344,6 +344,12 @@ using ArcHeadIterator =
|
||||
ArcPropertyIterator<Graph, ArcIterator, typename Graph::NodeIndex,
|
||||
&Graph::Head>;
|
||||
|
||||
// An iterator that iterates on the opposite arcs of another iterator.
|
||||
template <typename Graph, typename ArcIterator>
|
||||
using ArcOppositeArcIterator =
|
||||
ArcPropertyIterator<Graph, ArcIterator, typename Graph::ArcIndex,
|
||||
&Graph::OppositeArc>;
|
||||
|
||||
// Basic graph implementation without reverse arc. This class also serves as a
|
||||
// documentation for the generic graph interface (minus the part related to
|
||||
// reverse arcs).
|
||||
@@ -595,8 +601,7 @@ class ReverseArcListGraph
|
||||
using OutgoingHeadIterator =
|
||||
ArcHeadIterator<ReverseArcListGraph, OutgoingArcIterator>;
|
||||
using IncomingArcIterator =
|
||||
ArcPropertyIterator<ReverseArcListGraph, OppositeIncomingArcIterator,
|
||||
ArcIndexType, &ReverseArcListGraph::OppositeArc>;
|
||||
ArcOppositeArcIterator<ReverseArcListGraph, OppositeIncomingArcIterator>;
|
||||
|
||||
// ReverseArcListGraph<>::OutDegree() and ::InDegree() work in O(degree).
|
||||
ArcIndexType OutDegree(NodeIndexType node) const;
|
||||
@@ -713,16 +718,23 @@ class ReverseArcStaticGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
class OutgoingOrOppositeIncomingArcIterator;
|
||||
using OppositeIncomingArcIterator = IntegerRangeIterator<ArcIndexType>;
|
||||
class IncomingArcIterator;
|
||||
using OutgoingArcIterator = IntegerRangeIterator<ArcIndexType>;
|
||||
ArcIndexType OppositeArc(ArcIndexType arc) const;
|
||||
// TODO(user): support Head() and Tail() before Build(), like StaticGraph<>.
|
||||
NodeIndexType Head(ArcIndexType arc) const;
|
||||
NodeIndexType Tail(ArcIndexType arc) const;
|
||||
|
||||
// ReverseArcStaticGraph<>::OutDegree() and ::InDegree() work in O(1).
|
||||
ArcIndexType OutDegree(NodeIndexType node) const;
|
||||
ArcIndexType InDegree(NodeIndexType node) const;
|
||||
|
||||
// Deprecated.
|
||||
class OutgoingOrOppositeIncomingArcIterator;
|
||||
using OppositeIncomingArcIterator = IntegerRangeIterator<ArcIndexType>;
|
||||
using IncomingArcIterator =
|
||||
ArcOppositeArcIterator<ReverseArcStaticGraph,
|
||||
OppositeIncomingArcIterator>;
|
||||
using OutgoingArcIterator = IntegerRangeIterator<ArcIndexType>;
|
||||
|
||||
IntegerRange<ArcIndexType> OutgoingArcs(NodeIndexType node) const {
|
||||
return IntegerRange<ArcIndexType>(start_[node], DirectArcLimit(node));
|
||||
}
|
||||
@@ -746,12 +758,24 @@ class ReverseArcStaticGraph
|
||||
limit);
|
||||
}
|
||||
|
||||
BeginEndWrapper<IncomingArcIterator> IncomingArcs(NodeIndexType node) const;
|
||||
BeginEndWrapper<IncomingArcIterator> IncomingArcs(NodeIndexType node) const {
|
||||
const auto opposite_incoming_arcs = OppositeIncomingArcs(node);
|
||||
return {IncomingArcIterator(*this, opposite_incoming_arcs.begin()),
|
||||
IncomingArcIterator(*this, opposite_incoming_arcs.end())};
|
||||
}
|
||||
|
||||
BeginEndWrapper<IncomingArcIterator> IncomingArcsStartingFrom(
|
||||
NodeIndexType node, ArcIndexType from) const {
|
||||
DCHECK(Base::IsNodeValid(node));
|
||||
const auto opposite_incoming_arcs = OppositeIncomingArcsStartingFrom(
|
||||
node, from == Base::kNilArc ? Base::kNilArc : OppositeArc(from));
|
||||
return {IncomingArcIterator(*this, opposite_incoming_arcs.begin()),
|
||||
IncomingArcIterator(*this, opposite_incoming_arcs.end())};
|
||||
}
|
||||
|
||||
BeginEndWrapper<OutgoingOrOppositeIncomingArcIterator>
|
||||
OutgoingOrOppositeIncomingArcs(NodeIndexType node) const;
|
||||
|
||||
BeginEndWrapper<IncomingArcIterator> IncomingArcsStartingFrom(
|
||||
NodeIndexType node, ArcIndexType from) const;
|
||||
BeginEndWrapper<OutgoingOrOppositeIncomingArcIterator>
|
||||
OutgoingOrOppositeIncomingArcsStartingFrom(NodeIndexType node,
|
||||
ArcIndexType from) const;
|
||||
@@ -761,11 +785,6 @@ class ReverseArcStaticGraph
|
||||
// graph algorithms.
|
||||
absl::Span<const NodeIndexType> operator[](NodeIndexType node) const;
|
||||
|
||||
ArcIndexType OppositeArc(ArcIndexType arc) const;
|
||||
// TODO(user): support Head() and Tail() before Build(), like StaticGraph<>.
|
||||
NodeIndexType Head(ArcIndexType arc) const;
|
||||
NodeIndexType Tail(ArcIndexType arc) const;
|
||||
|
||||
void ReserveArcs(ArcIndexType bound) override;
|
||||
void AddNode(NodeIndexType node);
|
||||
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head);
|
||||
@@ -1592,7 +1611,6 @@ class ReverseArcListGraph<NodeIndexType,
|
||||
|
||||
// ReverseArcStaticGraph implementation ----------------------------------------
|
||||
|
||||
DEFINE_RANGE_BASED_ARC_ITERATION(ReverseArcStaticGraph, Incoming);
|
||||
DEFINE_RANGE_BASED_ARC_ITERATION(ReverseArcStaticGraph,
|
||||
OutgoingOrOppositeIncoming);
|
||||
|
||||
@@ -1719,48 +1737,6 @@ void ReverseArcStaticGraph<NodeIndexType, ArcIndexType>::Build(
|
||||
}
|
||||
}
|
||||
|
||||
template <typename NodeIndexType, typename ArcIndexType>
|
||||
class ReverseArcStaticGraph<NodeIndexType, ArcIndexType>::IncomingArcIterator
|
||||
: public OppositeIncomingArcIterator {
|
||||
public:
|
||||
IncomingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node)
|
||||
: limit_(graph.ReverseArcLimit(node)),
|
||||
index_(graph.reverse_start_[node]),
|
||||
graph_(graph) {
|
||||
DCHECK(graph.IsNodeValid(node));
|
||||
DCHECK_LE(index_, limit_);
|
||||
}
|
||||
|
||||
IncomingArcIterator(const ReverseArcStaticGraph& graph, NodeIndexType node,
|
||||
ArcIndexType arc)
|
||||
: limit_(graph.ReverseArcLimit(node)), graph_(graph) {
|
||||
index_ = arc == Base::kNilArc ? limit_
|
||||
: (arc == graph.ReverseArcLimit(node)
|
||||
? graph.ReverseArcLimit(node)
|
||||
: graph.OppositeArc(arc));
|
||||
DCHECK(graph.IsNodeValid(node));
|
||||
DCHECK_GE(index_, graph.reverse_start_[node]);
|
||||
DCHECK_LE(index_, limit_);
|
||||
}
|
||||
|
||||
bool Ok() const { return index_ != limit_; }
|
||||
ArcIndexType Index() const {
|
||||
return this->index_ == this->limit_ ? this->limit_
|
||||
: graph_.OppositeArc(this->index_);
|
||||
}
|
||||
void Next() {
|
||||
DCHECK(Ok());
|
||||
index_++;
|
||||
}
|
||||
|
||||
DEFINE_STL_ITERATOR_FUNCTIONS(IncomingArcIterator);
|
||||
|
||||
private:
|
||||
const ArcIndexType limit_;
|
||||
ArcIndexType index_;
|
||||
const ReverseArcStaticGraph& graph_;
|
||||
};
|
||||
|
||||
template <typename NodeIndexType, typename ArcIndexType>
|
||||
class ReverseArcStaticGraph<
|
||||
NodeIndexType, ArcIndexType>::OutgoingOrOppositeIncomingArcIterator {
|
||||
|
||||
Reference in New Issue
Block a user