Browse Source

active set (not working)

Former-commit-id: 3f71cf13a8c6c16f9fce593bace50d011ef993a1
Alec Jacobson (jalec 11 years ago
parent
commit
68a5ff2bff

+ 202 - 0
include/igl/active_set.cpp

@@ -0,0 +1,202 @@
+#include "active_set.h"
+#include "min_quad_with_fixed.h"
+#include "slice.h"
+
+#include <iostream>
+#include <limits>
+#include <algorithm>
+
+template <
+  typename AT, 
+  typename DerivedB,
+  typename Derivedknown, 
+  typename DerivedY,
+  typename AeqT,
+  typename DerivedBeq,
+  typename AieqT,
+  typename DerivedBieq,
+  typename Derivedlx,
+  typename Derivedux,
+  typename DerivedZ
+  >
+IGL_INLINE igl::SolverStatus igl::active_set(
+  const Eigen::SparseMatrix<AT>& A,
+  const Eigen::PlainObjectBase<DerivedB> & B,
+  const Eigen::PlainObjectBase<Derivedknown> & known,
+  const Eigen::PlainObjectBase<DerivedY> & Y,
+  const Eigen::SparseMatrix<AeqT>& Aeq,
+  const Eigen::PlainObjectBase<DerivedBeq> & Beq,
+  const Eigen::SparseMatrix<AieqT>& Aieq,
+  const Eigen::PlainObjectBase<DerivedBieq> & Bieq,
+  const Eigen::PlainObjectBase<Derivedlx> & lx,
+  const Eigen::PlainObjectBase<Derivedux> & ux,
+  const igl::active_set_params & params,
+  Eigen::PlainObjectBase<DerivedZ> & Z
+  )
+{
+  using namespace igl;
+  using namespace Eigen;
+  using namespace std;
+  // Linear inequality constraints are not supported yet
+  assert(Aieq.size() == 0);
+  assert(Bieq.size() == 0);
+  SolverStatus ret = SOLVER_STATUS_ERROR;
+  const int n = A.rows();
+  assert(n == A.cols());
+  // Discard const qualifiers
+  //if(B.size() == 0)
+  //{
+  //  B = Eigen::PlainObjectBase<DerivedB>::Zero(n,1);
+  //}
+  assert(n == B.rows());
+  assert(B.cols() == 1);
+  assert(Y.cols() == 1);
+  assert((Aeq.size() == 0 && Beq.size() == 0) || Aeq.cols() == n);
+  assert((Aeq.size() == 0 && Beq.size() == 0) || Aeq.rows() == Beq.rows());
+  assert((Aeq.size() == 0 && Beq.size() == 0) || Beq.cols() == 1);
+  assert((Aieq.size() == 0 && Bieq.size() == 0) || Aieq.cols() == n);
+  assert((Aieq.size() == 0 && Bieq.size() == 0) || Aieq.rows() == Bieq.rows());
+  assert((Aieq.size() == 0 && Bieq.size() == 0) || Bieq.cols() == 1);
+  // Discard const qualifiers
+  //if(lx.size() == 0)
+  //{
+  //  lx = Eigen::PlainObjectBase<Derivedlx>::Constant(
+  //    n,1,numeric_limits<typename Derivedlx::Scalar>::min());
+  //}
+  //if(ux.size() == 0)
+  //{
+  //  ux = Eigen::PlainObjectBase<Derivedux>::Constant(
+  //    n,1,numeric_limits<typename Derivedux::Scalar>::max());
+  //}
+  assert(lx.rows() == n);
+  assert(ux.rows() == n);
+  assert(ux.cols() == 1);
+  assert(lx.cols() == 1);
+  assert((ux.array()-lx.array()).minCoeff() > 0);
+  if(Z.size() != 0)
+  {
+    // Initial guess should have correct size
+    assert(Z.rows() == n);
+    assert(Z.cols() == 1);
+  }
+
+  // Initialize active sets
+  Matrix<bool,Dynamic,1> as_lx(n,1);
+  Matrix<bool,Dynamic,1> as_ux(n,1);
+  Matrix<bool,Dynamic,1> as_ieq(Aieq.rows(),1);
+
+  int iter = 0;
+  while(true)
+  {
+    // FIND BREACHES OF CONSTRAINTS
+    if(Z.size() > 0)
+    {
+      for(int z = 0;z < n;z++)
+      {
+        if(Z(z) < lx(z))
+        {
+          as_lx(z) = true;
+        }
+        if(Z(z) > ux(z))
+        {
+          as_ux(z) = true;
+        }
+      }
+    }
+
+    const int as_lx_count = count(as_lx.data(),as_lx.data()+n,true);
+    const int as_ux_count = count(as_ux.data(),as_ux.data()+n,true);
+    // PREPARE FIXED VALUES
+    Eigen::PlainObjectBase<Derivedknown> known_i;
+    known_i.resize((int)known.size() + as_lx_count + as_ux_count,1);
+    Eigen::PlainObjectBase<DerivedY> Y_i;
+    Y_i.resize((int)known.size() + as_lx_count + as_ux_count,1);
+    {
+      known_i.block(0,0,known.rows(),known.cols()) = known;
+      Y_i.block(0,0,Y.rows(),Y.cols()) = Y;
+      int k = known.size();
+      // Then all lx
+      for(int z = 0;z < n;z++)
+      {
+        if(as_lx(z))
+        {
+          known_i(k) = z;
+          Y_i(k) = lx(z);
+          k++;
+        }
+      }
+      // Finally all ux
+      for(int z = 0;z < n;z++)
+      {
+        if(as_ux(z))
+        {
+          known_i(k) = z;
+          Y_i(k) = ux(z);
+          k++;
+        }
+      }
+      assert(k==Y_i.size());
+      assert(k==known_i.size());
+    }
+
+    min_quad_with_fixed_data<AT> data;
+    if(!min_quad_with_fixed_precompute(A,known_i,Aeq,params.Auu_pd,data))
+    {
+      cerr<<"Error: min_quad_with_fixed precomputation failed."<<endl;
+      ret = SOLVER_STATUS_ERROR;
+      break;
+    }
+    if(!min_quad_with_fixed_solve(data,B,Y_i,Beq,Z))
+    {
+      cerr<<"Error: min_quad_with_fixed solve failed."<<endl;
+      ret = SOLVER_STATUS_ERROR;
+      break;
+    }
+
+    // Compute Lagrange multiplier values for known_i
+    // This needs to be adjusted slightly if A is not symmetric
+    assert(data.Auu_sym);
+    SparseMatrix<AT> Ak;
+    // Slow
+    slice(A,known_i,1,Ak);
+    Eigen::PlainObjectBase<DerivedB> Bk;
+    slice(B,known_i,Bk);
+    MatrixXd Lambda_known_i = -(Ak*Z + 0.5*Bk);
+    // reverse the lambda values for lx
+    Lambda_known_i.block(known.size(),0,as_lx_count,1) = 
+      (-1*Lambda_known_i.block(known.size(),0,as_lx_count,1)).eval();
+    
+    // Remove from active set
+    for(int z = 0;z<as_lx_count;z++)
+    {
+      if(Lambda_known_i(known.size() + z) < params.inactive_threshold)
+      {
+        as_lx(known_i(z)) = false;
+      }
+    }
+    for(int z = 0;z<as_ux_count;z++)
+    {
+      if(Lambda_known_i(known.size() + as_lx_count + z) < 
+        params.inactive_threshold)
+      {
+        as_ux(known_i(z)) = false;
+      }
+    }
+
+    iter++;
+    if(params.max_iter>0 && iter>=params.max_iter)
+    {
+      ret = SOLVER_STATUS_MAX_ITER;
+      break;
+    }
+  }
+
+finish:
+  return ret;
+}
+
+
+#ifndef IGL_HEADER_ONLY
+// Explicit template specialization
+template igl::SolverStatus igl::active_set<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, igl::active_set_params const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+#endif

+ 98 - 0
include/igl/active_set.h

@@ -0,0 +1,98 @@
+#ifndef ACTIVE_SET_H
+#define ACTIVE_SET_H
+
+#include "igl_inline.h"
+#include <Eigen/Core>
+#include <Eigen/Sparse>
+
+namespace igl
+{
+  enum SolverStatus
+  {
+    // Good
+    SOLVER_STATUS_CONVERGED = 0,
+    // OK
+    SOLVER_STATUS_MAX_ITER = 1,
+    // Bad
+    SOLVER_STATUS_ERROR = 2,
+    NUM_SOLVER_STATUSES = 3,
+  };
+  struct active_set_params;
+  // ACTIVE_SET Minimize quadratic energy Z'*A*Z + Z'*B + C with constraints
+  // that Z(known) = Y, optionally also subject to the constraints Aeq*Z = Beq,
+  // and further optionally subject to the linear inequality constraints that
+  // Aieq*Z <= Bieq and constant inequality constraints lx <= x <= ux
+  //
+  // Templates:
+  // Inputs:
+  //   A  n by n matrix of quadratic coefficients
+  //   B  n by 1 column of linear coefficients
+  //   known  list of indices to known rows in Z
+  //   Y  list of fixed values corresponding to known rows in Z
+  //   Aeq  meq by n list of linear equality constraint coefficients
+  //   Beq  meq by 1 list of linear equality constraint constant values
+  //   Aieq  mieq by n list of linear equality constraint coefficients
+  //   Bieq  mieq by 1 list of linear equality constraint constant values
+  //   lx  n by 1 list of lower bounds [] implies -Inf
+  //   ux  n by 1 list of upper bounds [] implies Inf
+  //   params  struct of additional parameters (see below)
+  // Outputs:
+  //   Z  n by 1 list of solution values
+  // Returns true on success, false on error
+  //
+  // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2
+  // secs, igl/min_quad_with_fixed.h 7.1 secs
+  //
+
+  template <
+    typename AT, 
+    typename DerivedB,
+    typename Derivedknown, 
+    typename DerivedY,
+    typename AeqT,
+    typename DerivedBeq,
+    typename AieqT,
+    typename DerivedBieq,
+    typename Derivedlx,
+    typename Derivedux,
+    typename DerivedZ
+    >
+  IGL_INLINE igl::SolverStatus active_set(
+    const Eigen::SparseMatrix<AT>& A,
+    const Eigen::PlainObjectBase<DerivedB> & B,
+    const Eigen::PlainObjectBase<Derivedknown> & known,
+    const Eigen::PlainObjectBase<DerivedY> & Y,
+    const Eigen::SparseMatrix<AeqT>& Aeq,
+    const Eigen::PlainObjectBase<DerivedBeq> & Beq,
+    const Eigen::SparseMatrix<AieqT>& Aieq,
+    const Eigen::PlainObjectBase<DerivedBieq> & Bieq,
+    const Eigen::PlainObjectBase<Derivedlx> & lx,
+    const Eigen::PlainObjectBase<Derivedux> & ux,
+    const igl::active_set_params & params,
+    Eigen::PlainObjectBase<DerivedZ> & Z
+    );
+};
+
+#include "EPS.h"
+struct igl::active_set_params
+{
+  // Input parameters for active_set:
+  //   Auu_pd  whethter Auu is positive definite {false}
+  //   max_iter  Maximum number of iterations ({0} = Infinity)
+  //   inactive_threshold  Threshold on Lagrange multiplier values to determine
+  //     whether to keep constraints active {EPS}
+  bool Auu_pd;
+  int max_iter;
+  double inactive_threshold;
+  active_set_params():
+    Auu_pd(false),
+    max_iter(-1),
+    inactive_threshold(igl::DOUBLE_EPS)
+    {};
+};
+
+#ifdef IGL_HEADER_ONLY
+#  include "active_set.cpp"
+#endif
+
+#endif

+ 7 - 1
include/igl/matlab/MatlabWorkspace.cpp

@@ -249,7 +249,7 @@ IGL_INLINE bool igl::MatlabWorkspace::find(
   // Handle vectors
   if(DerivedM::IsVectorAtCompileTime)
   {
-    assert(m==1 || n==1);
+    assert(m==1 || n==1 || (m==0 && n==0));
     M.resize(m*n);
   }else
   {
@@ -276,6 +276,12 @@ IGL_INLINE bool igl::MatlabWorkspace::find(
   }
   assert(i<=(int)data.size());
   mxArray * mx_data = data[i];
+  // Handle boring case where matrix is actually an empty dense matrix
+  if(mxGetNumberOfElements(mx_data) == 0)
+  {
+    M.resize(0,0);
+    return true;
+  }
   assert(mxIsSparse(mx_data));
   assert(mxGetNumberOfDimensions(mx_data) == 2);
   //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;

+ 17 - 10
include/igl/min_quad_with_fixed.cpp

@@ -19,13 +19,13 @@
 #include <cstdio>
 #include <iostream>
 
-template <typename T>
+template <typename T, typename Derivedknown>
 IGL_INLINE bool igl::min_quad_with_fixed_precompute(
   const Eigen::SparseMatrix<T>& A,
-  const Eigen::Matrix<int,Eigen::Dynamic,1> & known,
+  const Eigen::PlainObjectBase<Derivedknown> & known,
   const Eigen::SparseMatrix<T>& Aeq,
   const bool pd,
-  igl::min_quad_with_fixed_data<T> & data
+  min_quad_with_fixed_data<T> & data
   )
 {
   using namespace Eigen;
@@ -161,7 +161,7 @@ IGL_INLINE bool igl::min_quad_with_fixed_precompute(
   if(data.Auu_pd && neq == 0)
   {
     data.llt.compute(Auu);
-    switch(data.ldlt.info())
+    switch(data.llt.info())
     {
       case Eigen::Success:
         break;
@@ -227,12 +227,17 @@ IGL_INLINE bool igl::min_quad_with_fixed_precompute(
 }
 
 
-template <typename T, typename DerivedY, typename DerivedZ>
+template <
+  typename T,
+  typename DerivedB, 
+  typename DerivedY,
+  typename DerivedBeq, 
+  typename DerivedZ>
 IGL_INLINE bool igl::min_quad_with_fixed_solve(
-  const igl::min_quad_with_fixed_data<T> & data,
-  const Eigen::Matrix<T,Eigen::Dynamic,1> & B,
+  const min_quad_with_fixed_data<T> & data,
+  const Eigen::PlainObjectBase<DerivedB> & B,
   const Eigen::PlainObjectBase<DerivedY> & Y,
-  const Eigen::Matrix<T,Eigen::Dynamic,1> & Beq,
+  const Eigen::PlainObjectBase<DerivedBeq> & Beq,
   Eigen::PlainObjectBase<DerivedZ> & Z)
 {
   using namespace std;
@@ -244,6 +249,8 @@ IGL_INLINE bool igl::min_quad_with_fixed_solve(
   }
   // number of columns to solve
   int cols = Y.cols();
+  assert(B.cols() == 1);
+  assert(Beq.size() == 0 || Beq.cols() == 1);
 
   // number of lagrange multipliers aka linear equality constraints
   int neq = data.lagrange.size();
@@ -315,7 +322,7 @@ IGL_INLINE bool igl::min_quad_with_fixed_solve(
 
 #ifndef IGL_HEADER_ONLY
 // Explicit template specialization
-template bool igl::min_quad_with_fixed_solve<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::min_quad_with_fixed_data<double> const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::Matrix<double, -1, 1, 0, -1, 1> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
-template bool igl::min_quad_with_fixed_precompute<double>(Eigen::SparseMatrix<double, 0, int> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int> const&, bool, igl::min_quad_with_fixed_data<double>&);
+template bool igl::min_quad_with_fixed_solve<double, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >(igl::min_quad_with_fixed_data<double> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+template bool igl::min_quad_with_fixed_precompute<double, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> > const&, Eigen::SparseMatrix<double, 0, int> const&, bool, igl::min_quad_with_fixed_data<double>&);
 #endif
 

+ 18 - 10
include/igl/min_quad_with_fixed.h

@@ -23,21 +23,22 @@ namespace igl
   //   T  should be a eigen matrix primitive type like int or double
   // Inputs:
   //   A  n by n matrix of quadratic coefficients
-  //   B  n by 1 column of linear coefficients
   //   known list of indices to known rows in Z
   //   Y  list of fixed values corresponding to known rows in Z
-  //   Optional:
-  //     Aeq  m by n list of linear equality constraint coefficients
-  //     Beq  m by 1 list of linear equality constraint constant values
-  //     pd flag specifying whether A(unknown,unknown) is positive definite
+  //   Aeq  m by n list of linear equality constraint coefficients
+  //   pd flag specifying whether A(unknown,unknown) is positive definite
   // Outputs:
   //   data  factorization struct with all necessary information to solve
   //     using min_quad_with_fixed_solve
   // Returns true on success, false on error
-  template <typename T>
+  //
+  // Benchmark: For a harmonic solve on a mesh with 325K facets, matlab 2.2
+  // secs, igl/min_quad_with_fixed.h 7.1 secs
+  //
+  template <typename T, typename Derivedknown>
   IGL_INLINE bool min_quad_with_fixed_precompute(
     const Eigen::SparseMatrix<T>& A,
-    const Eigen::Matrix<int,Eigen::Dynamic,1> & known,
+    const Eigen::PlainObjectBase<Derivedknown> & known,
     const Eigen::SparseMatrix<T>& Aeq,
     const bool pd,
     min_quad_with_fixed_data<T> & data
@@ -51,15 +52,22 @@ namespace igl
   //   DerivedZ  type of Z (e.g. derived from VectorXd or MatrixXd)
   // Inputs:
   //   data  factorization struct with all necessary precomputation to solve
+  //   B  n by 1 column of linear coefficients
+  //   Beq  m by 1 list of linear equality constraint constant values
   // Outputs:
   //   Z  n by cols solution
   // Returns true on success, false on error
-  template <typename T,typename DerivedY,typename DerivedZ>
+  template <
+    typename T,
+    typename DerivedB, 
+    typename DerivedY,
+    typename DerivedBeq, 
+    typename DerivedZ>
   IGL_INLINE bool min_quad_with_fixed_solve(
     const min_quad_with_fixed_data<T> & data,
-    const Eigen::Matrix<T,Eigen::Dynamic,1> & B,
+    const Eigen::PlainObjectBase<DerivedB> & B,
     const Eigen::PlainObjectBase<DerivedY> & Y,
-    const Eigen::Matrix<T,Eigen::Dynamic,1> & Beq,
+    const Eigen::PlainObjectBase<DerivedBeq> & Beq,
     Eigen::PlainObjectBase<DerivedZ> & Z);
 }
 

+ 25 - 0
include/igl/slice.cpp

@@ -1,4 +1,5 @@
 #include "slice.h"
+#include "colon.h"
 
 #include <vector>
 
@@ -68,6 +69,29 @@ IGL_INLINE void igl::slice(
   Y = Eigen::SparseMatrix<T>(dyn_Y);
 }
 
+template <typename T>
+IGL_INLINE void igl::slice(
+  const Eigen::SparseMatrix<T>& X,
+  const Eigen::Matrix<int,Eigen::Dynamic,1> & R,
+  const int dim,
+  Eigen::SparseMatrix<T>& Y)
+{
+  
+  Eigen::VectorXi C;
+  switch(dim)
+  {
+    case 1:
+      igl::colon(0,X.cols()-1,C);
+      return slice(X,R,C,Y);
+    case 2:
+      igl::colon(0,X.rows()-1,C);
+      return slice(X,C,R,Y);
+    default:
+      assert(false);
+      return;
+  }
+}
+
 template <typename DerivedX>
 IGL_INLINE void igl::slice(
   const Eigen::PlainObjectBase<DerivedX> & X,
@@ -132,4 +156,5 @@ template void igl::slice<Eigen::Matrix<float, -1, -1, 0, -1, -1> >(Eigen::PlainO
 // generated by autoexplicit.sh
 template void igl::slice<double>(Eigen::SparseMatrix<double, 0, int> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::SparseMatrix<double, 0, int>&);
 template void igl::slice<Eigen::Matrix<double, -1, 1, 0, -1, 1> >(Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
+template void igl::slice<double>(Eigen::SparseMatrix<double, 0, int> const&, Eigen::Matrix<int, -1, 1, 0, -1, 1> const&, int, Eigen::SparseMatrix<double, 0, int>&);
 #endif

+ 10 - 0
include/igl/slice.h

@@ -23,6 +23,16 @@ namespace igl
     const Eigen::Matrix<int,Eigen::Dynamic,1> & R,
     const Eigen::Matrix<int,Eigen::Dynamic,1> & C,
     Eigen::SparseMatrix<T>& Y);
+  // Wrapper to only slice in one direction
+  //
+  // Inputs:
+  //   dim  dimension to slice in 1 or 2, dim=1 --> X(R,:), dim=2 --> X(:,R)
+  template <typename T>
+  IGL_INLINE void slice(
+    const Eigen::SparseMatrix<T>& X,
+    const Eigen::Matrix<int,Eigen::Dynamic,1> & R,
+    const int dim,
+    Eigen::SparseMatrix<T>& Y);
 
   template <typename DerivedX>
   IGL_INLINE void slice(

+ 8 - 1
matlab-to-eigen.html

@@ -216,9 +216,16 @@
         <td><pre><code>// build std::vector&ltEigen::Triplet&gt; IJV
 A.setFromTriplets(IJV);
         </code></pre></td>
-        <td>IJV and A should not be empty!</td>
+        <td>IJV and A should not be empty! (this might be fixed)</td>
       </tr>
 
+
+      <tr class=d0>
+        <td><pre><code>A = min(A,c);</code></pre></td>
+        <td><pre><code>C.array() = A.array().min(c);
+        </code></pre></td>
+        <td>Coefficient-wise minimum of matrix and scalar (or matching size matrix)</td>
+      </tr>
       <!-- Insert rows for each command pair -->
 
       <!-- Leave this here for copy and pasting