// Copyright 2010-2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef OR_TOOLS_GLOP_PRIMAL_EDGE_NORMS_H_
#define OR_TOOLS_GLOP_PRIMAL_EDGE_NORMS_H_

#include "ortools/glop/basis_representation.h"
#include "ortools/glop/parameters.pb.h"
#include "ortools/glop/update_row.h"
#include "ortools/glop/variables_info.h"
#include "ortools/lp_data/lp_data.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/lp_data/scattered_vector.h"
#include "ortools/util/stats.h"

namespace operations_research {
namespace glop {

// This class maintains the primal edge squared norms (and other variants) to be
// used in the primal pricing step. Instead of computing the needed values from
// scractch at each iteration, it is more efficient to update them incrementally
// for each basis pivot applied to the simplex basis matrix B.
//
// Terminology:
// - To each non-basic column 'a' of a matrix A, we can associate an "edge" in
//   the kernel of A equal to 1.0 on the index of 'a' and '-B^{-1}.a' on the
//   basic variables.
// - 'B^{-1}.a' is called the "right inverse" of 'a'.
// - The entering edge is the edge we are following during a simplex step,
//   and we call "direction" the reverse of this edge restricted to the
//   basic variables, i.e. the right inverse of the entering column.
//
// Papers:
// - D. Goldfarb, J.K. Reid, "A practicable steepest-edge simplex algorithm"
//   Mathematical Programming 12 (1977) 361-371, North-Holland.
//   http://www.springerlink.com/content/g8335137n3j16934/
// - J.J. Forrest, D. Goldfarb, "Steepest-edge simplex algorithms for linear
//   programming", Mathematical Programming 57 (1992) 341-374, North-Holland.
//   http://www.springerlink.com/content/q645w3t2q229m248/
// - Ping-Qi Pan "A fast simplex algorithm for linear programming".
//   http://www.optimization-online.org/DB_FILE/2007/10/1805.pdf
// - Ping-Qi Pan, "Efficient nested pricing in the simplex algorithm",
//   http://www.optimization-online.org/DB_FILE/2007/10/1810.pdf
class PrimalEdgeNorms {
 public:
  // Takes references to the linear program data we need. Note that we assume
  // that the matrix will never change in our back, but the other references are
  // supposed to reflect the correct state.
  PrimalEdgeNorms(const CompactSparseMatrix& compact_matrix,
                  const VariablesInfo& variables_info,
                  const BasisFactorization& basis_factorization);

  // Clears, i.e. resets the object to its initial value. This will trigger
  // a recomputation for the next Get*() method call.
  void Clear();

  // If this is true, then the caller must re-factorize the basis before the
  // next call to GetEdgeSquaredNorms(). This is because the latter will
  // recompute the norms from scratch and therefore needs a hightened precision
  // and speed.
  bool NeedsBasisRefactorization() const;

  // Returns the primal edge squared norms. This is only valid if the caller
  // properly called UpdateBeforeBasisPivot() before each basis pivot, or if
  // this is the first call to this function after a Clear(). Note that only the
  // relevant columns are filled.
  const DenseRow& GetEdgeSquaredNorms();

  // Returns an approximation of the edges norms "devex".
  // This is only valid if the caller properly called UpdateBeforeBasisPivot()
  // before each basis pivot, or if this is the first call to this function
  // after a Clear().
  const DenseRow& GetDevexWeights();

  // Returns the L2 norms of all the columns of A.
  // Note that this is currently not cleared by Clear().
  const DenseRow& GetMatrixColumnNorms();

  // Compares the current entering edge norm with its precise version (using the
  // direction that wasn't avaible before) and triggers a full recomputation if
  // the precision is not good enough (see recompute_edges_norm_threshold in
  // GlopParameters). As a side effect, this replace the entering_col edge
  // norm with its precise version.
  void TestEnteringEdgeNormPrecision(ColIndex entering_col,
                                     const ScatteredColumn& direction);

  // Updates any internal data BEFORE the given simplex pivot is applied to B.
  // Note that no updates are needed in case of a bound flip.
  // The arguments are in order:
  // - The index of the entering non-basic column of A.
  // - The index in B of the leaving basic variable.
  // - The 'direction', i.e. the right inverse of the entering column.
  // - The update row (see UpdateRow), which will only be computed if needed.
  void UpdateBeforeBasisPivot(ColIndex entering_col, ColIndex leaving_col,
                              RowIndex leaving_row,
                              const ScatteredColumn& direction,
                              UpdateRow* update_row);

  // Sets the algorithm parameters.
  void SetParameters(const GlopParameters& parameters) {
    parameters_ = parameters;
  }

  // Returns a string with statistics about this class.
  std::string StatString() const { return stats_.StatString(); }

  // Deterministic time used by the scalar product computation of this class.
  double DeterministicTime() const {
    return DeterministicTimeForFpOperations(num_operations_);
  }

 private:
  // Statistics about this class.
  struct Stats : public StatsGroup {
    Stats()
        : StatsGroup("PrimalEdgeNorms"),
          direction_left_inverse_density("direction_left_inverse_density",
                                         this),
          direction_left_inverse_accuracy("direction_left_inverse_accuracy",
                                          this),
          edges_norm_accuracy("edges_norm_accuracy", this),
          lower_bounded_norms("lower_bounded_norms", this) {}
    RatioDistribution direction_left_inverse_density;
    DoubleDistribution direction_left_inverse_accuracy;
    DoubleDistribution edges_norm_accuracy;
    IntegerDistribution lower_bounded_norms;
  };

  // Recompute the matrix column L2 norms from scratch.
  void ComputeMatrixColumnNorms();

  // Recompute the edge squared L2 norms from scratch.
  void ComputeEdgeSquaredNorms();

  // Compute the left inverse of the direction.
  // The first argument is there for checking precision.
  void ComputeDirectionLeftInverse(ColIndex entering_col,
                                   const ScatteredColumn& direction);

  // Updates edges_squared_norm_ according to the given pivot.
  void UpdateEdgeSquaredNorms(ColIndex entering_col, ColIndex leaving_col,
                              RowIndex leaving_row,
                              const DenseColumn& direction,
                              const UpdateRow& update_row);

  // Resets all devex weights to 1.0 .
  void ResetDevexWeights();

  // Updates devex_weights_ according to the given pivot.
  void UpdateDevexWeights(ColIndex entering_col, ColIndex leaving_col,
                          RowIndex leaving_row, const DenseColumn& direction,
                          const UpdateRow& update_row);

  // Problem data that should be updated from outside.
  const CompactSparseMatrix& compact_matrix_;
  const VariablesInfo& variables_info_;
  const BasisFactorization& basis_factorization_;

  // Internal data.
  GlopParameters parameters_;
  Stats stats_;

  // Booleans to control what happens on the next ChooseEnteringColumn() call.
  bool must_refactorize_basis_;
  bool recompute_edge_squared_norms_;
  bool reset_devex_weights_;

  // Norm^2 of the edges of the relevant columns of A.
  DenseRow edge_squared_norms_;

  // Norm of all the columns of A.
  DenseRow matrix_column_norms_;

  // Approximation of edges norms "devex".
  // Denoted by vector 'w' in Pin Qi Pan (1810.pdf section 1.1.4)
  // At any time, devex_weights_ >= 1.0.
  DenseRow devex_weights_;

  // Tracks number of updates of the devex weights since we have to reset
  // them to 1.0 every now and then.
  int num_devex_updates_since_reset_;

  // Left inverse by B of the 'direction'. This is the transpose of 'v' in the
  // steepest edge paper. Its scalar product with a column 'a' of A gives the
  // value of the scalar product of the 'direction' with the right inverse of
  // 'a'.
  ScatteredRow direction_left_inverse_;

  // Used by DeterministicTime().
  int64 num_operations_;

  DISALLOW_COPY_AND_ASSIGN(PrimalEdgeNorms);
};

}  // namespace glop
}  // namespace operations_research

#endif  // OR_TOOLS_GLOP_PRIMAL_EDGE_NORMS_H_
