Repository: scipy/scipy

Issue:
eigh gives incorrect results
<!-- 
Thank you for taking the time to file a bug report. 
Please fill in the fields below, deleting the sections that 
don't apply to your issue. You can view the final output
by clicking the preview button above.
Note: This is a comment, and won't appear in the output.
-->

I encountered a problem of `scipy.linalg.eigh` in scipy-1.5.* for both linux wheels and mac wheels from pypi. It works alright in scipy-1.4.

#### Reproducing code example:
<!-- 
If you place your code between the triple backticks below, 
it will be rendered as a code block. 
-->

```
import numpy as np
import scipy.linalg

h = np.array([[0.04158460437317617, 0.0006616854170254885, 0.004563334904502526, 0.000725462889684489],
              [0.0006616854170254896, 0.1026584900122756, -0.004270233810754883, -0.00039081602402200486],
              [0.0045633349045025254, -0.004270233810754883, 0.0006845005523483652, 9.663259412285491e-05],
              [0.0007254628896844891, -0.00039081602402200486, 9.663259412285491e-05, 1.423320805795794e-05]])
s = np.array([[1., 0., 0., 0.],
              [0., 1., 0., -0.4613537269212372],
              [0., 0., 1., 0.35774720964698314],
              [0., -0.4613537269212372, 0.35774720964698314, 1.]])
occ, mo = scipy.linalg.eigh(h, s, type=2)
print(abs(mo*occ - h.dot(s).dot(mo)).max())
```

#### Error message:
<!-- If any, paste the *full* error message inside a code block
as above (starting from line Traceback)
-->
The code above should output ~0 . It's 0.08554496849197549 using scipy-1.5

#### Scipy/Numpy/Python version information:
<!-- You can simply run the following and paste the result in a code block
```
import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
```
-->
1.5.1 1.19.0 sys.version_info(major=3, minor=7, micro=6, releaselevel='final', serial=0)




Discussion:
I have no idea how we missed this but `type` parameter is not taken into account in the current version so assumed to be `1` in all invocations. I'll fix this asap.  

Resolving patch:
diff --git a/scipy/linalg/decomp.py b/scipy/linalg/decomp.py
--- a/scipy/linalg/decomp.py
+++ b/scipy/linalg/decomp.py
@@ -460,8 +460,11 @@ def eigh(a, b=None, lower=True, eigvals_only=False, overwrite_a=False,
             raise ValueError("wrong b dimensions {}, should "
                              "be {}".format(b1.shape, a1.shape))
 
+        if type not in [1, 2, 3]:
+            raise ValueError('"type" keyword only accepts 1, 2, and 3.')
+
         cplx = True if iscomplexobj(b1) else (cplx or False)
-        drv_args.update({'overwrite_b': overwrite_b})
+        drv_args.update({'overwrite_b': overwrite_b, 'itype': type})
 
     # backwards-compatibility handling
     subset_by_index = subset_by_index if (eigvals is None) else eigvals


Test patch:
diff --git a/scipy/linalg/tests/test_decomp.py b/scipy/linalg/tests/test_decomp.py
--- a/scipy/linalg/tests/test_decomp.py
+++ b/scipy/linalg/tests/test_decomp.py
@@ -783,6 +783,9 @@ def test_wrong_inputs(self):
         assert_raises(ValueError, eigh, np.ones([2, 2]), np.ones([2, 1]))
         # Incompatible a, b sizes
         assert_raises(ValueError, eigh, np.ones([3, 3]), np.ones([2, 2]))
+        # Wrong type parameter for generalized problem
+        assert_raises(ValueError, eigh, np.ones([3, 3]), np.ones([3, 3]),
+                      type=4)
         # Both value and index subsets requested
         assert_raises(ValueError, eigh, np.ones([3, 3]), np.ones([3, 3]),
                       subset_by_value=[1, 2], eigvals=[2, 4])
@@ -846,13 +849,19 @@ def test_various_drivers_standard(self, driver):
         w, v = eigh(a, driver=driver)
         assert_allclose(a @ v - (v * w), 0., atol=1000*np.spacing(1.), rtol=0.)
 
+    @pytest.mark.parametrize('type', (1, 2, 3))
     @pytest.mark.parametrize('driver', ("gv", "gvd", "gvx"))
-    def test_various_drivers_generalized(self, driver):
+    def test_various_drivers_generalized(self, driver, type):
+        atol = np.spacing(5000.)
         a = _random_hermitian_matrix(20)
         b = _random_hermitian_matrix(20, posdef=True)
-        w, v = eigh(a=a, b=b, driver=driver)
-        assert_allclose(a @ v - w*(b @ v), 0.,
-                        atol=1000*np.spacing(1.), rtol=0.)
+        w, v = eigh(a=a, b=b, driver=driver, type=type)
+        if type == 1:
+            assert_allclose(a @ v - w*(b @ v), 0., atol=atol, rtol=0.)
+        elif type == 2:
+            assert_allclose(a @ b @ v - v * w, 0., atol=atol, rtol=0.)
+        else:
+            assert_allclose(b @ a @ v - v * w, 0., atol=atol, rtol=0.)
 
     # Old eigh tests kept for backwards compatibility
     @pytest.mark.parametrize('eigvals', (None, (2, 4)))