changeset 25696:3b6691ff0f59

Fix eigs for generalized problems with user defined function (bug #54167). * eigs-base.cc (EigsRealSymmetricFunc, EigsRealNonSymmetricFunc, EigsComplexNonSymmetricFunc): Add B as input argument and manage mode = 1 and mode = 3. * eigs-base.h: Declare EigsRealSymmetricFunc, EigsRealNonSymmetricFunc, and EigsComplexNonSymmetricFunc template functions. * __eigs__.cc: Call EigsRealSymmetricFunc, EigsRealNonSymmetricFunc, and EigsComplexNonSymmetricFunc with the new input argument B. * scripts/sparse/eigs.m: Add BIST tests.
author Marco Caliari <marco.caliari@univr.it>
date Wed, 27 Jun 2018 08:36:55 +0200
parents 038fb01854a0
children 91f0416c2ba7
files libinterp/dldfcn/__eigs__.cc liboctave/numeric/eigs-base.cc liboctave/numeric/eigs-base.h scripts/sparse/eigs.m
diffstat 4 files changed, 630 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__eigs__.cc	Thu Jul 19 22:43:17 2018 +0200
+++ b/libinterp/dldfcn/__eigs__.cc	Wed Jun 27 08:36:55 2018 +0200
@@ -182,6 +182,7 @@
   bool sym_tested = false;
   bool cholB = false;
   bool a_is_sparse = false;
+  bool b_is_sparse = false;
   ColumnVector permB;
   int arg_offset = 0;
   double tol = std::numeric_limits<double>::epsilon ();
@@ -265,6 +266,13 @@
       if (args(1+arg_offset).iscomplex ())
         {
           b_arg = 1+arg_offset;
+          if (args(b_arg).issparse ())
+            {
+              bscm = (args(b_arg).sparse_complex_matrix_value ());
+              b_is_sparse = true;
+            }
+          else
+            bcm = (args(b_arg).complex_matrix_value ());
           have_b = true;
           b_is_complex = true;
           arg_offset++;
@@ -272,6 +280,13 @@
       else
         {
           b_arg = 1+arg_offset;
+          if (args(b_arg).issparse ())
+            {
+              bsmm = (args(b_arg).sparse_matrix_value ());
+              b_is_sparse = true;
+            }
+          else
+            bmm = (args(b_arg).matrix_value ());
           have_b = true;
           arg_offset++;
         }
@@ -374,14 +389,14 @@
     {
       if (a_is_complex || b_is_complex)
         {
-          if (a_is_sparse)
+          if (b_is_sparse)
             bscm = args(b_arg).sparse_complex_matrix_value ();
           else
             bcm = args(b_arg).complex_matrix_value ();
         }
       else
         {
-          if (a_is_sparse)
+          if (b_is_sparse)
             bsmm = args(b_arg).sparse_matrix_value ();
           else
             bmm = args(b_arg).matrix_value ();
@@ -400,10 +415,18 @@
       ComplexColumnVector eig_val;
 
       if (have_a_fun)
-        nconv = EigsComplexNonSymmetricFunc
-                (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
-                 eig_val, cresid, octave_stdout, tol, (nargout > 1), cholB,
-                 disp, maxit);
+        {
+          if (b_is_sparse)
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bscm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+          else
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bcm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+        }
       else if (have_sigma)
         {
           if (a_is_sparse)
@@ -443,10 +466,18 @@
       ComplexColumnVector eig_val;
 
       if (have_a_fun)
-        nconv = EigsComplexNonSymmetricFunc
-                (eigs_complex_func, n, typ,  sigma, k, p, info, eig_vec,
-                 eig_val, cresid, octave_stdout, tol, (nargout > 1), cholB,
-                 disp, maxit);
+        {
+          if (b_is_sparse)
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bscm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+          else
+            nconv = EigsComplexNonSymmetricFunc
+              (eigs_complex_func, n, typ, sigma, k, p, info, eig_vec,
+               eig_val, bcm, permB, cresid, octave_stdout, tol,
+               (nargout > 1), cholB, disp, maxit);
+        }
       else
         {
           if (a_is_sparse)
@@ -474,10 +505,18 @@
           ColumnVector eig_val;
 
           if (have_a_fun)
-            nconv = EigsRealSymmetricFunc
-                    (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                     eig_val, resid, octave_stdout, tol, (nargout > 1),
-                     cholB, disp, maxit);
+            {
+              if (b_is_sparse)
+                nconv = EigsRealSymmetricFunc
+                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                        eig_val, bsmm, permB, resid, octave_stdout, tol,
+                        (nargout > 1), cholB, disp, maxit);
+              else
+                nconv = EigsRealSymmetricFunc
+                       (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                        eig_val, bmm, permB, resid, octave_stdout, tol,
+                        (nargout > 1), cholB, disp, maxit);
+            }
           else if (have_sigma)
             {
               if (a_is_sparse)
@@ -516,10 +555,18 @@
           ComplexColumnVector eig_val;
 
           if (have_a_fun)
-            nconv = EigsRealNonSymmetricFunc
-                    (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
-                     eig_val, resid, octave_stdout, tol, (nargout > 1),
-                     cholB, disp, maxit);
+            {
+              if (b_is_sparse)
+                nconv = EigsRealNonSymmetricFunc
+                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                         eig_val, bsmm, permB, resid, octave_stdout, tol,
+                         (nargout > 1), cholB, disp, maxit);
+              else
+                nconv = EigsRealNonSymmetricFunc
+                        (eigs_func, n, typ, sigmar, k, p, info, eig_vec,
+                         eig_val, bmm, permB, resid, octave_stdout, tol,
+                         (nargout > 1), cholB, disp, maxit);
+            }
           else if (have_sigma)
             {
               if (a_is_sparse)
--- a/liboctave/numeric/eigs-base.cc	Thu Jul 19 22:43:17 2018 +0200
+++ b/liboctave/numeric/eigs-base.cc	Wed Jun 27 08:36:55 2018 +0200
@@ -1202,23 +1202,29 @@
   return ip(4);
 }
 
+template <typename M>
 octave_idx_type
 EigsRealSymmetricFunc (EigsFunc fun, octave_idx_type n_arg,
                        const std::string& _typ, double sigma,
                        octave_idx_type k_arg, octave_idx_type p_arg,
                        octave_idx_type& info, Matrix& eig_vec,
-                       ColumnVector& eig_val, ColumnVector& resid,
+                       ColumnVector& eig_val, const M& _b,
+                       ColumnVector& permB, ColumnVector& resid,
                        std::ostream& os, double tol, bool rvec,
-                       bool /* cholB */, int disp, int maxit)
+                       bool cholB, int disp, int maxit)
 {
   F77_INT n = octave::to_f77_int (n_arg);
   F77_INT k = octave::to_f77_int (k_arg);
   F77_INT p = octave::to_f77_int (p_arg);
+  M b(_b);
   std::string typ (_typ);
   bool have_sigma = (sigma ? true : false);
+  bool have_b = ! b.isempty ();
+  bool note3 = false;
   char bmat = 'I';
   F77_INT mode = 1;
   int err = 0;
+  M bt;
 
   if (resid.isempty ())
     {
@@ -1254,6 +1260,23 @@
     (*current_liboctave_error_handler)
       ("eigs: opts.p must be greater than k and less than or equal to n");
 
+  if (have_b && cholB && ! permB.isempty ())
+    {
+      // Check the we really have a permutation vector
+      if (permB.numel () != n)
+        (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+
+      Array<bool> checked (dim_vector (n, 1), false);
+      for (F77_INT i = 0; i < n; i++)
+        {
+          octave_idx_type bidx = static_cast<octave_idx_type> (permB(i));
+
+          if (checked(bidx) || bidx < 0 || bidx >= n
+              || octave::math::x_nint (bidx) != bidx)
+            (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+        }
+    }
+
   if (! have_sigma)
     {
       if (typ != "LM" && typ != "SM" && typ != "LA" && typ != "SA"
@@ -1265,19 +1288,53 @@
         (*current_liboctave_error_handler)
           ("eigs: invalid sigma value for real symmetric problem");
 
+      if (typ != "SM" && have_b)
+        note3 = true;
+
       if (typ == "SM")
         {
           typ = "LM";
           sigma = 0.;
           mode = 3;
+          if (have_b)
+            bmat = 'G';
         }
     }
   else if (! std::abs (sigma))
-    typ = "SM";
+    {
+      typ = "SM";
+      if (have_b)
+        bmat = 'G';
+    }
   else
     {
       typ = "LM";
       mode = 3;
+      if (have_b)
+        bmat = 'G';
+    }
+
+  if (mode == 1 && have_b)
+    {
+      // See Note 3 dsaupd
+      note3 = true;
+      if (cholB)
+        {
+          bt = b;
+          b = b.transpose ();
+          if (permB.isempty ())
+            {
+              permB = ColumnVector (n);
+              for (F77_INT i = 0; i < n; i++)
+                permB(i) = i;
+            }
+        }
+      else
+        {
+          if (! make_cholb (b, bt, permB))
+            (*current_liboctave_error_handler)
+              ("eigs: The matrix B is not positive definite");
+        }
     }
 
   Array<F77_INT> ip (dim_vector (11, 1));
@@ -1352,20 +1409,85 @@
 
       if (ido == -1 || ido == 1 || ido == 2)
         {
-          double *ip2 = workd + iptr(0) - 1;
-          ColumnVector x(n);
-
-          for (F77_INT i = 0; i < n; i++)
-            x(i) = *ip2++;
-
-          ColumnVector y = fun (x, err);
-
-          if (err)
-            return false;
-
-          ip2 = workd + iptr(1) - 1;
-          for (F77_INT i = 0; i < n; i++)
-            *ip2++ = y(i);
+          if (have_b)
+            {
+              if (mode == 1) // regular mode with factorized B
+                {
+                  Matrix mtmp (n,1);
+                  for (F77_INT i = 0; i < n; i++)
+                    mtmp(i,0) = workd[i + iptr(0) - 1];
+
+                  mtmp = utsolve (bt, permB, mtmp);
+                  ColumnVector y = fun (mtmp, err);
+
+                  if (err)
+                    return false;
+
+                  mtmp = ltsolve (b, permB, y);
+
+                  for (F77_INT i = 0; i < n; i++)
+                    workd[i+iptr(1)-1] = mtmp(i,0);
+                }
+              else // shift-invert mode
+                {
+                  if (ido == -1)
+                    {
+                      OCTAVE_LOCAL_BUFFER (double, dtmp, n);
+
+                      vector_product (b, workd+iptr(0)-1, dtmp);
+
+                      ColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = dtmp[i];
+
+                      ColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      double *ip2 = workd + iptr(1) - 1;
+                      for (F77_INT i = 0; i < n; i++)
+                        ip2[i] = y(i);
+                    }
+                  else if (ido == 2)
+                    vector_product (b, workd+iptr(0)-1, workd+iptr(1)-1);
+                  else
+                    {
+                      double *ip2 = workd+iptr(2)-1;
+                      ColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = *ip2++;
+
+                      ColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      ip2 = workd + iptr(1) - 1;
+                      for (F77_INT i = 0; i < n; i++)
+                        *ip2++ = y(i);
+                     }
+                }
+            }
+          else
+            {
+              double *ip2 = workd + iptr(0) - 1;
+              ColumnVector x(n);
+
+              for (F77_INT i = 0; i < n; i++)
+                x(i) = *ip2++;
+
+              ColumnVector y = fun (x, err);
+
+              if (err)
+                return false;
+
+              ip2 = workd + iptr(1) - 1;
+              for (F77_INT i = 0; i < n; i++)
+                *ip2++ = y(i);
+            }
         }
       else
         {
@@ -1408,7 +1530,7 @@
       for (F77_INT i = ip(4); i < k; i++)
         d[i] = octave::numeric_limits<double>::NaN ();
       F77_INT k2 = ip(4) / 2;
-      if (typ != "SM" && typ != "BE")
+      if (mode == 3 || (mode == 1 && typ != "SM" && typ != "BE"))
         {
           for (F77_INT i = 0; i < k2; i++)
             {
@@ -1426,7 +1548,7 @@
               for (F77_INT j = 0; j < n; j++)
                 z[off1 + j] = octave::numeric_limits<double>::NaN ();
             }
-          if (typ != "SM" && typ != "BE")
+          if (mode == 3 || (mode == 1 && typ != "SM" && typ != "BE"))
             {
               OCTAVE_LOCAL_BUFFER (double, dtmp, n);
 
@@ -1448,7 +1570,9 @@
                     z[off2 + j] = dtmp[j];
                 }
             }
-        }
+          if (note3)
+            eig_vec = utsolve (bt, permB, eig_vec);
+         }
     }
   else
     (*current_liboctave_error_handler) ("eigs: error %d in dseupd", info2);
@@ -2179,24 +2303,30 @@
   return ip(4);
 }
 
+template <typename M>
 octave_idx_type
 EigsRealNonSymmetricFunc (EigsFunc fun, octave_idx_type n_arg,
                           const std::string& _typ, double sigmar,
                           octave_idx_type k_arg, octave_idx_type p_arg,
                           octave_idx_type& info, ComplexMatrix& eig_vec,
-                          ComplexColumnVector& eig_val, ColumnVector& resid,
+                          ComplexColumnVector& eig_val, const M& _b,
+                          ColumnVector& permB, ColumnVector& resid,
                           std::ostream& os, double tol, bool rvec,
-                          bool /* cholB */, int disp, int maxit)
+                          bool cholB, int disp, int maxit)
 {
   F77_INT n = octave::to_f77_int (n_arg);
   F77_INT k = octave::to_f77_int (k_arg);
   F77_INT p = octave::to_f77_int (p_arg);
+  M b(_b);
   std::string typ (_typ);
   bool have_sigma = (sigmar ? true : false);
-  char bmat = 'I';
   double sigmai = 0.;
   F77_INT mode = 1;
+  bool have_b = ! b.isempty ();
+  bool note3 = false;
+  char bmat = 'I';
   int err = 0;
+  M bt;
 
   if (resid.isempty ())
     {
@@ -2232,6 +2362,23 @@
     (*current_liboctave_error_handler)
       ("eigs: opts.p must be greater than k and less than or equal to n");
 
+  if (have_b && cholB && ! permB.isempty ())
+    {
+      // Check the we really have a permutation vector
+      if (permB.numel () != n)
+        (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+
+      Array<bool> checked (dim_vector (n, 1), false);
+      for (F77_INT i = 0; i < n; i++)
+        {
+          octave_idx_type bidx = static_cast<octave_idx_type> (permB(i));
+
+          if (checked(bidx) || bidx < 0 || bidx >= n
+              || octave::math::x_nint (bidx) != bidx)
+            (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+        }
+    }
+
   if (! have_sigma)
     {
       if (typ != "LM" && typ != "SM" && typ != "LA" && typ != "SA"
@@ -2243,19 +2390,53 @@
         (*current_liboctave_error_handler)
           ("eigs: invalid sigma value for unsymmetric problem");
 
+      if (typ != "SM" && have_b)
+        note3 = true;
+
       if (typ == "SM")
         {
           typ = "LM";
           sigmar = 0.;
           mode = 3;
+          if (have_b)
+            bmat = 'G';
         }
+   }
+  else if (! std::abs (sigmar))
+    {
+      typ = "SM";
+      if (have_b)
+        bmat = 'G';
     }
-  else if (! std::abs (sigmar))
-    typ = "SM";
   else
     {
       typ = "LM";
       mode = 3;
+      if (have_b)
+        bmat = 'G';
+    }
+
+  if (mode == 1 && have_b)
+    {
+      // See Note 3 dsaupd
+      note3 = true;
+      if (cholB)
+        {
+          bt = b;
+          b = b.transpose ();
+          if (permB.isempty ())
+            {
+              permB = ColumnVector (n);
+              for (F77_INT i = 0; i < n; i++)
+                permB(i) = i;
+            }
+        }
+      else
+        {
+          if (! make_cholb (b, bt, permB))
+            (*current_liboctave_error_handler)
+              ("eigs: The matrix B is not positive definite");
+        }
     }
 
   Array<F77_INT> ip (dim_vector (11, 1));
@@ -2334,20 +2515,85 @@
 
       if (ido == -1 || ido == 1 || ido == 2)
         {
-          double *ip2 = workd + iptr(0) - 1;
-          ColumnVector x(n);
-
-          for (F77_INT i = 0; i < n; i++)
-            x(i) = *ip2++;
-
-          ColumnVector y = fun (x, err);
-
-          if (err)
-            return false;
-
-          ip2 = workd + iptr(1) - 1;
-          for (F77_INT i = 0; i < n; i++)
-            *ip2++ = y(i);
+          if (have_b)
+            {
+              if (mode == 1) // regular mode with factorized B
+                {
+                  Matrix mtmp (n,1);
+                  for (F77_INT i = 0; i < n; i++)
+                    mtmp(i,0) = workd[i + iptr(0) - 1];
+
+                  mtmp = utsolve (bt, permB, mtmp);
+                  ColumnVector y = fun (mtmp, err);
+
+                  if (err)
+                    return false;
+
+                  mtmp = ltsolve (b, permB, y);
+
+                  for (F77_INT i = 0; i < n; i++)
+                    workd[i+iptr(1)-1] = mtmp(i,0);
+                }
+              else // shift-invert mode
+                {
+                  if (ido == -1)
+                    {
+                      OCTAVE_LOCAL_BUFFER (double, dtmp, n);
+
+                      vector_product (b, workd+iptr(0)-1, dtmp);
+
+                      ColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = dtmp[i];
+
+                      ColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      double *ip2 = workd + iptr(1) - 1;
+                      for (F77_INT i = 0; i < n; i++)
+                        ip2[i] = y(i);
+                    }
+                  else if (ido == 2)
+                    vector_product (b, workd+iptr(0)-1, workd+iptr(1)-1);
+                  else
+                    {
+                      double *ip2 = workd+iptr(2)-1;
+                      ColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = *ip2++;
+
+                      ColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      ip2 = workd + iptr(1) - 1;
+                      for (F77_INT i = 0; i < n; i++)
+                        *ip2++ = y(i);
+                     }
+                }
+            }
+          else
+            {
+              double *ip2 = workd + iptr(0) - 1;
+              ColumnVector x(n);
+
+              for (F77_INT i = 0; i < n; i++)
+                x(i) = *ip2++;
+
+              ColumnVector y = fun (x, err);
+
+              if (err)
+                return false;
+
+              ip2 = workd + iptr(1) - 1;
+              for (F77_INT i = 0; i < n; i++)
+                *ip2++ = y(i);
+            }
         }
       else
         {
@@ -2485,6 +2731,8 @@
                   eig_vec(jj,ii) =
                     Complex (octave::numeric_limits<double>::NaN (), 0.);
             }
+          if (note3)
+              eig_vec = utsolve (bt, permB, eig_vec);
         }
       if (k0 < k)
         {
@@ -3119,24 +3367,29 @@
   return ip(4);
 }
 
+template <typename M>
 octave_idx_type
 EigsComplexNonSymmetricFunc (EigsComplexFunc fun, octave_idx_type n_arg,
                              const std::string& _typ, Complex sigma,
                              octave_idx_type k_arg, octave_idx_type p_arg,
                              octave_idx_type& info, ComplexMatrix& eig_vec,
-                             ComplexColumnVector& eig_val,
-                             ComplexColumnVector& cresid, std::ostream& os,
-                             double tol, bool rvec, bool /* cholB */,
-                             int disp, int maxit)
+                             ComplexColumnVector& eig_val, const M& _b,
+                             ColumnVector& permB, ComplexColumnVector& cresid,
+                             std::ostream& os, double tol, bool rvec,
+                             bool cholB, int disp, int maxit)
 {
   F77_INT n = octave::to_f77_int (n_arg);
   F77_INT k = octave::to_f77_int (k_arg);
   F77_INT p = octave::to_f77_int (p_arg);
+  M b(_b);
   std::string typ (_typ);
   bool have_sigma = (std::abs (sigma) ? true : false);
+  F77_INT mode = 1;
+  bool have_b = ! b.isempty ();
+  bool note3 = false;
   char bmat = 'I';
-  F77_INT mode = 1;
   int err = 0;
+  M bt;
 
   if (cresid.isempty ())
     {
@@ -3176,6 +3429,23 @@
     (*current_liboctave_error_handler)
       ("eigs: opts.p must be greater than k and less than or equal to n");
 
+  if (have_b && cholB && ! permB.isempty ())
+    {
+      // Check the we really have a permutation vector
+      if (permB.numel () != n)
+        (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+
+      Array<bool> checked (dim_vector (n, 1), false);
+      for (F77_INT i = 0; i < n; i++)
+        {
+          octave_idx_type bidx = static_cast<octave_idx_type> (permB(i));
+
+          if (checked(bidx) || bidx < 0 || bidx >= n
+              || octave::math::x_nint (bidx) != bidx)
+            (*current_liboctave_error_handler) ("eigs: permB vector invalid");
+        }
+    }
+
   if (! have_sigma)
     {
       if (typ != "LM" && typ != "SM" && typ != "LA" && typ != "SA"
@@ -3187,19 +3457,53 @@
         (*current_liboctave_error_handler)
           ("eigs: invalid sigma value for complex problem");
 
+      if (typ != "SM" && have_b)
+        note3 = true;
+
       if (typ == "SM")
         {
           typ = "LM";
           sigma = 0.;
           mode = 3;
+          if (have_b)
+            bmat ='G';
         }
     }
   else if (! std::abs (sigma))
-    typ = "SM";
+    {
+      typ = "SM";
+      if (have_b)
+        bmat = 'G';
+    }
   else
     {
       typ = "LM";
       mode = 3;
+      if (have_b)
+        bmat = 'G';
+    }
+
+  if (mode == 1 && have_b)
+    {
+      // See Note 3 dsaupd
+      note3 = true;
+      if (cholB)
+        {
+          bt = b;
+          b = b.hermitian ();
+          if (permB.isempty ())
+            {
+              permB = ColumnVector (n);
+              for (F77_INT i = 0; i < n; i++)
+                permB(i) = i;
+            }
+        }
+      else
+        {
+          if (! make_cholb (b, bt, permB))
+            (*current_liboctave_error_handler)
+              ("eigs: The matrix B is not positive definite");
+        }
     }
 
   Array<F77_INT> ip (dim_vector (11, 1));
@@ -3276,20 +3580,85 @@
 
       if (ido == -1 || ido == 1 || ido == 2)
         {
-          Complex *ip2 = workd + iptr(0) - 1;
-          ComplexColumnVector x(n);
-
-          for (F77_INT i = 0; i < n; i++)
-            x(i) = *ip2++;
-
-          ComplexColumnVector y = fun (x, err);
-
-          if (err)
-            return false;
-
-          ip2 = workd + iptr(1) - 1;
-          for (F77_INT i = 0; i < n; i++)
-            *ip2++ = y(i);
+          if (have_b)
+            {
+              if (mode == 1) // regular mode with factorized B
+                {
+                  ComplexMatrix mtmp (n,1);
+                  for (F77_INT i = 0; i < n; i++)
+                    mtmp(i,0) = workd[i + iptr(0) - 1];
+
+                  mtmp = utsolve (bt, permB, mtmp);
+                  ComplexColumnVector y = fun (mtmp, err);
+
+                  if (err)
+                    return false;
+
+                  mtmp = ltsolve (b, permB, y);
+
+                  for (F77_INT i = 0; i < n; i++)
+                    workd[i+iptr(1)-1] = mtmp(i,0);
+                }
+              else // shift-invert mode
+                {
+                  if (ido == -1)
+                    {
+                      OCTAVE_LOCAL_BUFFER (Complex, ctmp, n);
+
+                      vector_product (b, workd+iptr(0)-1, ctmp);
+
+                      ComplexColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = ctmp[i];
+
+                      ComplexColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      Complex *ip2 = workd+iptr(1)-1;
+                      for (F77_INT i = 0; i < n; i++)
+                        ip2[i] = y(i);
+                    }
+                  else if (ido == 2)
+                    vector_product (b, workd+iptr(0)-1, workd+iptr(1)-1);
+                  else
+                    {
+                      Complex *ip2 = workd+iptr(2)-1;
+                      ComplexColumnVector x(n);
+
+                      for (F77_INT i = 0; i < n; i++)
+                        x(i) = *ip2++;
+
+                      ComplexColumnVector y = fun (x, err);
+
+                      if (err)
+                        return false;
+
+                      ip2 = workd + iptr(1) - 1;
+                      for (F77_INT i = 0; i < n; i++)
+                        *ip2++ = y(i);
+                    }
+                }
+            }
+          else
+            {
+              Complex *ip2 = workd + iptr(0) - 1;
+              ComplexColumnVector x(n);
+
+              for (F77_INT i = 0; i < n; i++)
+                x(i) = *ip2++;
+
+              ComplexColumnVector y = fun (x, err);
+
+              if (err)
+                return false;
+
+              ip2 = workd + iptr(1) - 1;
+              for (F77_INT i = 0; i < n; i++)
+                *ip2++ = y(i);
+            }
         }
       else
         {
@@ -3377,6 +3746,8 @@
               for (F77_INT j = 0; j < n; j++)
                 z[off2 + j] = ctmp[j];
             }
+          if (note3)
+            eig_vec = utsolve (bt, permB, eig_vec);
         }
     }
   else
@@ -3409,6 +3780,15 @@
 
 template
 octave_idx_type
+EigsRealSymmetricFunc<Matrix>
+(EigsFunc fun, octave_idx_type n, const std::string& _typ, double sigma,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   Matrix& eig_vec, ColumnVector& eig_val, const Matrix& _b,
+   ColumnVector& permB, ColumnVector& resid, std::ostream& os, double tol,
+   bool rvec, bool cholB, int disp, int maxit);
+
+template
+octave_idx_type
 EigsRealNonSymmetricMatrix<Matrix>
   (const Matrix& m, const std::string typ, octave_idx_type k,
    octave_idx_type p, octave_idx_type& info, ComplexMatrix& eig_vec,
@@ -3425,6 +3805,15 @@
    ColumnVector& resid, std::ostream& os, double tol, bool rvec,
    bool cholB, int disp, int maxit);
 
+template
+octave_idx_type
+EigsRealNonSymmetricFunc<Matrix>
+(EigsFunc fun, octave_idx_type n, const std::string& _typ, double sigmar,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   ComplexMatrix& eig_vec, ComplexColumnVector& eig_val, const Matrix& _b,
+   ColumnVector& permB, ColumnVector& resid, std::ostream& os, double tol,
+   bool rvec, bool cholB, int disp, int maxit);
+
 // SparseMatrix
 
 template
@@ -3447,6 +3836,15 @@
 
 template
 octave_idx_type
+EigsRealSymmetricFunc<SparseMatrix>
+(EigsFunc fun, octave_idx_type n, const std::string& _typ, double sigma,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   Matrix& eig_vec, ColumnVector& eig_val, const SparseMatrix& _b,
+   ColumnVector& permB, ColumnVector& resid, std::ostream& os, double tol,
+   bool rvec, bool cholB, int disp, int maxit);
+
+template
+octave_idx_type
 EigsRealNonSymmetricMatrix<SparseMatrix>
   (const SparseMatrix& m, const std::string typ, octave_idx_type k,
    octave_idx_type p, octave_idx_type& info, ComplexMatrix& eig_vec,
@@ -3463,6 +3861,15 @@
    ColumnVector& resid, std::ostream& os, double tol, bool rvec,
    bool cholB, int disp, int maxit);
 
+template
+octave_idx_type
+EigsRealNonSymmetricFunc<SparseMatrix>
+(EigsFunc fun, octave_idx_type n, const std::string& _typ, double sigmar,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   ComplexMatrix& eig_vec, ComplexColumnVector& eig_val,
+   const SparseMatrix& _b, ColumnVector& permB, ColumnVector& resid,
+   std::ostream& os, double tol, bool rvec, bool cholB, int disp, int maxit);
+
 // ComplexMatrix
 
 template
@@ -3483,6 +3890,15 @@
    ComplexColumnVector& cresid, std::ostream& os, double tol,
    bool rvec, bool cholB, int disp, int maxit);
 
+template
+octave_idx_type
+EigsComplexNonSymmetricFunc<ComplexMatrix>
+(EigsComplexFunc fun, octave_idx_type n, const std::string& _typ, Complex sigma,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   ComplexMatrix& eig_vec, ComplexColumnVector& eig_val,
+   const ComplexMatrix& _b, ColumnVector& permB, ComplexColumnVector& cresid,
+   std::ostream& os, double tol, bool rvec, bool cholB, int disp, int maxit);
+
 // SparseComplexMatrix
 
 template
@@ -3503,4 +3919,13 @@
    ColumnVector& permB, ComplexColumnVector& cresid, std::ostream& os,
    double tol, bool rvec, bool cholB, int disp, int maxit);
 
+template
+octave_idx_type
+EigsComplexNonSymmetricFunc<SparseComplexMatrix>
+(EigsComplexFunc fun, octave_idx_type n, const std::string& _typ, Complex sigma,
+   octave_idx_type k, octave_idx_type p, octave_idx_type& info,
+   ComplexMatrix& eig_vec, ComplexColumnVector& eig_val,
+   const SparseComplexMatrix& _b, ColumnVector& permB,
+   ComplexColumnVector& cresid, std::ostream& os, double tol, bool rvec,
+   bool cholB, int disp, int maxit);
 #endif
--- a/liboctave/numeric/eigs-base.h	Thu Jul 19 22:43:17 2018 +0200
+++ b/liboctave/numeric/eigs-base.h	Wed Jun 27 08:36:55 2018 +0200
@@ -60,14 +60,16 @@
                               std::ostream& os, double tol, bool rvec,
                               bool cholB, int disp, int maxit);
 
+template <typename M>
 extern OCTAVE_API octave_idx_type
 EigsRealSymmetricFunc (EigsFunc fun, octave_idx_type n,
                        const std::string& _typ, double sigma,
                        octave_idx_type k, octave_idx_type p,
                        octave_idx_type& info, Matrix& eig_vec,
-                       ColumnVector& eig_val, ColumnVector& resid,
+                       ColumnVector& eig_val, const M& _b,
+                       ColumnVector& permB, ColumnVector& resid,
                        std::ostream& os, double tol, bool rvec,
-                       bool /* cholB */, int disp, int maxit);
+                       bool cholB, int disp, int maxit);
 
 template <typename M>
 octave_idx_type
@@ -90,14 +92,16 @@
                                  std::ostream& os, double tol, bool rvec,
                                  bool cholB, int disp, int maxit);
 
+template <typename M>
 extern OCTAVE_API octave_idx_type
 EigsRealNonSymmetricFunc (EigsFunc fun, octave_idx_type n,
                           const std::string& _typ, double sigmar,
                           octave_idx_type k, octave_idx_type p,
                           octave_idx_type& info, ComplexMatrix& eig_vec,
-                          ComplexColumnVector& eig_val, ColumnVector& resid,
+                          ComplexColumnVector& eig_val, const M& _b,
+                          ColumnVector& permB, ColumnVector& resid,
                           std::ostream& os, double tol, bool rvec,
-                          bool /* cholB */, int disp, int maxit);
+                          bool cholB, int disp, int maxit);
 
 template <typename M>
 octave_idx_type
@@ -122,14 +126,15 @@
                                     std::ostream& os, double tol, bool rvec,
                                     bool cholB, int disp, int maxit);
 
+template <typename M>
 extern OCTAVE_API octave_idx_type
 EigsComplexNonSymmetricFunc (EigsComplexFunc fun, octave_idx_type n,
                              const std::string& _typ, Complex sigma,
                              octave_idx_type k, octave_idx_type p,
                              octave_idx_type& info, ComplexMatrix& eig_vec,
-                             ComplexColumnVector& eig_val,
-                             ComplexColumnVector& cresid, std::ostream& os,
-                             double tol, bool rvec, bool /* cholB */,
-                             int disp, int maxit);
+                             ComplexColumnVector& eig_val, const M& _b,
+                             ColumnVector& permB, ComplexColumnVector& cresid,
+                             std::ostream& os, double tol, bool rvec,
+                             bool cholB, int disp, int maxit);
 
 #endif
--- a/scripts/sparse/eigs.m	Thu Jul 19 22:43:17 2018 +0200
+++ b/scripts/sparse/eigs.m	Wed Jun 27 08:36:55 2018 +0200
@@ -1480,11 +1480,77 @@
 %! j_B = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 %! v_B = [3, 10i, 1, 8i, 7, 6i, 5, 4i, 9, 7i];
 %! B = sparse(i_B, j_B, v_B); # not SPD
-%! [Evectors Evalues] = eigs(A, B, 5, 'SM'); # call_eig is true
+%! [Evectors, Evalues] = eigs(A, B, 5, "SM"); # call_eig is true
 %! ResidualVectors = A * Evectors - B * Evectors * Evalues;
 %! RelativeErrors = norm (ResidualVectors, "columns") ./ ...
 %! norm (A * Evectors, "columns");
-%! assert (RelativeErrors, zeros (1, 5))
+%! assert (RelativeErrors, zeros (1, 5));
 %!testif HAVE_ARPACK
 %! A = rand (8);
 %! eigs (A, 6, "lr"); # this failed on 4.2.x
+%!testif HAVE_ARPACK
+%! A = rand (10);
+%! B = rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "LM", opts);
+%! Afun = @(x) A * x;
+%! [Evector_f Evalues_f] = eigs (Afun, 10, B, 4, "LM", opts);
+%! assert (Evector, Evector_f);
+%! assert (Evalues, Evalues_f);
+%!testif HAVE_ARPACK
+%! A = rand (10);
+%! B = rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "SM", opts);
+%! Afun = @(x) A \ x;
+%! [Evector_f Evalues_f] = eigs (Afun, 10, B, 4, "SM", opts);
+%! assert (Evector, Evector_f);
+%! assert (Evalues, Evalues_f);
+%!testif HAVE_ARPACK
+%! A = rand (10);
+%! A = A * A';
+%! B = rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "LM", opts);
+%! Afun = @(x) A * x;
+%! opts.issym = true;
+%! [Evector_f Evalues_f] = eigs (Afun, 10, B, 4, "LM", opts);
+%! assert (Evector, Evector_f);
+%! assert (Evalues, Evalues_f);
+%!testif HAVE_ARPACK
+%! A = rand (10);
+%! A = A * A';
+%! B = rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "SM", opts);
+%! Afun = @(x) A \ x;
+%! opts.issym = true;
+%! [Evector_f Evalues_f] = eigs (Afun, 10, B, 4, "SM", opts);
+%! assert (Evector, Evector_f, 100*eps);
+%! assert (Evalues, Evalues_f, 100*eps);
+%!testif HAVE_ARPACK
+%! A = rand (10) + 1i * rand (10);
+%! B = rand (10) + 1i * rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "LM", opts);
+%! Afun = @(x) A * x;
+%! opts.isreal = false;
+%! [Evector_f Evalues_f] = eigs (Afun, 10, B, 4, "LM", opts);
+%! assert (Evector, Evector_f);
+%! assert (Evalues, Evalues_f);
+%!testif HAVE_ARPACK
+%! A = rand (10) + 1i * rand (10);
+%! B = rand (10) + 1i * rand (10);
+%! B = B * B';
+%! opts.v0 = (1:10)';
+%! [Evector, Evalues] = eigs (A, B, 4, "SM", opts);
+%! Afun = @(x) A \ x;
+%! opts.isreal = false;
+%! [Evector_f, Evalues_f] = eigs (Afun, 10, B, 4, "SM", opts);
+%! assert (Evector, Evector_f);
+%! assert (Evalues, Evalues_f);