Repository: sagemath/sage

Issue:
performance bug in subs of fraction field
### Steps To Reproduce

Substitutions in the fraction field of a polynomial ring are much slower than necessary:
```
sage: P = PolynomialRing(QQ, ["x%s" % i for i in range(1000)])
sage: PF = P.fraction_field()
sage: p = sum(i*P.gen(i) for i in range(50,52)) / sum(i*P.gen(i) for i in range(51,53)); v = P.gen(51)
sage: %time p.subs({v: 100})
CPU times: user 55.4 ms, sys: 0 ns, total: 55.4 ms
Wall time: 55.4 ms
(50*x50 + 5100)/(52*x52 + 5100)
sage: %time p.numerator().subs({v: 100}) / p.denominator().subs({v: 100})
CPU times: user 3.32 ms, sys: 0 ns, total: 3.32 ms
Wall time: 3.33 ms
(50*x50 + 5100)/(52*x52 + 5100)
```

### Expected Behavior

There should be not much difference in timing.

### Actual Behavior

The method called is `Element.subs`, which, first of all, creates a dictionary containing all the `gens` of the parent.

### Additional Information

#1381 seems to be orthogonal to this.

### Environment

```markdown
irrelevant.
```


### Checklist

- [X] I have searched the existing issues for a bug report that matches the one I want to file, without success.
- [X] I have read the documentation and troubleshoot guide


Discussion:

Resolving patch:
diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py
--- a/src/sage/rings/polynomial/multi_polynomial_element.py
+++ b/src/sage/rings/polynomial/multi_polynomial_element.py
@@ -178,8 +178,8 @@ def __call__(self, *x, **kwds):
         except AttributeError:
             K = self.parent().base_ring()
         y = K(0)
-        for (m,c) in self.element().dict().items():
-            y += c*prod([ x[i]**m[i] for i in range(n) if m[i] != 0])
+        for m, c in self.element().dict().items():
+            y += c * prod(v ** e for v, e in zip(x, m) if e)
         return y
 
     def _richcmp_(self, right, op):
@@ -1388,7 +1388,7 @@ def is_term(self):
         """
         return len(self.element()) == 1
 
-    def subs(self, fixed=None, **kw):
+    def subs(self, fixed=None, **kwds):
         """
         Fix some given variables in a given multivariate polynomial and
         return the changed multivariate polynomials. The polynomial itself
@@ -1400,10 +1400,9 @@ def subs(self, fixed=None, **kw):
 
         INPUT:
 
-
         -  ``fixed`` - (optional) dictionary of inputs
 
-        -  ``**kw`` - named parameters
+        -  ``**kwds`` - named parameters
 
 
         OUTPUT: new :class:`MPolynomial`
@@ -1419,11 +1418,14 @@ def subs(self, fixed=None, **kw):
             25*y^2 + y + 30
         """
         variables = list(self.parent().gens())
-        for i in range(0,len(variables)):
-            if str(variables[i]) in kw:
-                variables[i] = kw[str(variables[i])]
-            elif fixed and variables[i] in fixed:
-                variables[i] = fixed[variables[i]]
+        for i in range(len(variables)):
+            if str(variables[i]) in kwds:
+                variables[i] = kwds[str(variables[i])]
+            elif fixed:
+                if variables[i] in fixed:
+                    variables[i] = fixed[variables[i]]
+                elif i in fixed:
+                    variables[i] = fixed[i]
         return self(tuple(variables))
 
     def monomials(self):


Test patch: