Repository: scipy/scipy

Issue:
odr.Data `fix` assignment is not passed through to odr.ODR as `ifixx` assignment
<<Please describe the issue in detail here, and for bug reports fill in the fields below.>>

I am using `scipy.odr` to fit an implicitly defined function. It seems that I have to assign directly to `ifixx` when creating an `odr.ODR` object instead of relying on my earlier assignment to `fix` when creating the `odr.Data` object that I pass to `odr.ODR`. Perhaps defining `fix` in odr.Data objects should simply be removed, because this duplicated functionality is somewhat confusing and doesn't seem to work as expected with `odr.ODR`. Otherwise, if this is the intended behavior, then perhaps this requirement to specify both `fix` and `ifixx` should be better documented.

### Reproducing code example:
Input:
```Python
import scipy.odr as odr

# Some noisy data solving x2 = x1**2
x1 = [-2.01, -0.99, -0.001, 1.02, 1.98]
x2 = [3.98, 1.01, 0.001, 0.998, 4.01]
fix = np.vstack((np.zeros_like(x1, dtype=int), np.ones_like(x2, dtype=int)))

# Fixing x1 via the odr.Data object
odr_data = odr.Data(np.vstack((x1, x2)), y=1, fix=fix)

# Define the odr.Model object with an implicit model
odr_model = odr.Model(lambda beta, x: x[1, :] - beta[0] * x[0, :]**2., implicit=True)

# Prep and run the model WITHOUT specifying ifixx in odr.ODR object construction
odr_odr = odr.ODR(odr_data, odr_model, beta0=np.array([1.]))
odr_sol = odr_odr.run()
print(odr_sol.beta)

# Prep and run the model WITH specifying ifixx in odr.ODR object construction
odr_odr = odr.ODR(odr_data, odr_model, beta0=np.array([1.]), ifixx=fix)
odr_sol = odr_odr.run()
print(odr_sol.beta)
```
Output:
```
[1.00205273]
[1.00277605]
```
(I think the output should be the second output in both cases.)

### Error message:
N/A

### Scipy/Numpy/Python version information:
```
Python version: 3.6.5
Numpy version: 1.14.5
Scipy version: 1.1.0
```




Discussion:

Resolving patch:
diff --git a/scipy/odr/odrpack.py b/scipy/odr/odrpack.py
--- a/scipy/odr/odrpack.py
+++ b/scipy/odr/odrpack.py
@@ -742,6 +742,9 @@ def __init__(self, data, model, beta0=None, delta0=None, ifixb=None,
         else:
             self.beta0 = _conv(beta0)
 
+        if ifixx is None and data.fix is not None:
+            ifixx = data.fix
+
         self.delta0 = _conv(delta0)
         # These really are 32-bit integers in FORTRAN (gfortran), even on 64-bit
         # platforms.


Test patch:
diff --git a/scipy/odr/tests/test_odr.py b/scipy/odr/tests/test_odr.py
--- a/scipy/odr/tests/test_odr.py
+++ b/scipy/odr/tests/test_odr.py
@@ -344,3 +344,17 @@ def linear(c, x):
         result = job.run()
         assert_equal(result.info, 2)
 
+    # Verify fix for gh-9140
+
+    def test_ifixx(self):
+        x1 = [-2.01, -0.99, -0.001, 1.02, 1.98]
+        x2 = [3.98, 1.01, 0.001, 0.998, 4.01]
+        fix = np.vstack((np.zeros_like(x1, dtype=int), np.ones_like(x2, dtype=int)))
+        data = Data(np.vstack((x1, x2)), y=1, fix=fix)
+        model = Model(lambda beta, x: x[1, :] - beta[0] * x[0, :]**2., implicit=True)
+
+        odr1 = ODR(data, model, beta0=np.array([1.]))
+        sol1 = odr1.run()
+        odr2 = ODR(data, model, beta0=np.array([1.]), ifixx=fix)
+        sol2 = odr2.run()
+        assert_equal(sol1.beta, sol2.beta)