Use absl hash containers for graph algorithms

This reduces pre-processor time when finding an
ordering automatically substantially.

Before:

ComputeStableSchurOrdering
                                        Delta   Cumulative
              CreateHessianGraph :    0.50324      0.50324
                     Preordering :    0.00692      0.51017
            StableIndependentSet :    0.26341      0.77358
         ConstantParameterBlocks :    0.00095      0.77453
                           Total :    0.23978      1.01431

After:

ComputeStableSchurOrdering
                                        Delta   Cumulative
              CreateHessianGraph :    0.17183      0.17183
                     Preordering :    0.00226      0.17409
            StableIndependentSet :    0.12510      0.29919
         ConstantParameterBlocks :    0.00073      0.29991
                           Total :    0.01638      0.31629

Change-Id: I50bbac69f8b3f19240a61a218913cebf34da0db5
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index ed30c7d..4c501a5 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -47,6 +47,8 @@
 
 list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES absl::strings)
 list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES absl::time)
+list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES absl::flat_hash_map)
+list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES absl::flat_hash_set)
 
 list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::log)
 list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES absl::check)
diff --git a/internal/ceres/canonical_views_clustering.cc b/internal/ceres/canonical_views_clustering.cc
index 15459e1..a41e5f8 100644
--- a/internal/ceres/canonical_views_clustering.cc
+++ b/internal/ceres/canonical_views_clustering.cc
@@ -31,10 +31,10 @@
 
 #include "ceres/canonical_views_clustering.h"
 
-#include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "ceres/graph.h"
@@ -43,8 +43,8 @@
 
 namespace ceres::internal {
 
-using IntMap = std::unordered_map<int, int>;
-using IntSet = std::unordered_set<int>;
+using IntMap = absl::flat_hash_map<int, int>;
+using IntSet = absl::flat_hash_set<int>;
 
 class CERES_NO_EXPORT CanonicalViewsClustering {
  public:
@@ -75,7 +75,7 @@
   // center).
   IntMap view_to_canonical_view_;
   // Maps a view to its similarity to its current cluster center.
-  std::unordered_map<int, double> view_to_canonical_view_similarity_;
+  absl::flat_hash_map<int, double> view_to_canonical_view_similarity_;
 };
 
 void ComputeCanonicalViewsClustering(
diff --git a/internal/ceres/canonical_views_clustering.h b/internal/ceres/canonical_views_clustering.h
index eb05a91..c7c4d2f 100644
--- a/internal/ceres/canonical_views_clustering.h
+++ b/internal/ceres/canonical_views_clustering.h
@@ -41,9 +41,9 @@
 #ifndef CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_
 #define CERES_INTERNAL_CANONICAL_VIEWS_CLUSTERING_H_
 
-#include <unordered_map>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
 #include "ceres/graph.h"
 #include "ceres/internal/disable_warnings.h"
 #include "ceres/internal/export.h"
@@ -99,7 +99,7 @@
     const CanonicalViewsClusteringOptions& options,
     const WeightedGraph<int>& graph,
     std::vector<int>* centers,
-    std::unordered_map<int, int>* membership);
+    absl::flat_hash_map<int, int>* membership);
 
 struct CERES_NO_EXPORT CanonicalViewsClusteringOptions {
   // The minimum number of canonical views to compute.
diff --git a/internal/ceres/canonical_views_clustering_test.cc b/internal/ceres/canonical_views_clustering_test.cc
index fa79582..634eed7 100644
--- a/internal/ceres/canonical_views_clustering_test.cc
+++ b/internal/ceres/canonical_views_clustering_test.cc
@@ -31,8 +31,7 @@
 
 #include "ceres/canonical_views_clustering.h"
 
-#include <unordered_map>
-
+#include "absl/container/flat_hash_map.h"
 #include "ceres/graph.h"
 #include "gtest/gtest.h"
 
@@ -74,7 +73,7 @@
 
   CanonicalViewsClusteringOptions options_;
   std::vector<int> centers_;
-  std::unordered_map<int, int> membership_;
+  absl::flat_hash_map<int, int> membership_;
 };
 
 TEST_F(CanonicalViewsTest, ComputeCanonicalViewsTest) {
diff --git a/internal/ceres/covariance_impl.cc b/internal/ceres/covariance_impl.cc
index bdbb590..8da6d89 100644
--- a/internal/ceres/covariance_impl.cc
+++ b/internal/ceres/covariance_impl.cc
@@ -35,13 +35,13 @@
 #include <memory>
 #include <numeric>
 #include <sstream>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
 #include "Eigen/SVD"
 #include "Eigen/SparseCore"
 #include "Eigen/SparseQR"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "ceres/compressed_col_sparse_matrix_utils.h"
@@ -367,7 +367,7 @@
   std::vector<double*> all_parameter_blocks;
   problem->GetParameterBlocks(&all_parameter_blocks);
   const ProblemImpl::ParameterMap& parameter_map = problem->parameter_map();
-  std::unordered_set<ParameterBlock*> parameter_blocks_in_use;
+  absl::flat_hash_set<ParameterBlock*> parameter_blocks_in_use;
   std::vector<ResidualBlock*> residual_blocks;
   problem->GetResidualBlocks(&residual_blocks);
 
diff --git a/internal/ceres/graph.h b/internal/ceres/graph.h
index 9b28b06..f683d37 100644
--- a/internal/ceres/graph.h
+++ b/internal/ceres/graph.h
@@ -32,14 +32,13 @@
 #define CERES_INTERNAL_GRAPH_H_
 
 #include <limits>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "ceres/internal/export.h"
 #include "ceres/map_util.h"
-#include "ceres/pair_hash.h"
 #include "ceres/types.h"
 
 namespace ceres::internal {
@@ -52,7 +51,7 @@
   // Add a vertex.
   void AddVertex(const Vertex& vertex) {
     if (vertices_.insert(vertex).second) {
-      edges_[vertex] = std::unordered_set<Vertex>();
+      edges_[vertex] = absl::flat_hash_set<Vertex>();
     }
   }
 
@@ -62,7 +61,7 @@
     }
 
     vertices_.erase(vertex);
-    const std::unordered_set<Vertex>& sinks = edges_[vertex];
+    const absl::flat_hash_set<Vertex>& sinks = edges_[vertex];
     for (const Vertex& s : sinks) {
       edges_[s].erase(vertex);
     }
@@ -88,15 +87,15 @@
 
   // Calling Neighbors on a vertex not in the graph will result in
   // undefined behaviour.
-  const std::unordered_set<Vertex>& Neighbors(const Vertex& vertex) const {
+  const absl::flat_hash_set<Vertex>& Neighbors(const Vertex& vertex) const {
     return FindOrDie(edges_, vertex);
   }
 
-  const std::unordered_set<Vertex>& vertices() const { return vertices_; }
+  const absl::flat_hash_set<Vertex>& vertices() const { return vertices_; }
 
  private:
-  std::unordered_set<Vertex> vertices_;
-  std::unordered_map<Vertex, std::unordered_set<Vertex>> edges_;
+  absl::flat_hash_set<Vertex> vertices_;
+  absl::flat_hash_map<Vertex, absl::flat_hash_set<Vertex>> edges_;
 };
 
 // A weighted undirected graph templated over the vertex ids. Vertex
@@ -109,7 +108,7 @@
   void AddVertex(const Vertex& vertex, double weight) {
     if (vertices_.find(vertex) == vertices_.end()) {
       vertices_.insert(vertex);
-      edges_[vertex] = std::unordered_set<Vertex>();
+      edges_[vertex] = absl::flat_hash_set<Vertex>();
     }
     vertex_weights_[vertex] = weight;
   }
@@ -125,7 +124,7 @@
 
     vertices_.erase(vertex);
     vertex_weights_.erase(vertex);
-    const std::unordered_set<Vertex>& sinks = edges_[vertex];
+    const absl::flat_hash_set<Vertex>& sinks = edges_[vertex];
     for (const Vertex& s : sinks) {
       if (vertex < s) {
         edge_weights_.erase(std::make_pair(vertex, s));
@@ -187,22 +186,21 @@
 
   // Calling Neighbors on a vertex not in the graph will result in
   // undefined behaviour.
-  const std::unordered_set<Vertex>& Neighbors(const Vertex& vertex) const {
+  const absl::flat_hash_set<Vertex>& Neighbors(const Vertex& vertex) const {
     return FindOrDie(edges_, vertex);
   }
 
-  const std::unordered_set<Vertex>& vertices() const { return vertices_; }
+  const absl::flat_hash_set<Vertex>& vertices() const { return vertices_; }
 
   static double InvalidWeight() {
     return std::numeric_limits<double>::quiet_NaN();
   }
 
  private:
-  std::unordered_set<Vertex> vertices_;
-  std::unordered_map<Vertex, double> vertex_weights_;
-  std::unordered_map<Vertex, std::unordered_set<Vertex>> edges_;
-  std::unordered_map<std::pair<Vertex, Vertex>, double, pair_hash>
-      edge_weights_;
+  absl::flat_hash_set<Vertex> vertices_;
+  absl::flat_hash_map<Vertex, double> vertex_weights_;
+  absl::flat_hash_map<Vertex, absl::flat_hash_set<Vertex>> edges_;
+  absl::flat_hash_map<std::pair<Vertex, Vertex>, double> edge_weights_;
 };
 
 }  // namespace ceres::internal
diff --git a/internal/ceres/graph_algorithms.h b/internal/ceres/graph_algorithms.h
index 60520d5..fd395f8 100644
--- a/internal/ceres/graph_algorithms.h
+++ b/internal/ceres/graph_algorithms.h
@@ -35,11 +35,11 @@
 
 #include <algorithm>
 #include <memory>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "ceres/graph.h"
 #include "ceres/internal/export.h"
@@ -96,7 +96,7 @@
 template <typename Vertex>
 int IndependentSetOrdering(const Graph<Vertex>& graph,
                            std::vector<Vertex>* ordering) {
-  const std::unordered_set<Vertex>& vertices = graph.vertices();
+  const absl::flat_hash_set<Vertex>& vertices = graph.vertices();
   const int num_vertices = vertices.size();
 
   CHECK(ordering != nullptr);
@@ -109,7 +109,7 @@
   const char kBlack = 2;
 
   // Mark all vertices white.
-  std::unordered_map<Vertex, char> vertex_color;
+  absl::flat_hash_map<Vertex, char> vertex_color;
   std::vector<Vertex> vertex_queue;
   for (const Vertex& vertex : vertices) {
     vertex_color[vertex] = kWhite;
@@ -129,7 +129,7 @@
 
     ordering->push_back(vertex);
     vertex_color[vertex] = kBlack;
-    const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
+    const absl::flat_hash_set<Vertex>& neighbors = graph.Neighbors(vertex);
     for (const Vertex& neighbor : neighbors) {
       vertex_color[neighbor] = kGrey;
     }
@@ -165,7 +165,7 @@
 int StableIndependentSetOrdering(const Graph<Vertex>& graph,
                                  std::vector<Vertex>* ordering) {
   CHECK(ordering != nullptr);
-  const std::unordered_set<Vertex>& vertices = graph.vertices();
+  const absl::flat_hash_set<Vertex>& vertices = graph.vertices();
   const int num_vertices = vertices.size();
   CHECK_EQ(vertices.size(), ordering->size());
 
@@ -181,7 +181,7 @@
                    VertexDegreeLessThan<Vertex>(graph));
 
   // Mark all vertices white.
-  std::unordered_map<Vertex, char> vertex_color;
+  absl::flat_hash_map<Vertex, char> vertex_color;
   for (const Vertex& vertex : vertices) {
     vertex_color[vertex] = kWhite;
   }
@@ -198,7 +198,7 @@
 
     ordering->push_back(vertex);
     vertex_color[vertex] = kBlack;
-    const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
+    const absl::flat_hash_set<Vertex>& neighbors = graph.Neighbors(vertex);
     for (const Vertex& neighbor : neighbors) {
       vertex_color[neighbor] = kGrey;
     }
@@ -228,7 +228,7 @@
 // is what gives this data structure its efficiency.
 template <typename Vertex>
 Vertex FindConnectedComponent(const Vertex& vertex,
-                              std::unordered_map<Vertex, Vertex>* union_find) {
+                              absl::flat_hash_map<Vertex, Vertex>* union_find) {
   auto it = union_find->find(vertex);
   DCHECK(it != union_find->end());
   if (it->second != vertex) {
@@ -265,18 +265,18 @@
 
   // Disjoint-set to keep track of the connected components in the
   // maximum spanning tree.
-  std::unordered_map<Vertex, Vertex> disjoint_set;
+  absl::flat_hash_map<Vertex, Vertex> disjoint_set;
 
   // Sort of the edges in the graph in decreasing order of their
   // weight. Also add the vertices of the graph to the Maximum
   // Spanning Tree graph and set each vertex to be its own connected
   // component in the disjoint_set structure.
-  const std::unordered_set<Vertex>& vertices = graph.vertices();
+  const absl::flat_hash_set<Vertex>& vertices = graph.vertices();
   for (const Vertex& vertex1 : vertices) {
     forest->AddVertex(vertex1, graph.VertexWeight(vertex1));
     disjoint_set[vertex1] = vertex1;
 
-    const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex1);
+    const absl::flat_hash_set<Vertex>& neighbors = graph.Neighbors(vertex1);
     for (const Vertex& vertex2 : neighbors) {
       if (vertex1 >= vertex2) {
         continue;
diff --git a/internal/ceres/graph_algorithms_test.cc b/internal/ceres/graph_algorithms_test.cc
index 6c86668..aa1d759 100644
--- a/internal/ceres/graph_algorithms_test.cc
+++ b/internal/ceres/graph_algorithms_test.cc
@@ -32,9 +32,9 @@
 
 #include <algorithm>
 #include <memory>
-#include <unordered_set>
 #include <vector>
 
+#include "absl/container/flat_hash_set.h"
 #include "ceres/graph.h"
 #include "ceres/internal/export.h"
 #include "gtest/gtest.h"
@@ -112,7 +112,7 @@
   std::unique_ptr<WeightedGraph<int>> forest(
       Degree2MaximumSpanningForest(graph));
 
-  const std::unordered_set<int>& vertices = forest->vertices();
+  const absl::flat_hash_set<int>& vertices = forest->vertices();
   EXPECT_EQ(vertices.size(), 2);
   EXPECT_EQ(forest->VertexWeight(0), 1.0);
   EXPECT_EQ(forest->VertexWeight(1), 2.0);
@@ -135,35 +135,35 @@
 
   std::unique_ptr<WeightedGraph<int>> forest(
       Degree2MaximumSpanningForest(graph));
-  const std::unordered_set<int>& vertices = forest->vertices();
+  const absl::flat_hash_set<int>& vertices = forest->vertices();
   EXPECT_EQ(vertices.size(), 5);
 
   {
-    const std::unordered_set<int>& neighbors = forest->Neighbors(0);
+    const absl::flat_hash_set<int>& neighbors = forest->Neighbors(0);
     EXPECT_EQ(neighbors.size(), 2);
     EXPECT_TRUE(neighbors.find(4) != neighbors.end());
     EXPECT_TRUE(neighbors.find(3) != neighbors.end());
   }
 
   {
-    const std::unordered_set<int>& neighbors = forest->Neighbors(3);
+    const absl::flat_hash_set<int>& neighbors = forest->Neighbors(3);
     EXPECT_EQ(neighbors.size(), 1);
     EXPECT_TRUE(neighbors.find(0) != neighbors.end());
   }
 
   {
-    const std::unordered_set<int>& neighbors = forest->Neighbors(4);
+    const absl::flat_hash_set<int>& neighbors = forest->Neighbors(4);
     EXPECT_EQ(neighbors.size(), 1);
     EXPECT_TRUE(neighbors.find(0) != neighbors.end());
   }
 
   {
-    const std::unordered_set<int>& neighbors = forest->Neighbors(1);
+    const absl::flat_hash_set<int>& neighbors = forest->Neighbors(1);
     EXPECT_EQ(neighbors.size(), 0);
   }
 
   {
-    const std::unordered_set<int>& neighbors = forest->Neighbors(2);
+    const absl::flat_hash_set<int>& neighbors = forest->Neighbors(2);
     EXPECT_EQ(neighbors.size(), 0);
   }
 }
diff --git a/internal/ceres/graph_test.cc b/internal/ceres/graph_test.cc
index 8c8afc6..4801646 100644
--- a/internal/ceres/graph_test.cc
+++ b/internal/ceres/graph_test.cc
@@ -30,8 +30,7 @@
 
 #include "ceres/graph.h"
 
-#include <unordered_set>
-
+#include "absl/container/flat_hash_set.h"
 #include "gtest/gtest.h"
 
 namespace ceres::internal {
@@ -47,7 +46,7 @@
   graph.AddVertex(1);
   graph.AddEdge(0, 1);
 
-  const std::unordered_set<int>& vertices = graph.vertices();
+  const absl::flat_hash_set<int>& vertices = graph.vertices();
   EXPECT_EQ(vertices.size(), 2);
   EXPECT_EQ(graph.Neighbors(0).size(), 1);
   EXPECT_EQ(graph.Neighbors(1).size(), 1);
@@ -59,7 +58,7 @@
   graph.AddVertex(1);
   graph.AddEdge(0, 1);
 
-  const std::unordered_set<int>& vertices = graph.vertices();
+  const absl::flat_hash_set<int>& vertices = graph.vertices();
 
   EXPECT_EQ(vertices.size(), 2);
 
@@ -92,7 +91,7 @@
   graph.AddVertex(1, 2.0);
   graph.AddEdge(0, 1, 0.5);
 
-  const std::unordered_set<int>& vertices = graph.vertices();
+  const absl::flat_hash_set<int>& vertices = graph.vertices();
   EXPECT_EQ(vertices.size(), 2);
   EXPECT_EQ(graph.VertexWeight(0), 1.0);
   EXPECT_EQ(graph.VertexWeight(1), 2.0);
@@ -108,7 +107,7 @@
   graph.AddVertex(1, 2.0);
   graph.AddEdge(0, 1, 0.5);
 
-  const std::unordered_set<int>& vertices = graph.vertices();
+  const absl::flat_hash_set<int>& vertices = graph.vertices();
 
   EXPECT_EQ(vertices.size(), 2);
 
diff --git a/internal/ceres/pair_hash.h b/internal/ceres/pair_hash.h
deleted file mode 100644
index 64882cd..0000000
--- a/internal/ceres/pair_hash.h
+++ /dev/null
@@ -1,116 +0,0 @@
-// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2023 Google Inc. All rights reserved.
-// http://ceres-solver.org/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// * Redistributions of source code must retain the above copyright notice,
-//   this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright notice,
-//   this list of conditions and the following disclaimer in the documentation
-//   and/or other materials provided with the distribution.
-// * Neither the name of Google Inc. nor the names of its contributors may be
-//   used to endorse or promote products derived from this software without
-//   specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: keir@google.com (Keir Mierle)
-//
-// A hasher for std::pair<T, T>.
-
-#ifndef CERES_INTERNAL_PAIR_HASH_H_
-#define CERES_INTERNAL_PAIR_HASH_H_
-
-#include <cstddef>
-#include <cstdint>
-#include <functional>
-#include <utility>
-
-#include "ceres/internal/export.h"
-
-namespace ceres::internal {
-
-#if defined(_WIN32) && !defined(__MINGW64__) && !defined(__MINGW32__)
-#define GG_LONGLONG(x) x##I64
-#define GG_ULONGLONG(x) x##UI64
-#else
-#define GG_LONGLONG(x) x##LL
-#define GG_ULONGLONG(x) x##ULL
-#endif
-
-// The hash function is due to Bob Jenkins (see
-// http://burtleburtle.net/bob/hash/index.html). Each mix takes 36 instructions,
-// in 18 cycles if you're lucky. On x86 architectures, this requires 45
-// instructions in 27 cycles, if you're lucky.
-//
-// clang-format off
-//
-// 32bit version
-inline void hash_mix(uint32_t& a, uint32_t& b, uint32_t& c) {
-  a -= b; a -= c; a ^= (c>>13);
-  b -= c; b -= a; b ^= (a<<8);
-  c -= a; c -= b; c ^= (b>>13);
-  a -= b; a -= c; a ^= (c>>12);
-  b -= c; b -= a; b ^= (a<<16);
-  c -= a; c -= b; c ^= (b>>5);
-  a -= b; a -= c; a ^= (c>>3);
-  b -= c; b -= a; b ^= (a<<10);
-  c -= a; c -= b; c ^= (b>>15);
-}
-
-// 64bit version
-inline void hash_mix(uint64_t& a, uint64_t& b, uint64_t& c) {
-  a -= b; a -= c; a ^= (c>>43);
-  b -= c; b -= a; b ^= (a<<9);
-  c -= a; c -= b; c ^= (b>>8);
-  a -= b; a -= c; a ^= (c>>38);
-  b -= c; b -= a; b ^= (a<<23);
-  c -= a; c -= b; c ^= (b>>5);
-  a -= b; a -= c; a ^= (c>>35);
-  b -= c; b -= a; b ^= (a<<49);
-  c -= a; c -= b; c ^= (b>>11);
-}
-// clang-format on
-
-inline uint32_t Hash32NumWithSeed(uint32_t num, uint32_t c) {
-  // The golden ratio; an arbitrary value.
-  uint32_t b = 0x9e3779b9UL;
-  hash_mix(num, b, c);
-  return c;
-}
-
-inline uint64_t Hash64NumWithSeed(uint64_t num, uint64_t c) {
-  // More of the golden ratio.
-  uint64_t b = GG_ULONGLONG(0xe08c1d668b756f82);
-  hash_mix(num, b, c);
-  return c;
-}
-
-// Hasher for STL pairs. Requires hashers for both members to be defined.
-struct pair_hash {
- public:
-  template <typename T>
-  std::size_t operator()(const std::pair<T, T>& p) const {
-    const std::size_t h1 = std::hash<T>()(p.first);
-    const std::size_t h2 = std::hash<T>()(p.second);
-    // The decision below is at compile time
-    return (sizeof(h1) <= sizeof(uint32_t)) ? Hash32NumWithSeed(h1, h2)
-                                            : Hash64NumWithSeed(h1, h2);
-  }
-};
-
-}  // namespace ceres::internal
-
-#endif  // CERES_INTERNAL_PAIR_HASH_H_
diff --git a/internal/ceres/parameter_block.h b/internal/ceres/parameter_block.h
index a76192c..96dd9de 100644
--- a/internal/ceres/parameter_block.h
+++ b/internal/ceres/parameter_block.h
@@ -37,8 +37,8 @@
 #include <limits>
 #include <memory>
 #include <string>
-#include <unordered_set>
 
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "absl/strings/str_format.h"
@@ -64,7 +64,7 @@
 // proper disposal of the manifold.
 class CERES_NO_EXPORT ParameterBlock {
  public:
-  using ResidualBlockSet = std::unordered_set<ResidualBlock*>;
+  using ResidualBlockSet = absl::flat_hash_set<ResidualBlock*>;
 
   // Create a parameter block with the user state, size, and index specified.
   // The size is the size of the parameter block and the index is the position
diff --git a/internal/ceres/parameter_block_ordering.cc b/internal/ceres/parameter_block_ordering.cc
index 68dfb05..6db8a60 100644
--- a/internal/ceres/parameter_block_ordering.cc
+++ b/internal/ceres/parameter_block_ordering.cc
@@ -33,7 +33,6 @@
 #include <map>
 #include <memory>
 #include <set>
-#include <unordered_set>
 #include <vector>
 
 #include "absl/log/check.h"
@@ -57,7 +56,7 @@
 
   const std::vector<ParameterBlock*>& parameter_blocks =
       program.parameter_blocks();
-  const std::unordered_set<ParameterBlock*>& vertices = graph->vertices();
+  const auto& vertices = graph->vertices();
   for (auto* parameter_block : parameter_blocks) {
     if (vertices.count(parameter_block) > 0) {
       ordering->push_back(parameter_block);
diff --git a/internal/ceres/parameter_block_ordering_test.cc b/internal/ceres/parameter_block_ordering_test.cc
index 459a055..feb5f6d 100644
--- a/internal/ceres/parameter_block_ordering_test.cc
+++ b/internal/ceres/parameter_block_ordering_test.cc
@@ -32,9 +32,9 @@
 
 #include <cstddef>
 #include <memory>
-#include <unordered_set>
 #include <vector>
 
+#include "absl/container/flat_hash_set.h"
 #include "ceres/cost_function.h"
 #include "ceres/graph.h"
 #include "ceres/problem_impl.h"
@@ -45,7 +45,7 @@
 
 namespace ceres::internal {
 
-using VertexSet = std::unordered_set<ParameterBlock*>;
+using VertexSet = absl::flat_hash_set<ParameterBlock*>;
 
 template <int M, int... Ns>
 class DummyCostFunction : public SizedCostFunction<M, Ns...> {
diff --git a/internal/ceres/problem_impl.h b/internal/ceres/problem_impl.h
index 73a4a05..f5bf4d0 100644
--- a/internal/ceres/problem_impl.h
+++ b/internal/ceres/problem_impl.h
@@ -42,10 +42,9 @@
 #include <array>
 #include <map>
 #include <memory>
-#include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "ceres/context_impl.h"
 #include "ceres/internal/disable_warnings.h"
@@ -70,7 +69,7 @@
 class CERES_NO_EXPORT ProblemImpl {
  public:
   using ParameterMap = std::map<double*, ParameterBlock*>;
-  using ResidualBlockSet = std::unordered_set<ResidualBlock*>;
+  using ResidualBlockSet = absl::flat_hash_set<ResidualBlock*>;
   using CostFunctionRefCount = std::map<CostFunction*, int>;
   using LossFunctionRefCount = std::map<LossFunction*, int>;
 
diff --git a/internal/ceres/single_linkage_clustering.cc b/internal/ceres/single_linkage_clustering.cc
index 8ebbbb9..5545405 100644
--- a/internal/ceres/single_linkage_clustering.cc
+++ b/internal/ceres/single_linkage_clustering.cc
@@ -30,9 +30,8 @@
 
 #include "ceres/single_linkage_clustering.h"
 
-#include <unordered_map>
-#include <unordered_set>
-
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "ceres/graph.h"
 #include "ceres/graph_algorithms.h"
@@ -42,18 +41,18 @@
 int ComputeSingleLinkageClustering(
     const SingleLinkageClusteringOptions& options,
     const WeightedGraph<int>& graph,
-    std::unordered_map<int, int>* membership) {
+    absl::flat_hash_map<int, int>* membership) {
   CHECK(membership != nullptr);
   membership->clear();
 
   // Initially each vertex is in its own cluster.
-  const std::unordered_set<int>& vertices = graph.vertices();
+  const absl::flat_hash_set<int>& vertices = graph.vertices();
   for (const int v : vertices) {
     (*membership)[v] = v;
   }
 
   for (const int vertex1 : vertices) {
-    const std::unordered_set<int>& neighbors = graph.Neighbors(vertex1);
+    const absl::flat_hash_set<int>& neighbors = graph.Neighbors(vertex1);
     for (const int vertex2 : neighbors) {
       // Since the graph is undirected, only pay attention to one side
       // of the edge and ignore weak edges.
diff --git a/internal/ceres/single_linkage_clustering.h b/internal/ceres/single_linkage_clustering.h
index 3f49540..46a5fb8 100644
--- a/internal/ceres/single_linkage_clustering.h
+++ b/internal/ceres/single_linkage_clustering.h
@@ -31,8 +31,7 @@
 #ifndef CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_
 #define CERES_INTERNAL_SINGLE_LINKAGE_CLUSTERING_H_
 
-#include <unordered_map>
-
+#include "absl/container/flat_hash_map.h"
 #include "ceres/graph.h"
 #include "ceres/internal/disable_warnings.h"
 #include "ceres/internal/export.h"
@@ -58,7 +57,7 @@
 CERES_NO_EXPORT int ComputeSingleLinkageClustering(
     const SingleLinkageClusteringOptions& options,
     const WeightedGraph<int>& graph,
-    std::unordered_map<int, int>* membership);
+    absl::flat_hash_map<int, int>* membership);
 
 }  // namespace ceres::internal
 
diff --git a/internal/ceres/single_linkage_clustering_test.cc b/internal/ceres/single_linkage_clustering_test.cc
index cc16cb4..6cb7e59 100644
--- a/internal/ceres/single_linkage_clustering_test.cc
+++ b/internal/ceres/single_linkage_clustering_test.cc
@@ -30,8 +30,7 @@
 
 #include "ceres/single_linkage_clustering.h"
 
-#include <unordered_map>
-
+#include "absl/container/flat_hash_map.h"
 #include "ceres/graph.h"
 #include "gtest/gtest.h"
 
@@ -52,7 +51,7 @@
   graph.AddEdge(4, 5, 1.0);
 
   SingleLinkageClusteringOptions options;
-  std::unordered_map<int, int> membership;
+  absl::flat_hash_map<int, int> membership;
   ComputeSingleLinkageClustering(options, graph, &membership);
   EXPECT_EQ(membership.size(), kNumVertices);
 
@@ -81,7 +80,7 @@
   graph.AddEdge(4, 5, 0.5);
 
   SingleLinkageClusteringOptions options;
-  std::unordered_map<int, int> membership;
+  absl::flat_hash_map<int, int> membership;
   ComputeSingleLinkageClustering(options, graph, &membership);
   EXPECT_EQ(membership.size(), kNumVertices);
 
@@ -111,7 +110,7 @@
   graph.AddEdge(4, 5, 1.0);
 
   SingleLinkageClusteringOptions options;
-  std::unordered_map<int, int> membership;
+  absl::flat_hash_map<int, int> membership;
   ComputeSingleLinkageClustering(options, graph, &membership);
   EXPECT_EQ(membership.size(), kNumVertices);
 
diff --git a/internal/ceres/visibility.cc b/internal/ceres/visibility.cc
index 05ac8b0..ca0853f 100644
--- a/internal/ceres/visibility.cc
+++ b/internal/ceres/visibility.cc
@@ -35,7 +35,6 @@
 #include <ctime>
 #include <memory>
 #include <set>
-#include <unordered_map>
 #include <utility>
 #include <vector>
 
@@ -43,7 +42,6 @@
 #include "absl/log/log.h"
 #include "ceres/block_structure.h"
 #include "ceres/graph.h"
-#include "ceres/pair_hash.h"
 
 namespace ceres::internal {
 
@@ -102,7 +100,7 @@
 
   // Map from camera pairs to number of points visible to both cameras
   // in the pair.
-  std::unordered_map<std::pair<int, int>, int, pair_hash> camera_pairs;
+  absl::flat_hash_map<std::pair<int, int>, int> camera_pairs;
 
   // Count the number of points visible to each camera/f_block pair.
   for (const auto& inverse_visibility_set : inverse_visibility) {
diff --git a/internal/ceres/visibility_based_preconditioner.cc b/internal/ceres/visibility_based_preconditioner.cc
index 0f46a87..0062e1a 100644
--- a/internal/ceres/visibility_based_preconditioner.cc
+++ b/internal/ceres/visibility_based_preconditioner.cc
@@ -36,11 +36,12 @@
 #include <memory>
 #include <set>
 #include <string>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
 #include "Eigen/Dense"
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "absl/log/check.h"
 #include "absl/log/log.h"
 #include "ceres/block_random_access_sparse_matrix.h"
@@ -175,7 +176,7 @@
   auto schur_complement_graph = CreateSchurComplementGraph(visibility);
   CHECK(schur_complement_graph != nullptr);
 
-  std::unordered_map<int, int> membership;
+  absl::flat_hash_map<int, int> membership;
 
   if (options_.visibility_clustering_type == CANONICAL_VIEWS) {
     std::vector<int> centers;
@@ -457,17 +458,17 @@
 // each vertex.
 void VisibilityBasedPreconditioner::ForestToClusterPairs(
     const WeightedGraph<int>& forest,
-    std::unordered_set<std::pair<int, int>, pair_hash>* cluster_pairs) const {
+    absl::flat_hash_set<std::pair<int, int>>* cluster_pairs) const {
   CHECK(cluster_pairs != nullptr);
   cluster_pairs->clear();
-  const std::unordered_set<int>& vertices = forest.vertices();
+  const absl::flat_hash_set<int>& vertices = forest.vertices();
   CHECK_EQ(vertices.size(), num_clusters_);
 
   // Add all the cluster pairs corresponding to the edges in the
   // forest.
   for (const int cluster1 : vertices) {
     cluster_pairs->insert(std::make_pair(cluster1, cluster1));
-    const std::unordered_set<int>& neighbors = forest.Neighbors(cluster1);
+    const absl::flat_hash_set<int>& neighbors = forest.Neighbors(cluster1);
     for (const int cluster2 : neighbors) {
       if (cluster1 < cluster2) {
         cluster_pairs->insert(std::make_pair(cluster1, cluster2));
@@ -528,7 +529,7 @@
   return cluster_graph;
 }
 
-// Canonical views clustering returns a std::unordered_map from vertices to
+// Canonical views clustering returns a absl::flat_hash_set from vertices to
 // cluster ids. Convert this into a flat array for quick lookup. It is
 // possible that some of the vertices may not be associated with any
 // cluster. In that case, randomly assign them to one of the clusters.
@@ -537,13 +538,13 @@
 // the membership_map, we also map the cluster ids to a contiguous set
 // of integers so that the cluster ids are in [0, num_clusters_).
 void VisibilityBasedPreconditioner::FlattenMembershipMap(
-    const std::unordered_map<int, int>& membership_map,
+    const absl::flat_hash_map<int, int>& membership_map,
     std::vector<int>* membership_vector) const {
   CHECK(membership_vector != nullptr);
   membership_vector->resize(0);
   membership_vector->resize(num_blocks_, -1);
 
-  std::unordered_map<int, int> cluster_id_to_index;
+  absl::flat_hash_map<int, int> cluster_id_to_index;
   // Iterate over the cluster membership map and update the
   // cluster_membership_ vector assigning arbitrary cluster ids to
   // the few cameras that have not been clustered.
diff --git a/internal/ceres/visibility_based_preconditioner.h b/internal/ceres/visibility_based_preconditioner.h
index d2d4aad..68827b3 100644
--- a/internal/ceres/visibility_based_preconditioner.h
+++ b/internal/ceres/visibility_based_preconditioner.h
@@ -50,15 +50,14 @@
 
 #include <memory>
 #include <set>
-#include <unordered_map>
-#include <unordered_set>
 #include <utility>
 #include <vector>
 
+#include "absl/container/flat_hash_map.h"
+#include "absl/container/flat_hash_set.h"
 #include "ceres/block_structure.h"
 #include "ceres/graph.h"
 #include "ceres/linear_solver.h"
-#include "ceres/pair_hash.h"
 #include "ceres/preconditioner.h"
 #include "ceres/sparse_cholesky.h"
 
@@ -156,7 +155,7 @@
   void ScaleOffDiagonalCells();
 
   void ClusterCameras(const std::vector<std::set<int>>& visibility);
-  void FlattenMembershipMap(const std::unordered_map<int, int>& membership_map,
+  void FlattenMembershipMap(const absl::flat_hash_map<int, int>& membership_map,
                             std::vector<int>* membership_vector) const;
   void ComputeClusterVisibility(
       const std::vector<std::set<int>>& visibility,
@@ -165,7 +164,7 @@
       const std::vector<std::set<int>>& visibility) const;
   void ForestToClusterPairs(
       const WeightedGraph<int>& forest,
-      std::unordered_set<std::pair<int, int>, pair_hash>* cluster_pairs) const;
+      absl::flat_hash_set<std::pair<int, int>>* cluster_pairs) const;
   void ComputeBlockPairsInPreconditioner(const CompressedRowBlockStructure& bs);
   bool IsBlockPairInPreconditioner(int block1, int block2) const;
   bool IsBlockPairOffDiagonal(int block1, int block2) const;
@@ -189,7 +188,7 @@
 
   // Set of cluster pairs (including self pairs (i,i)) in the
   // preconditioner.
-  std::unordered_set<std::pair<int, int>, pair_hash> cluster_pairs_;
+  absl::flat_hash_set<std::pair<int, int>> cluster_pairs_;
   std::unique_ptr<SchurEliminatorBase> eliminator_;
 
   // Preconditioner matrix.
diff --git a/internal/ceres/visibility_based_preconditioner_test.cc b/internal/ceres/visibility_based_preconditioner_test.cc
index 4558964..acc7da0 100644
--- a/internal/ceres/visibility_based_preconditioner_test.cc
+++ b/internal/ceres/visibility_based_preconditioner_test.cc
@@ -110,7 +110,7 @@
 
 //   AssertionResult IsSparsityStructureValid() {
 //     preconditioner_->InitStorage(*A_->block_structure());
-//     const std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
+//     const absl::flat_hash_set<pair<int, int>>& cluster_pairs =
 //     get_cluster_pairs(); const vector<int>& cluster_membership =
 //     get_cluster_membership();
 
@@ -135,7 +135,7 @@
 
 //   AssertionResult PreconditionerValuesMatch() {
 //     preconditioner_->Update(*A_, D_.get());
-//     const std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
+//     const absl::flat_hash_set<pair<int, int>>& cluster_pairs =
 //     get_cluster_pairs(); const BlockRandomAccessSparseMatrix* m = get_m();
 //     Matrix preconditioner_matrix;
 //     m->matrix()->ToDenseMatrix(&preconditioner_matrix);
@@ -203,11 +203,11 @@
 //     return &preconditioner_->block_pairs_;
 //   }
 
-//   const std::unordered_set<pair<int, int>, pair_hash>& get_cluster_pairs() {
+//   const absl::flat_hash_set<pair<int, int>>& get_cluster_pairs() {
 //     return preconditioner_->cluster_pairs_;
 //   }
 
-//   std::unordered_set<pair<int, int>, pair_hash>* get_mutable_cluster_pairs()
+//   absl::flat_hash_set<pair<int, int>>* get_mutable_cluster_pairs()
 //   {
 //     return &preconditioner_->cluster_pairs_;
 //   }
@@ -253,7 +253,7 @@
 
 //   *get_mutable_num_clusters() = 1;
 
-//   std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
+//   absl::flat_hash_set<pair<int, int>>& cluster_pairs =
 //   *get_mutable_cluster_pairs(); cluster_pairs.clear();
 //   cluster_pairs.insert(make_pair(0, 0));
 
@@ -300,7 +300,7 @@
 //   }
 //   *get_mutable_num_clusters() = kNumClusters;
 
-//   std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
+//   absl::flat_hash_set<pair<int, int>>& cluster_pairs =
 //   *get_mutable_cluster_pairs(); cluster_pairs.clear(); for (int i = 0; i <
 //   kNumClusters; ++i) {
 //     cluster_pairs.insert(make_pair(i, i));
@@ -326,7 +326,7 @@
 //   *get_mutable_num_clusters() = kNumClusters;
 
 //   // Spanning forest has structure 0-1 2
-//   std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
+//   absl::flat_hash_set<pair<int, int>>& cluster_pairs =
 //   *get_mutable_cluster_pairs(); cluster_pairs.clear(); for (int i = 0; i <
 //   kNumClusters; ++i) {
 //     cluster_pairs.insert(make_pair(i, i));