Improvements to Schur template specializations

1. Refactor the python code that generates the template specializations
   to remove code duplication.
2. Improved the logic for template specialization selection where
   Eigen::Dynamic now serves as a wildcard.
3. Added schur_templates.h/cc which allows querying the set of available
   template specializations without instantiating a linear solver.
4. Added Solver::Summary::schur_structre_given and
   Solver::Summary::schur_structure_used and expose them in
   Solver::Summary::FullReport for better performance debugging.
5. Updated the templates with newer dates and some minor comments cleanup
   which lead to the the template specializations to be re-generated.

Change-Id: Iaf3c6f714353597899916c300465da01f151c3de
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index bdcbdd1..0f6c4be 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -98,6 +98,7 @@
     schur_complement_solver.cc
     schur_eliminator.cc
     schur_jacobi_preconditioner.cc
+    schur_templates.cc
     scratch_evaluate_preparer.cc
     single_linkage_clustering.cc
     solver.cc
diff --git a/internal/ceres/generate_eliminator_specialization.py b/internal/ceres/generate_template_specializations.py
similarity index 67%
rename from internal/ceres/generate_eliminator_specialization.py
rename to internal/ceres/generate_template_specializations.py
index e89e7a4..cbfcb71 100644
--- a/internal/ceres/generate_eliminator_specialization.py
+++ b/internal/ceres/generate_template_specializations.py
@@ -67,10 +67,74 @@
                    (4, 4, 2),
                    (4, 4, 3),
                    (4, 4, 4),
-                   (4, 4, "Eigen::Dynamic"),
-                   ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")]
-HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+                   (4, 4, "Eigen::Dynamic")]
+
+import schur_eliminator_template
+import partitioned_matrix_view_template
+
+def SuffixForSize(size):
+  if size == "Eigen::Dynamic":
+    return "d"
+  return str(size)
+
+def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size):
+  return "_".join([prefix] + map(SuffixForSize, (row_block_size,
+                                                 e_block_size,
+                                                 f_block_size)))
+
+def GenerateFactoryConditional(row_block_size, e_block_size, f_block_size):
+  conditionals = []
+  if (row_block_size != "Eigen::Dynamic"):
+    conditionals.append("(options.row_block_size == %s)" % row_block_size)
+  if (e_block_size != "Eigen::Dynamic"):
+    conditionals.append("(options.e_block_size == %s)" % e_block_size)
+  if (f_block_size != "Eigen::Dynamic"):
+    conditionals.append("(options.f_block_size == %s)" % f_block_size)
+  if (len(conditionals) == 0):
+    return "%s"
+
+  if (len(conditionals) == 1):
+    return " if " + conditionals[0] + "{\n  %s\n }\n"
+
+  return " if (" + " &&\n  ".join(conditionals) + ") {\n  %s\n }\n"
+
+def Specialize(name, data):
+  """
+  Generate specialization code and the conditionals to instantiate it.
+  """
+  f = open(name + ".cc", "w")
+  f.write(data["HEADER"])
+  f.write(data["FACTORY_FILE_HEADER"])
+
+  for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
+    output = SpecializationFilename("generated/" + name,
+                                    row_block_size,
+                                    e_block_size,
+                                    f_block_size) + ".cc"
+    fptr = open(output, "w")
+    fptr.write(data["HEADER"])
+
+    template = data["SPECIALIZATION_FILE"]
+    if (row_block_size == "Eigen::Dynamic" and
+        e_block_size == "Eigen::Dynamic" and
+        f_block_size == "Eigen::Dynamic"):
+      template = data["DYNAMIC_FILE"]
+
+    fptr.write(template % (row_block_size, e_block_size, f_block_size))
+    fptr.close()
+
+    FACTORY_CONDITIONAL =
+    GenerateFactoryConditional(row_block_size, e_block_size, f_block_size)
+    f.write(FACTORY_CONDITIONAL % data["FACTORY"] %
+              (row_block_size, e_block_size, f_block_size));
+
+  f.write(data["FACTORY_FOOTER"])
+  f.close()
+
+
+
+QUERY_HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -99,7 +163,7 @@
 //
 // Author: sameeragarwal@google.com (Sameer Agarwal)
 //
-// Template specialization of SchurEliminator.
+// What template specializations are available.
 //
 // ========================================
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
@@ -108,124 +172,60 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 """
 
-DYNAMIC_FILE = """
-
-#include "ceres/schur_eliminator_impl.h"
+QUERY_FILE_HEADER = """
 #include "ceres/internal/eigen.h"
+#include "ceres/schur_templates.h"
 
 namespace ceres {
 namespace internal {
 
-template class SchurEliminator<%s, %s, %s>;
-
-}  // namespace internal
-}  // namespace ceres
-"""
-
-SPECIALIZATION_FILE = """
-// This include must come before any #ifndef check on Ceres compile options.
-#include "ceres/internal/port.h"
-
-#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
-
-#include "ceres/schur_eliminator_impl.h"
-#include "ceres/internal/eigen.h"
-
-namespace ceres {
-namespace internal {
-
-template class SchurEliminator<%s, %s, %s>;
-
-}  // namespace internal
-}  // namespace ceres
-
-#endif  // CERES_RESTRICT_SCHUR_SPECIALIZATION
-"""
-
-FACTORY_FILE_HEADER = """
-#include "ceres/linear_solver.h"
-#include "ceres/schur_eliminator.h"
-#include "ceres/internal/eigen.h"
-
-namespace ceres {
-namespace internal {
-
-SchurEliminatorBase*
-SchurEliminatorBase::Create(const LinearSolver::Options& options) {
+void GetBestSchurTemplateSpecialization(int* row_block_size,
+                                        int* e_block_size,
+                                        int* f_block_size) {
+  LinearSolver::Options options;
+  options.row_block_size = *row_block_size;
+  options.e_block_size = *e_block_size;
+  options.f_block_size = *f_block_size;
+  *row_block_size = Eigen::Dynamic;
+  *e_block_size = Eigen::Dynamic;
+  *f_block_size = Eigen::Dynamic;
 #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
 """
 
-FACTORY_CONDITIONAL = """  if ((options.row_block_size == %s) &&
-      (options.e_block_size == %s) &&
-      (options.f_block_size == %s)) {
-    return new SchurEliminator<%s, %s, %s>(options);
-  }
-"""
-
-FACTORY_FOOTER = """
+QUERY_FOOTER = """
 #endif
-  VLOG(1) << "Template specializations not found for <"
-          << options.row_block_size << ","
-          << options.e_block_size << ","
-          << options.f_block_size << ">";
-  return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>(options);
+  return;
 }
 
 }  // namespace internal
 }  // namespace ceres
 """
 
+QUERY_ACTION = """*row_block_size = %s;
+  *e_block_size = %s;
+  *f_block_size = %s;
+  return;"""
 
-def SuffixForSize(size):
-  if size == "Eigen::Dynamic":
-    return "d"
-  return str(size)
-
-
-def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size):
-  return "_".join([prefix] + map(SuffixForSize, (row_block_size,
-                                                 e_block_size,
-                                                 f_block_size)))
-
-
-def Specialize():
+def GenerateQueryFile():
   """
   Generate specialization code and the conditionals to instantiate it.
   """
-  f = open("schur_eliminator.cc", "w")
-  f.write(HEADER)
-  f.write(FACTORY_FILE_HEADER)
+  f = open("schur_templates.cc", "w")
+  f.write(QUERY_HEADER)
+  f.write(QUERY_FILE_HEADER)
 
   for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
-    output = SpecializationFilename("generated/schur_eliminator",
-                                    row_block_size,
-                                    e_block_size,
-                                    f_block_size) + ".cc"
-    fptr = open(output, "w")
-    fptr.write(HEADER)
+    FACTORY_CONDITIONAL = GenerateFactoryConditional(row_block_size, e_block_size, f_block_size)
+    f.write(FACTORY_CONDITIONAL % QUERY_ACTION % (row_block_size, e_block_size, f_block_size));
 
-    template = SPECIALIZATION_FILE
-    if (row_block_size == "Eigen::Dynamic" and
-        e_block_size == "Eigen::Dynamic" and
-        f_block_size == "Eigen::Dynamic"):
-      template = DYNAMIC_FILE
-
-    fptr.write(template % (row_block_size, e_block_size, f_block_size))
-    fptr.close()
-
-    f.write(FACTORY_CONDITIONAL % (row_block_size,
-                                   e_block_size,
-                                   f_block_size,
-                                   row_block_size,
-                                   e_block_size,
-                                   f_block_size))
-  f.write(FACTORY_FOOTER)
+  f.write(QUERY_FOOTER)
   f.close()
 
 
 if __name__ == "__main__":
-  Specialize()
+  Specialize("schur_eliminator", schur_eliminator_template.__dict__)
+  Specialize("partitioned_matrix_view", partitioned_matrix_view_template.__dict__)
+  GenerateQueryFile()
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc b/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc
index 500115b..86ad17b 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_2_2.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc b/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc
index 1384cb6..33018d5 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_2_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc b/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc
index 030035e..a429a54 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_2_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc b/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc
index c9501b5..f6f03ea 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_2_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc b/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc
index c2639bf..0b73e1a 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_3_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc b/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc
index 693e439..bc4a861 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_3_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc b/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc
index 7b9368f..fe8f7dd 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_3_6.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc b/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc
index e72c5f6..ac493fc 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_3_9.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc b/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc
index c1f410e..e29efaf 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_3_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc b/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc
index 7292c33..e61e0a3 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_4_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc b/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc
index 891d65a..2e1170d 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_4_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc b/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc
index 395f6bd..83015f1 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_4_8.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc b/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc
index 88952b1..25671f9 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_4_9.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc b/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc
index 7733e19..d259802 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_4_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc b/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc
index 117a0cd..c956759 100644
--- a/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_2_d_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc b/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc
index a620bb7..f08049c 100644
--- a/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_4_4_2.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc b/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc
index 2978630..9342612 100644
--- a/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_4_4_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc b/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc
index bcd03b0..8b273fa 100644
--- a/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_4_4_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc b/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc
index 6b541ec..e8b45e4 100644
--- a/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_4_4_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc b/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
index 85111e7..434902c 100644
--- a/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
+++ b/internal/ceres/generated/partitioned_matrix_view_d_d_d.cc
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 
 #include "ceres/partitioned_matrix_view_impl.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_2_2.cc b/internal/ceres/generated/schur_eliminator_2_2_2.cc
index ac07a3f..79fcf43 100644
--- a/internal/ceres/generated/schur_eliminator_2_2_2.cc
+++ b/internal/ceres/generated/schur_eliminator_2_2_2.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_2_3.cc b/internal/ceres/generated/schur_eliminator_2_2_3.cc
index 0ec0955..edd7fb6 100644
--- a/internal/ceres/generated/schur_eliminator_2_2_3.cc
+++ b/internal/ceres/generated/schur_eliminator_2_2_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_2_4.cc b/internal/ceres/generated/schur_eliminator_2_2_4.cc
index 74a42cc..692267d 100644
--- a/internal/ceres/generated/schur_eliminator_2_2_4.cc
+++ b/internal/ceres/generated/schur_eliminator_2_2_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_2_d.cc b/internal/ceres/generated/schur_eliminator_2_2_d.cc
index 5ce757f..33d9c6d 100644
--- a/internal/ceres/generated/schur_eliminator_2_2_d.cc
+++ b/internal/ceres/generated/schur_eliminator_2_2_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_3_3.cc b/internal/ceres/generated/schur_eliminator_2_3_3.cc
index 2e7ae28..4a5e2fe 100644
--- a/internal/ceres/generated/schur_eliminator_2_3_3.cc
+++ b/internal/ceres/generated/schur_eliminator_2_3_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_3_4.cc b/internal/ceres/generated/schur_eliminator_2_3_4.cc
index 4432070..7ee63d0 100644
--- a/internal/ceres/generated/schur_eliminator_2_3_4.cc
+++ b/internal/ceres/generated/schur_eliminator_2_3_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_3_6.cc b/internal/ceres/generated/schur_eliminator_2_3_6.cc
index ac2f358..108760e 100644
--- a/internal/ceres/generated/schur_eliminator_2_3_6.cc
+++ b/internal/ceres/generated/schur_eliminator_2_3_6.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_3_9.cc b/internal/ceres/generated/schur_eliminator_2_3_9.cc
index 930ab44..4fea2fa 100644
--- a/internal/ceres/generated/schur_eliminator_2_3_9.cc
+++ b/internal/ceres/generated/schur_eliminator_2_3_9.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_3_d.cc b/internal/ceres/generated/schur_eliminator_2_3_d.cc
index 486c53d..0d13c99 100644
--- a/internal/ceres/generated/schur_eliminator_2_3_d.cc
+++ b/internal/ceres/generated/schur_eliminator_2_3_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_4_3.cc b/internal/ceres/generated/schur_eliminator_2_4_3.cc
index 6f247a7..3827c65 100644
--- a/internal/ceres/generated/schur_eliminator_2_4_3.cc
+++ b/internal/ceres/generated/schur_eliminator_2_4_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_4_4.cc b/internal/ceres/generated/schur_eliminator_2_4_4.cc
index c44cd04..47bdfab 100644
--- a/internal/ceres/generated/schur_eliminator_2_4_4.cc
+++ b/internal/ceres/generated/schur_eliminator_2_4_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_4_8.cc b/internal/ceres/generated/schur_eliminator_2_4_8.cc
index c9a0d5f..862c76a 100644
--- a/internal/ceres/generated/schur_eliminator_2_4_8.cc
+++ b/internal/ceres/generated/schur_eliminator_2_4_8.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_4_9.cc b/internal/ceres/generated/schur_eliminator_2_4_9.cc
index b0455b0..5b5b7cc 100644
--- a/internal/ceres/generated/schur_eliminator_2_4_9.cc
+++ b/internal/ceres/generated/schur_eliminator_2_4_9.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_4_d.cc b/internal/ceres/generated/schur_eliminator_2_4_d.cc
index 3234380..ce2d450 100644
--- a/internal/ceres/generated/schur_eliminator_2_4_d.cc
+++ b/internal/ceres/generated/schur_eliminator_2_4_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_2_d_d.cc b/internal/ceres/generated/schur_eliminator_2_d_d.cc
index 311f855..9b02bd9 100644
--- a/internal/ceres/generated/schur_eliminator_2_d_d.cc
+++ b/internal/ceres/generated/schur_eliminator_2_d_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_4_4_2.cc b/internal/ceres/generated/schur_eliminator_4_4_2.cc
index bc40bd5..10f709d 100644
--- a/internal/ceres/generated/schur_eliminator_4_4_2.cc
+++ b/internal/ceres/generated/schur_eliminator_4_4_2.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_4_4_3.cc b/internal/ceres/generated/schur_eliminator_4_4_3.cc
index cca88c8..bcbcc74 100644
--- a/internal/ceres/generated/schur_eliminator_4_4_3.cc
+++ b/internal/ceres/generated/schur_eliminator_4_4_3.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_4_4_4.cc b/internal/ceres/generated/schur_eliminator_4_4_4.cc
index 33c94a9..44ecc87 100644
--- a/internal/ceres/generated/schur_eliminator_4_4_4.cc
+++ b/internal/ceres/generated/schur_eliminator_4_4_4.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_4_4_d.cc b/internal/ceres/generated/schur_eliminator_4_4_d.cc
index 1a1866f..69c8563 100644
--- a/internal/ceres/generated/schur_eliminator_4_4_d.cc
+++ b/internal/ceres/generated/schur_eliminator_4_4_d.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 // This include must come before any #ifndef check on Ceres compile options.
 #include "ceres/internal/port.h"
diff --git a/internal/ceres/generated/schur_eliminator_d_d_d.cc b/internal/ceres/generated/schur_eliminator_d_d_d.cc
index 6b18ef8..4a8a9c6 100644
--- a/internal/ceres/generated/schur_eliminator_d_d_d.cc
+++ b/internal/ceres/generated/schur_eliminator_d_d_d.cc
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 
 #include "ceres/schur_eliminator_impl.h"
diff --git a/internal/ceres/partitioned_matrix_view.cc b/internal/ceres/partitioned_matrix_view.cc
index 8054964..210eff1 100644
--- a/internal/ceres/partitioned_matrix_view.cc
+++ b/internal/ceres/partitioned_matrix_view.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 #include "ceres/linear_solver.h"
 #include "ceres/partitioned_matrix_view.h"
@@ -51,126 +50,95 @@
 PartitionedMatrixViewBase::Create(const LinearSolver::Options& options,
                                   const BlockSparseMatrix& matrix) {
 #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 2)) {
-    return new PartitionedMatrixView<2, 2, 2>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 3)) {
-    return new PartitionedMatrixView<2, 2, 3>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 4)) {
-    return new PartitionedMatrixView<2, 2, 4>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<2, 2, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 3)) {
-    return new PartitionedMatrixView<2, 3, 3>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 4)) {
-    return new PartitionedMatrixView<2, 3, 4>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 6)) {
-    return new PartitionedMatrixView<2, 3, 6>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 9)) {
-    return new PartitionedMatrixView<2, 3, 9>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<2, 3, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 3)) {
-    return new PartitionedMatrixView<2, 4, 3>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 4)) {
-    return new PartitionedMatrixView<2, 4, 4>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 8)) {
-    return new PartitionedMatrixView<2, 4, 8>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 9)) {
-    return new PartitionedMatrixView<2, 4, 9>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<2, 4, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == Eigen::Dynamic) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<2, Eigen::Dynamic, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 2)) {
-    return new PartitionedMatrixView<4, 4, 2>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 3)) {
-    return new PartitionedMatrixView<4, 4, 3>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 4)) {
-    return new PartitionedMatrixView<4, 4, 4>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
-  if ((options.row_block_size == Eigen::Dynamic) &&
-      (options.e_block_size == Eigen::Dynamic) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new PartitionedMatrixView<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>(
-                 matrix, options.elimination_groups[0]);
-  }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 2)) {
+  return new PartitionedMatrixView<2, 2, 2>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 3)) {
+  return new PartitionedMatrixView<2, 2, 3>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 4)) {
+  return new PartitionedMatrixView<2, 2, 4>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2)) {
+  return new PartitionedMatrixView<2, 2, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 3)) {
+  return new PartitionedMatrixView<2, 3, 3>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 4)) {
+  return new PartitionedMatrixView<2, 3, 4>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 6)) {
+  return new PartitionedMatrixView<2, 3, 6>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 9)) {
+  return new PartitionedMatrixView<2, 3, 9>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3)) {
+  return new PartitionedMatrixView<2, 3, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  return new PartitionedMatrixView<2, 4, 3>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  return new PartitionedMatrixView<2, 4, 4>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 8)) {
+  return new PartitionedMatrixView<2, 4, 8>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 9)) {
+  return new PartitionedMatrixView<2, 4, 9>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4)) {
+  return new PartitionedMatrixView<2, 4, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
+ if (options.row_block_size == 2){
+  return new PartitionedMatrixView<2, Eigen::Dynamic, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 2)) {
+  return new PartitionedMatrixView<4, 4, 2>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  return new PartitionedMatrixView<4, 4, 3>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  return new PartitionedMatrixView<4, 4, 4>(matrix, options.elimination_groups[0]);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4)) {
+  return new PartitionedMatrixView<4, 4, Eigen::Dynamic>(matrix, options.elimination_groups[0]);
+ }
 
 #endif
   VLOG(1) << "Template specializations not found for <"
diff --git a/internal/ceres/generate_partitioned_matrix_view_specializations.py b/internal/ceres/partitioned_matrix_view_template.py
similarity index 67%
rename from internal/ceres/generate_partitioned_matrix_view_specializations.py
rename to internal/ceres/partitioned_matrix_view_template.py
index c4ac3cf..f610833 100644
--- a/internal/ceres/generate_partitioned_matrix_view_specializations.py
+++ b/internal/ceres/partitioned_matrix_view_template.py
@@ -46,29 +46,8 @@
 # The list of tuples, specializations indicates the set of
 # specializations that is generated.
 
-# Set of template specializations to generate
-SPECIALIZATIONS = [(2, 2, 2),
-                   (2, 2, 3),
-                   (2, 2, 4),
-                   (2, 2, "Eigen::Dynamic"),
-                   (2, 3, 3),
-                   (2, 3, 4),
-                   (2, 3, 6),
-                   (2, 3, 9),
-                   (2, 3, "Eigen::Dynamic"),
-                   (2, 4, 3),
-                   (2, 4, 4),
-                   (2, 4, 8),
-                   (2, 4, 9),
-                   (2, 4, "Eigen::Dynamic"),
-                   (2, "Eigen::Dynamic", "Eigen::Dynamic"),
-                   (4, 4, 2),
-                   (4, 4, 3),
-                   (4, 4, 4),
-                   (4, 4, "Eigen::Dynamic"),
-                   ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")]
 HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -106,8 +85,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_partitioned_matrix_view_specializations.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 """
 
 DYNAMIC_FILE = """
@@ -157,14 +135,7 @@
                                   const BlockSparseMatrix& matrix) {
 #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
 """
-
-FACTORY_CONDITIONAL = """  if ((options.row_block_size == %s) &&
-      (options.e_block_size == %s) &&
-      (options.f_block_size == %s)) {
-    return new PartitionedMatrixView<%s, %s, %s>(
-                 matrix, options.elimination_groups[0]);
-  }
-"""
+FACTORY = """return new PartitionedMatrixView<%s, %s, %s>(matrix, options.elimination_groups[0]);"""
 
 FACTORY_FOOTER = """
 #endif
@@ -179,54 +150,3 @@
 }  // namespace internal
 }  // namespace ceres
 """
-
-
-def SuffixForSize(size):
-  if size == "Eigen::Dynamic":
-    return "d"
-  return str(size)
-
-
-def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size):
-  return "_".join([prefix] + map(SuffixForSize, (row_block_size,
-                                                 e_block_size,
-                                                 f_block_size)))
-
-
-def Specialize():
-  """
-  Generate specialization code and the conditionals to instantiate it.
-  """
-  f = open("partitioned_matrix_view.cc", "w")
-  f.write(HEADER)
-  f.write(FACTORY_FILE_HEADER)
-
-  for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
-    output = SpecializationFilename("generated/partitioned_matrix_view",
-                                    row_block_size,
-                                    e_block_size,
-                                    f_block_size) + ".cc"
-    fptr = open(output, "w")
-    fptr.write(HEADER)
-
-    template = SPECIALIZATION_FILE
-    if (row_block_size == "Eigen::Dynamic" and
-        e_block_size == "Eigen::Dynamic" and
-        f_block_size == "Eigen::Dynamic"):
-      template = DYNAMIC_FILE
-
-    fptr.write(template % (row_block_size, e_block_size, f_block_size))
-    fptr.close()
-
-    f.write(FACTORY_CONDITIONAL % (row_block_size,
-                                   e_block_size,
-                                   f_block_size,
-                                   row_block_size,
-                                   e_block_size,
-                                   f_block_size))
-  f.write(FACTORY_FOOTER)
-  f.close()
-
-
-if __name__ == "__main__":
-  Specialize()
diff --git a/internal/ceres/schur_eliminator.cc b/internal/ceres/schur_eliminator.cc
index ec0e2a0..0def9f0 100644
--- a/internal/ceres/schur_eliminator.cc
+++ b/internal/ceres/schur_eliminator.cc
@@ -1,5 +1,5 @@
 // Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -37,8 +37,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 
 #include "ceres/linear_solver.h"
 #include "ceres/schur_eliminator.h"
@@ -50,106 +49,95 @@
 SchurEliminatorBase*
 SchurEliminatorBase::Create(const LinearSolver::Options& options) {
 #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 2)) {
-    return new SchurEliminator<2, 2, 2>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 3)) {
-    return new SchurEliminator<2, 2, 3>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == 4)) {
-    return new SchurEliminator<2, 2, 4>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 2) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<2, 2, Eigen::Dynamic>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 3)) {
-    return new SchurEliminator<2, 3, 3>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 4)) {
-    return new SchurEliminator<2, 3, 4>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 6)) {
-    return new SchurEliminator<2, 3, 6>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == 9)) {
-    return new SchurEliminator<2, 3, 9>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 3) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<2, 3, Eigen::Dynamic>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 3)) {
-    return new SchurEliminator<2, 4, 3>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 4)) {
-    return new SchurEliminator<2, 4, 4>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 8)) {
-    return new SchurEliminator<2, 4, 8>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 9)) {
-    return new SchurEliminator<2, 4, 9>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<2, 4, Eigen::Dynamic>(options);
-  }
-  if ((options.row_block_size == 2) &&
-      (options.e_block_size == Eigen::Dynamic) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>(options);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 2)) {
-    return new SchurEliminator<4, 4, 2>(options);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 3)) {
-    return new SchurEliminator<4, 4, 3>(options);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == 4)) {
-    return new SchurEliminator<4, 4, 4>(options);
-  }
-  if ((options.row_block_size == 4) &&
-      (options.e_block_size == 4) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<4, 4, Eigen::Dynamic>(options);
-  }
-  if ((options.row_block_size == Eigen::Dynamic) &&
-      (options.e_block_size == Eigen::Dynamic) &&
-      (options.f_block_size == Eigen::Dynamic)) {
-    return new SchurEliminator<Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic>(options);
-  }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 2)) {
+  return new SchurEliminator<2, 2, 2>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 3)) {
+  return new SchurEliminator<2, 2, 3>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 4)) {
+  return new SchurEliminator<2, 2, 4>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2)) {
+  return new SchurEliminator<2, 2, Eigen::Dynamic>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 3)) {
+  return new SchurEliminator<2, 3, 3>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 4)) {
+  return new SchurEliminator<2, 3, 4>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 6)) {
+  return new SchurEliminator<2, 3, 6>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 9)) {
+  return new SchurEliminator<2, 3, 9>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3)) {
+  return new SchurEliminator<2, 3, Eigen::Dynamic>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  return new SchurEliminator<2, 4, 3>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  return new SchurEliminator<2, 4, 4>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 8)) {
+  return new SchurEliminator<2, 4, 8>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 9)) {
+  return new SchurEliminator<2, 4, 9>(options);
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4)) {
+  return new SchurEliminator<2, 4, Eigen::Dynamic>(options);
+ }
+ if (options.row_block_size == 2){
+  return new SchurEliminator<2, Eigen::Dynamic, Eigen::Dynamic>(options);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 2)) {
+  return new SchurEliminator<4, 4, 2>(options);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  return new SchurEliminator<4, 4, 3>(options);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  return new SchurEliminator<4, 4, 4>(options);
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4)) {
+  return new SchurEliminator<4, 4, Eigen::Dynamic>(options);
+ }
 
 #endif
   VLOG(1) << "Template specializations not found for <"
diff --git a/internal/ceres/generate_eliminator_specialization.py b/internal/ceres/schur_eliminator_template.py
similarity index 67%
copy from internal/ceres/generate_eliminator_specialization.py
copy to internal/ceres/schur_eliminator_template.py
index e89e7a4..979ef25 100644
--- a/internal/ceres/generate_eliminator_specialization.py
+++ b/internal/ceres/schur_eliminator_template.py
@@ -1,5 +1,5 @@
 # Ceres Solver - A fast non-linear least squares minimizer
-# Copyright 2015 Google Inc. All rights reserved.
+# Copyright 2017 Google Inc. All rights reserved.
 # http://ceres-solver.org/
 #
 # Redistribution and use in source and binary forms, with or without
@@ -49,28 +49,9 @@
 # specializations that is generated.
 
 # Set of template specializations to generate
-SPECIALIZATIONS = [(2, 2, 2),
-                   (2, 2, 3),
-                   (2, 2, 4),
-                   (2, 2, "Eigen::Dynamic"),
-                   (2, 3, 3),
-                   (2, 3, 4),
-                   (2, 3, 6),
-                   (2, 3, 9),
-                   (2, 3, "Eigen::Dynamic"),
-                   (2, 4, 3),
-                   (2, 4, 4),
-                   (2, 4, 8),
-                   (2, 4, 9),
-                   (2, 4, "Eigen::Dynamic"),
-                   (2, "Eigen::Dynamic", "Eigen::Dynamic"),
-                   (4, 4, 2),
-                   (4, 4, 3),
-                   (4, 4, 4),
-                   (4, 4, "Eigen::Dynamic"),
-                   ("Eigen::Dynamic", "Eigen::Dynamic", "Eigen::Dynamic")]
+
 HEADER = """// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2015 Google Inc. All rights reserved.
+// Copyright 2017 Google Inc. All rights reserved.
 // http://ceres-solver.org/
 //
 // Redistribution and use in source and binary forms, with or without
@@ -108,8 +89,7 @@
 // THIS FILE IS AUTOGENERATED. DO NOT EDIT.
 //=========================================
 //
-// This file is generated using generate_eliminator_specialization.py.
-// Editing it manually is not recommended.
+// This file is generated using generate_template_specializations.py.
 """
 
 DYNAMIC_FILE = """
@@ -159,12 +139,7 @@
 #ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
 """
 
-FACTORY_CONDITIONAL = """  if ((options.row_block_size == %s) &&
-      (options.e_block_size == %s) &&
-      (options.f_block_size == %s)) {
-    return new SchurEliminator<%s, %s, %s>(options);
-  }
-"""
+FACTORY = """return new SchurEliminator<%s, %s, %s>(options);"""
 
 FACTORY_FOOTER = """
 #endif
@@ -178,54 +153,3 @@
 }  // namespace internal
 }  // namespace ceres
 """
-
-
-def SuffixForSize(size):
-  if size == "Eigen::Dynamic":
-    return "d"
-  return str(size)
-
-
-def SpecializationFilename(prefix, row_block_size, e_block_size, f_block_size):
-  return "_".join([prefix] + map(SuffixForSize, (row_block_size,
-                                                 e_block_size,
-                                                 f_block_size)))
-
-
-def Specialize():
-  """
-  Generate specialization code and the conditionals to instantiate it.
-  """
-  f = open("schur_eliminator.cc", "w")
-  f.write(HEADER)
-  f.write(FACTORY_FILE_HEADER)
-
-  for row_block_size, e_block_size, f_block_size in SPECIALIZATIONS:
-    output = SpecializationFilename("generated/schur_eliminator",
-                                    row_block_size,
-                                    e_block_size,
-                                    f_block_size) + ".cc"
-    fptr = open(output, "w")
-    fptr.write(HEADER)
-
-    template = SPECIALIZATION_FILE
-    if (row_block_size == "Eigen::Dynamic" and
-        e_block_size == "Eigen::Dynamic" and
-        f_block_size == "Eigen::Dynamic"):
-      template = DYNAMIC_FILE
-
-    fptr.write(template % (row_block_size, e_block_size, f_block_size))
-    fptr.close()
-
-    f.write(FACTORY_CONDITIONAL % (row_block_size,
-                                   e_block_size,
-                                   f_block_size,
-                                   row_block_size,
-                                   e_block_size,
-                                   f_block_size))
-  f.write(FACTORY_FOOTER)
-  f.close()
-
-
-if __name__ == "__main__":
-  Specialize()
diff --git a/internal/ceres/schur_templates.cc b/internal/ceres/schur_templates.cc
new file mode 100644
index 0000000..60f4291
--- /dev/null
+++ b/internal/ceres/schur_templates.cc
@@ -0,0 +1,211 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2017 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: sameeragarwal@google.com (Sameer Agarwal)
+//
+// What template specializations are available.
+//
+// ========================================
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+// THIS FILE IS AUTOGENERATED. DO NOT EDIT.
+//=========================================
+//
+// This file is generated using generate_template_specializations.py.
+
+#include "ceres/internal/eigen.h"
+#include "ceres/schur_templates.h"
+
+namespace ceres {
+namespace internal {
+
+void GetBestSchurTemplateSpecialization(int* row_block_size,
+                                        int* e_block_size,
+                                        int* f_block_size) {
+  LinearSolver::Options options;
+  options.row_block_size = *row_block_size;
+  options.e_block_size = *e_block_size;
+  options.f_block_size = *f_block_size;
+  *row_block_size = Eigen::Dynamic;
+  *e_block_size = Eigen::Dynamic;
+  *f_block_size = Eigen::Dynamic;
+#ifndef CERES_RESTRICT_SCHUR_SPECIALIZATION
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 2)) {
+  *row_block_size = 2;
+  *e_block_size = 2;
+  *f_block_size = 2;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 3)) {
+  *row_block_size = 2;
+  *e_block_size = 2;
+  *f_block_size = 3;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2) &&
+  (options.f_block_size == 4)) {
+  *row_block_size = 2;
+  *e_block_size = 2;
+  *f_block_size = 4;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 2)) {
+  *row_block_size = 2;
+  *e_block_size = 2;
+  *f_block_size = Eigen::Dynamic;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 3)) {
+  *row_block_size = 2;
+  *e_block_size = 3;
+  *f_block_size = 3;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 4)) {
+  *row_block_size = 2;
+  *e_block_size = 3;
+  *f_block_size = 4;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 6)) {
+  *row_block_size = 2;
+  *e_block_size = 3;
+  *f_block_size = 6;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3) &&
+  (options.f_block_size == 9)) {
+  *row_block_size = 2;
+  *e_block_size = 3;
+  *f_block_size = 9;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 3)) {
+  *row_block_size = 2;
+  *e_block_size = 3;
+  *f_block_size = Eigen::Dynamic;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  *row_block_size = 2;
+  *e_block_size = 4;
+  *f_block_size = 3;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  *row_block_size = 2;
+  *e_block_size = 4;
+  *f_block_size = 4;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 8)) {
+  *row_block_size = 2;
+  *e_block_size = 4;
+  *f_block_size = 8;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 9)) {
+  *row_block_size = 2;
+  *e_block_size = 4;
+  *f_block_size = 9;
+  return;
+ }
+ if ((options.row_block_size == 2) &&
+  (options.e_block_size == 4)) {
+  *row_block_size = 2;
+  *e_block_size = 4;
+  *f_block_size = Eigen::Dynamic;
+  return;
+ }
+ if (options.row_block_size == 2){
+  *row_block_size = 2;
+  *e_block_size = Eigen::Dynamic;
+  *f_block_size = Eigen::Dynamic;
+  return;
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 2)) {
+  *row_block_size = 4;
+  *e_block_size = 4;
+  *f_block_size = 2;
+  return;
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 3)) {
+  *row_block_size = 4;
+  *e_block_size = 4;
+  *f_block_size = 3;
+  return;
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4) &&
+  (options.f_block_size == 4)) {
+  *row_block_size = 4;
+  *e_block_size = 4;
+  *f_block_size = 4;
+  return;
+ }
+ if ((options.row_block_size == 4) &&
+  (options.e_block_size == 4)) {
+  *row_block_size = 4;
+  *e_block_size = 4;
+  *f_block_size = Eigen::Dynamic;
+  return;
+ }
+
+#endif
+  return;
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/schur_templates.h b/internal/ceres/schur_templates.h
new file mode 100644
index 0000000..90aee0a
--- /dev/null
+++ b/internal/ceres/schur_templates.h
@@ -0,0 +1,46 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2017 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: sameeragarwal@google.com (Sameer Agarwal)
+//
+
+#ifndef CERES_INTERNAL_SCHUR_TEMPLATES_H_
+#define CERES_INTERNAL_SCHUR_TEMPLATES_H_
+
+#include "ceres/linear_solver.h"
+
+namespace ceres {
+namespace internal {
+
+void GetBestSchurTemplateSpecialization(int* row_block_size,
+                                        int* e_block_size,
+                                        int* f_block_size);
+}  // namespace internal
+}  // namespace ceres
+
+#endif  // CERES_INTERNAL_SCHUR_TEMPLATES_H_
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 8411350..8d9fc08 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -34,6 +34,7 @@
 #include <algorithm>
 #include <sstream>   // NOLINT
 #include <vector>
+#include "ceres/detect_structure.h"
 #include "ceres/gradient_checking_cost_function.h"
 #include "ceres/internal/port.h"
 #include "ceres/parameter_block_ordering.h"
@@ -41,6 +42,7 @@
 #include "ceres/problem.h"
 #include "ceres/problem_impl.h"
 #include "ceres/program.h"
+#include "ceres/schur_templates.h"
 #include "ceres/solver_utils.h"
 #include "ceres/stringprintf.h"
 #include "ceres/types.h"
@@ -316,7 +318,7 @@
   }
 
   for (int i = 0; i < ordering.size() - 1; ++i) {
-    internal::StringAppendF(report, "%d, ", ordering[i]);
+    internal::StringAppendF(report, "%d,", ordering[i]);
   }
   internal::StringAppendF(report, "%d", ordering.back());
 }
@@ -446,6 +448,24 @@
   }
 }
 
+std::string SchurStructureToString(const int row_block_size,
+                                   const int e_block_size,
+                                   const int f_block_size) {
+  const std::string row =
+      (row_block_size == Eigen::Dynamic)
+      ? "d" : internal::StringPrintf("%d", row_block_size);
+
+  const std::string e =
+      (e_block_size == Eigen::Dynamic)
+      ? "d" : internal::StringPrintf("%d", e_block_size);
+
+  const std::string f =
+      (f_block_size == Eigen::Dynamic)
+      ? "d" : internal::StringPrintf("%d", f_block_size);
+
+  return internal::StringPrintf("%s,%s,%s",row.c_str(), e.c_str(), f.c_str());
+}
+
 }  // namespace
 
 bool Solver::Options::IsValid(string* error) const {
@@ -517,7 +537,32 @@
   scoped_ptr<Preprocessor> preprocessor(
       Preprocessor::Create(modified_options.minimizer_type));
   PreprocessedProblem pp;
+
   const bool status = preprocessor->Preprocess(modified_options, problem_impl, &pp);
+
+  if (IsSchurType(pp.options.linear_solver_type)) {
+    // TODO(sameeragarwal): We can likely eliminate the duplicate call
+    // to DetectStructure here and inside the linear solver, by
+    // calling this in the preprocessor.
+    int row_block_size;
+    int e_block_size;
+    int f_block_size;
+    DetectStructure(*static_cast<internal::BlockSparseMatrix*>(
+                        pp.minimizer_options.jacobian.get())
+                    ->block_structure(),
+                    pp.linear_solver_options.elimination_groups[0],
+                    &row_block_size,
+                    &e_block_size,
+                    &f_block_size);
+    summary->schur_structure_given =
+        SchurStructureToString(row_block_size, e_block_size, f_block_size);
+    internal::GetBestSchurTemplateSpecialization(&row_block_size,
+                                                 &e_block_size,
+                                                 &f_block_size);
+    summary->schur_structure_used =
+        SchurStructureToString(row_block_size, e_block_size, f_block_size);
+  }
+
   summary->fixed_cost = pp.fixed_cost;
   summary->preprocessor_time_in_seconds = WallTimeInSeconds() - start_time;
 
@@ -720,6 +765,12 @@
                   "Linear solver ordering %22s %24s\n",
                   given.c_str(),
                   used.c_str());
+    if (IsSchurType(linear_solver_type_used)) {
+      StringAppendF(&report,
+                    "Schur structure        %22s %24s\n",
+                    schur_structure_given.c_str(),
+                    schur_structure_used.c_str());
+    }
 
     if (inner_iterations_given) {
       StringAppendF(&report,