Codegen Optimizer API

This patch adds the classes Optimizer and OptimizationPass, which
define the core API of the codegen optimization.

A single (trivial) optimization is added that removes NOP
expressions from a graph.

Change-Id: I0430d6ffc61a474f997475ff787a81a9c69cfe23
diff --git a/include/ceres/codegen/internal/eliminate_nops.h b/include/ceres/codegen/internal/eliminate_nops.h
new file mode 100644
index 0000000..864aa0c
--- /dev/null
+++ b/include/ceres/codegen/internal/eliminate_nops.h
@@ -0,0 +1,67 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+//
+#ifndef CERES_PUBLIC_CODEGEN_INTERNAL_ELIMINATE_NOPS_H_
+#define CERES_PUBLIC_CODEGEN_INTERNAL_ELIMINATE_NOPS_H_
+
+#include "ceres/codegen/internal/expression_graph.h"
+#include "ceres/codegen/internal/optimization_pass_summary.h"
+
+namespace ceres {
+namespace internal {
+
+// [OptimizationPass] NOP Cleanup
+//
+// Short Description:
+//   Removes all expression with type==ExpressionType::NOP.
+//
+// Description:
+//   Removing an expression not at the end requires reindexing of all later
+//   expressions. Therefore, other optimization passes replace expression with
+//   NOP instead of removing them. This optimization pass removes all NOPs back
+//   to front.
+//   The returned value is equal to the number of removed NOPs.
+//
+// Example:
+//   v_0 = 1;
+//   // NOP
+//   v_2 = 2;
+//   v_3 = v_0 + v_2
+// Transforms to:
+//   v_0 = 1;
+//   v_1 = 2;
+//   v_2 = v_0 + v_1;
+
+OptimizationPassSummary EliminateNops(ExpressionGraph* graph);
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif  // CERES_PUBLIC_CODEGEN_INTERNAL_ELIMINATE_NOPS_H_
diff --git a/include/ceres/codegen/internal/optimization_pass_summary.h b/include/ceres/codegen/internal/optimization_pass_summary.h
new file mode 100644
index 0000000..1e376af
--- /dev/null
+++ b/include/ceres/codegen/internal/optimization_pass_summary.h
@@ -0,0 +1,51 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+//
+#ifndef CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZATION_PASS_SUMMARY_H_
+#define CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZATION_PASS_SUMMARY_H_
+
+#include <string>
+
+namespace ceres {
+namespace internal {
+
+struct OptimizationPassSummary {
+  bool expression_graph_changed = false;
+  std::string optimization_pass_name;
+  int num_expressions_replaced_by_nop = 0;
+  int num_expressions_removed = 0;
+  int num_expressions_inserted = 0;
+  int num_expressions_modified = 0;
+};
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif  // CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZATION_PASS_SUMMARY_H_
diff --git a/include/ceres/codegen/internal/optimize_expression_graph.h b/include/ceres/codegen/internal/optimize_expression_graph.h
new file mode 100644
index 0000000..8e06e8b
--- /dev/null
+++ b/include/ceres/codegen/internal/optimize_expression_graph.h
@@ -0,0 +1,74 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+//
+#ifndef CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZE_EXPRESSION_GRAPH_H_
+#define CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZE_EXPRESSION_GRAPH_H_
+
+#include <memory>
+#include <vector>
+
+#include "ceres/codegen/internal/expression_graph.h"
+#include "ceres/codegen/internal/optimization_pass_summary.h"
+
+namespace ceres {
+namespace internal {
+
+struct OptimizeExpressionGraphOptions {
+  int max_num_iterations = 100;
+  bool eliminate_nops = true;
+};
+
+struct OptimizeExpressionGraphSummary {
+  int num_iterations;
+  std::vector<OptimizationPassSummary> summaries;
+};
+
+// Optimize the given ExpressionGraph in-place according to the defined
+// OptimizeExpressionGraphOptions. This will change the ExpressionGraph, but the
+// generated code will output the same numerical values.
+//
+// The Optimization iteratively applies all OptimizationPasses until the graph
+// does not change anymore or max_num_iterations is reached. Pseudo Code:
+//
+//   for(int it = 0; it < max_num_iterations; ++it) {
+//      graph.hasChanged = false;
+//      for each pass in OptimizationPasses
+//         pass.applyTo(graph)
+//      if(!graph.hasChanged)
+//         break;
+//   }
+//
+OptimizeExpressionGraphSummary OptimizeExpressionGraph(
+    const OptimizeExpressionGraphOptions& options, ExpressionGraph* graph);
+
+}  // namespace internal
+}  // namespace ceres
+
+#endif  // CERES_PUBLIC_CODEGEN_INTERNAL_OPTIMIZE_EXPRESSION_GRAPH_H_
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 020a183..1611870 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -154,6 +154,8 @@
       expression.cc
       expression_graph.cc
       expression_ref.cc
+      codegen/optimize_expression_graph.cc
+      codegen/eliminate_nops.cc
   )
   list(APPEND CERES_INTERNAL_SRC ${CERES_CODEGEN_SRC})
 endif(CODE_GENERATION)
diff --git a/internal/ceres/codegen/CMakeLists.txt b/internal/ceres/codegen/CMakeLists.txt
index 7362aef..db21f9c 100644
--- a/internal/ceres/codegen/CMakeLists.txt
+++ b/internal/ceres/codegen/CMakeLists.txt
@@ -53,5 +53,6 @@
     ceres_test(expression_graph)
     ceres_test(expression_ref)
     ceres_test(code_generator)
+    ceres_test(eliminate_nops)
     set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
 endif()
diff --git a/internal/ceres/codegen/eliminate_nops.cc b/internal/ceres/codegen/eliminate_nops.cc
new file mode 100644
index 0000000..f94a873
--- /dev/null
+++ b/internal/ceres/codegen/eliminate_nops.cc
@@ -0,0 +1,55 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2019 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+
+#include "ceres/codegen/internal/eliminate_nops.h"
+
+#include "glog/logging.h"
+
+namespace ceres {
+namespace internal {
+
+OptimizationPassSummary EliminateNops(ExpressionGraph* graph) {
+  OptimizationPassSummary summary;
+
+  for (ExpressionId id = 0; id < graph->Size(); ++id) {
+    Expression& expr = graph->ExpressionForId(id);
+
+    if (expr.type() == ExpressionType::NOP) {
+      graph->Erase(id);
+      id--;
+      summary.expression_graph_changed = true;
+      summary.num_expressions_removed++;
+    }
+  }
+  return summary;
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/codegen/eliminate_nops_test.cc b/internal/ceres/codegen/eliminate_nops_test.cc
new file mode 100644
index 0000000..4b196b1
--- /dev/null
+++ b/internal/ceres/codegen/eliminate_nops_test.cc
@@ -0,0 +1,110 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2020 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+//
+#define CERES_CODEGEN
+
+#include "ceres/codegen/internal/eliminate_nops.h"
+
+#include "ceres/codegen/internal/code_generator.h"
+#include "ceres/codegen/internal/expression_graph.h"
+#include "ceres/codegen/internal/expression_ref.h"
+#include "gtest/gtest.h"
+
+namespace ceres {
+namespace internal {
+
+using T = ExpressionRef;
+
+TEST(EliminateNops, SimpleLinear) {
+  StartRecordingExpressions();
+  {
+    T a = T(0);
+    // The Expression default constructor creates a NOP.
+    AddExpressionToGraph(Expression());
+    AddExpressionToGraph(Expression());
+    T b = T(2);
+    AddExpressionToGraph(Expression());
+    MakeOutput(b, "residual[0]");
+    AddExpressionToGraph(Expression());
+  }
+  auto graph = StopRecordingExpressions();
+
+  StartRecordingExpressions();
+  {
+    T a = T(0);
+    T b = T(2);
+    MakeOutput(b, "residual[0]");
+  }
+  auto reference = StopRecordingExpressions();
+
+  auto summary = EliminateNops(&graph);
+  EXPECT_TRUE(summary.expression_graph_changed);
+  EXPECT_EQ(graph, reference);
+}
+
+TEST(EliminateNops, Branches) {
+  StartRecordingExpressions();
+  {
+    T a = T(0);
+    // The Expression default constructor creates a NOP.
+    AddExpressionToGraph(Expression());
+    AddExpressionToGraph(Expression());
+    T b = T(2);
+    CERES_IF(a < b) {
+      AddExpressionToGraph(Expression());
+      T c = T(3);
+    }
+    CERES_ELSE {
+      AddExpressionToGraph(Expression());
+      MakeOutput(b, "residual[0]");
+      AddExpressionToGraph(Expression());
+    }
+    CERES_ENDIF
+    AddExpressionToGraph(Expression());
+  }
+  auto graph = StopRecordingExpressions();
+
+  StartRecordingExpressions();
+  {
+    T a = T(0);
+    T b = T(2);
+    CERES_IF(a < b) { T c = T(3); }
+    CERES_ELSE { MakeOutput(b, "residual[0]"); }
+    CERES_ENDIF
+  }
+  auto reference = StopRecordingExpressions();
+
+  auto summary = EliminateNops(&graph);
+  EXPECT_TRUE(summary.expression_graph_changed);
+  EXPECT_EQ(graph, reference);
+}
+
+}  // namespace internal
+}  // namespace ceres
diff --git a/internal/ceres/codegen/optimize_expression_graph.cc b/internal/ceres/codegen/optimize_expression_graph.cc
new file mode 100644
index 0000000..5414dc6
--- /dev/null
+++ b/internal/ceres/codegen/optimize_expression_graph.cc
@@ -0,0 +1,60 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2019 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// 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: darius.rueckert@fau.de (Darius Rueckert)
+
+#include "ceres/codegen/internal/optimize_expression_graph.h"
+
+#include "ceres/codegen/internal/eliminate_nops.h"
+#include "glog/logging.h"
+namespace ceres {
+namespace internal {
+
+OptimizeExpressionGraphSummary OptimizeExpressionGraph(
+    const OptimizeExpressionGraphOptions& options, ExpressionGraph* graph) {
+  OptimizeExpressionGraphSummary summary;
+  summary.num_iterations = 0;
+  while (summary.num_iterations < options.max_num_iterations) {
+    summary.num_iterations++;
+    bool changed = false;
+
+    if (options.eliminate_nops) {
+      auto pass_summary = EliminateNops(graph);
+      changed |= pass_summary.expression_graph_changed;
+      summary.summaries.push_back(pass_summary);
+    }
+
+    if (!changed) {
+      break;
+    }
+  }
+  return summary;
+}
+
+}  // namespace internal
+}  // namespace ceres