Repository: scipy/scipy

Issue:
BUG: sokalmichener appears to incorrectly apply weights
The code and results for the sokalmichener distance metric appears not to apply weights to all the factors.

I've been writing gpu versions of the distance metrics, and noticed inconsistent results when comparing my sokalmichener values to the scipy ones.

Reading the code, I believe the problem is that the weights are not being applied to the `ntf` and `nft` values:

[distance.py:1582](https://github.com/scipy/scipy/blob/5ab7426247900db9de856e790b8bea1bd71aec49/scipy/spatial/distance.py#L1580):

```python
        w = _validate_weights(w)
        ntt = (u * v * w).sum()
        nff = ((1.0 - u) * (1.0 - v) * w).sum()
    (nft, ntf) = _nbool_correspond_ft_tf(u, v)
```

You can see that the weights `w` are being applied to `ntt` and `nff`, but not passed in to the `ntf`/`nft` calculation.

If this is compared to the `sokalsneath` function immediately below:

```python
        w = _validate_weights(w)
        ntt = (u * v * w).sum()
    (nft, ntf) = _nbool_correspond_ft_tf(u, v, w=w)
```
It superficially seems clear that `w=w` should be passed in to the `_nbool_correspond_ft_tf` function to apply weights.

## Test case:

Here's my worked test case, that shows the issue (It's possible I've misunderstood something here, apologies if so):

```python
from scipy.spatial.distance import sokalmichener

#from: | 1 |   | 0 |
#to:   | 1 |   | 1 |
#weight|   | 1 |   | 0.2
ntf =    0 * 1 + 0 * 0.2
nft =    0 * 1 + 1 * 0.2
ntt =    1 * 1 + 0 * 0.2
nff =    0 * 1 + 0 * 0.2

expected =  2 * (nft + ntf) / (ntt + nff + 2 * (nft + ntf))

print(expected)
assert expected - 0.2857143 < 1e-6
actual = sokalmichener([1, 0],[1, 1], w=[1, 0.2])
assert actual - expected < 1e-6, actual    # fails
```

#### Scipy/Numpy/Python version information:

Observed in:
1.4.1 1.20.1 sys.version_info(major=3, minor=8, micro=1, releaselevel='final', serial=0)

Code seems wrong on main branch


Discussion:

Resolving patch:
diff --git a/scipy/spatial/distance.py b/scipy/spatial/distance.py
--- a/scipy/spatial/distance.py
+++ b/scipy/spatial/distance.py
@@ -1553,7 +1553,7 @@ def sokalmichener(u, v, w=None):
         w = _validate_weights(w)
         ntt = (u * v * w).sum()
         nff = ((1.0 - u) * (1.0 - v) * w).sum()
-    (nft, ntf) = _nbool_correspond_ft_tf(u, v)
+    (nft, ntf) = _nbool_correspond_ft_tf(u, v, w=w)
     return float(2.0 * (ntf + nft)) / float(ntt + nff + 2.0 * (ntf + nft))
 
 


Test patch:
diff --git a/scipy/spatial/tests/test_distance.py b/scipy/spatial/tests/test_distance.py
--- a/scipy/spatial/tests/test_distance.py
+++ b/scipy/spatial/tests/test_distance.py
@@ -2063,6 +2063,28 @@ def test_sokalmichener():
     assert_equal(dist1, dist2)
 
 
+def test_sokalmichener_with_weight():
+    # from: | 1 |   | 0 |
+    # to:   | 1 |   | 1 |
+    # weight|   | 1 |   | 0.2
+    ntf = 0 * 1 + 0 * 0.2
+    nft = 0 * 1 + 1 * 0.2
+    ntt = 1 * 1 + 0 * 0.2
+    nff = 0 * 1 + 0 * 0.2
+    expected = 2 * (nft + ntf) / (ntt + nff + 2 * (nft + ntf))
+    assert_almost_equal(expected, 0.2857143)
+    actual = sokalmichener([1, 0], [1, 1], w=[1, 0.2])
+    assert_almost_equal(expected, actual)
+
+    a1 = [False, False, True, True, True, False, False, True, True, True, True,
+          True, True, False, True, False, False, False, True, True]
+    a2 = [True, True, True, False, False, True, True, True, False, True,
+          True, True, True, True, False, False, False, True, True, True]
+
+    for w in [0.05, 0.1, 1.0, 20.0]:
+        assert_almost_equal(sokalmichener(a2, a1, [w]), 0.6666666666666666)
+
+
 def test_modifies_input():
     # test whether cdist or pdist modifies input arrays
     X1 = np.asarray([[1., 2., 3.],