def divided_difference(self, i): """ EXAMPLES:: sage: X = SchubertPolynomialRing(ZZ) sage: a = X([3,2,1]) sage: a.divided_difference(1) X[2, 3, 1] sage: a.divided_difference([3,2,1]) X[1] """ if isinstance(i, Integer): return symmetrica.divdiff_schubert(i, self) elif i in permutation.Permutations(): return symmetrica.divdiff_perm_schubert(i, self) else: raise TypeError, "i must either be an integer or permutation"
def divided_difference(self, i, algorithm="sage"): r""" Return the ``i``-th divided difference operator, applied to ``self``. Here, ``i`` can be either a permutation or a positive integer. INPUT: - ``i`` -- permutation or positive integer - ``algorithm`` -- (default: ``'sage'``) either ``'sage'`` or ``'symmetrica'``; this determines which software is called for the computation OUTPUT: The result of applying the ``i``-th divided difference operator to ``self``. If `i` is a positive integer, then the `i`-th divided difference operator `\delta_i` is the linear operator sending each polynomial `f = f(x_1, x_2, \ldots, x_n)` (in `n \geq i+1` variables) to the polynomial .. MATH:: \frac{f - f_i}{x_i - x_{i+1}}, \qquad \text{ where } f_i = f(x_1, x_2, ..., x_{i-1}, x_{i+1}, x_i, x_{i+1}, ..., x_n) . If `\sigma` is a permutation in the `n`-th symmetric group, then the `\sigma`-th divided difference operator `\delta_\sigma` is the composition `\delta_{i_1} \delta_{i_2} \cdots \delta_{i_k}`, where `\sigma = s_{i_1} \circ s_{i_2} \circ \cdots \circ s_{i_k}` is any reduced expression for `\sigma` (the precise choice of reduced expression is immaterial). .. NOTE:: The :meth:`expand` method results in a polynomial in `n` variables named ``x0, x1, ..., x(n-1)`` rather than `x_1, x_2, \ldots, x_n`. The variable named ``xi`` corresponds to `x_{i+1}`. Thus, ``self.divided_difference(i)`` involves the variables ``x(i-1)`` and ``xi`` getting switched (in the numerator). EXAMPLES:: sage: X = SchubertPolynomialRing(ZZ) sage: a = X([3,2,1]) sage: a.divided_difference(1) X[2, 3, 1] sage: a.divided_difference([3,2,1]) X[1] sage: a.divided_difference(5) 0 Any divided difference of `0` is `0`:: sage: X.zero().divided_difference(2) 0 This is compatible when a permutation is given as input:: sage: a = X([3,2,4,1]) sage: a.divided_difference([2,3,1]) 0 sage: a.divided_difference(1).divided_difference(2) 0 :: sage: a = X([4,3,2,1]) sage: a.divided_difference([2,3,1]) X[3, 2, 4, 1] sage: a.divided_difference(1).divided_difference(2) X[3, 2, 4, 1] sage: a.divided_difference([4,1,3,2]) X[1, 4, 2, 3] sage: b = X([4, 1, 3, 2]) sage: b.divided_difference(1).divided_difference(2) X[1, 3, 4, 2] sage: b.divided_difference(1).divided_difference(2).divided_difference(3) X[1, 3, 2] sage: b.divided_difference(1).divided_difference(2).divided_difference(3).divided_difference(2) X[1] sage: b.divided_difference(1).divided_difference(2).divided_difference(3).divided_difference(3) 0 sage: b.divided_difference(1).divided_difference(2).divided_difference(1) 0 TESTS: Check that :trac:`23403` is fixed:: sage: X = SchubertPolynomialRing(ZZ) sage: a = X([3,2,4,1]) sage: a.divided_difference(2) 0 sage: a.divided_difference([3,2,1]) 0 sage: a.divided_difference(0) Traceback (most recent call last): ... ValueError: cannot apply \delta_{0} to a (= X[3, 2, 4, 1]) """ if not self: # if self is 0 return self Perms = Permutations() if i in ZZ: if algorithm == "sage": if i <= 0: raise ValueError(r"cannot apply \delta_{%s} to a (= %s)" % (i, self)) # The operator `\delta_i` sends the Schubert # polynomial `X_\pi` (where `\pi` is a finitely supported # permutation of `\{1, 2, 3, \ldots\}`) to: # - the Schubert polynomial X_\sigma`, where `\sigma` is # obtained from `\pi` by switching the values at `i` and `i+1`, # if `i` is a descent of `\pi` (that is, `\pi(i) > \pi(i+1)`); # - `0` otherwise. # Notice that distinct `\pi`s lead to distinct `\sigma`s, # so we can use `_from_dict` here. res_dict = {} for pi, coeff in self: pi = pi[:] n = len(pi) if n <= i: continue if pi[i - 1] < pi[i]: continue pi[i - 1], pi[i] = pi[i], pi[i - 1] pi = Perms(pi).remove_extra_fixed_points() res_dict[pi] = coeff return self.parent()._from_dict(res_dict) else: # if algorithm == "symmetrica": return symmetrica.divdiff_schubert(i, self) elif i in Perms: if algorithm == "sage": i = Permutation(i) redw = i.reduced_word() res_dict = {} for pi, coeff in self: next_pi = False pi = pi[:] n = len(pi) for j in redw: if n <= j: next_pi = True break if pi[j - 1] < pi[j]: next_pi = True break pi[j - 1], pi[j] = pi[j], pi[j - 1] if next_pi: continue pi = Perms(pi).remove_extra_fixed_points() res_dict[pi] = coeff return self.parent()._from_dict(res_dict) else: # if algorithm == "symmetrica": return symmetrica.divdiff_perm_schubert(i, self) else: raise TypeError("i must either be an integer or permutation")
def divided_difference(self, i, algorithm="sage"): r""" Return the ``i``-th divided difference operator, applied to ``self``. Here, ``i`` can be either a permutation or a positive integer. INPUT: - ``i`` -- permutation or positive integer - ``algorithm`` -- (default: ``'sage'``) either ``'sage'`` or ``'symmetrica'``; this determines which software is called for the computation OUTPUT: The result of applying the ``i``-th divided difference operator to ``self``. If `i` is a positive integer, then the `i`-th divided difference operator `\delta_i` is the linear operator sending each polynomial `f = f(x_1, x_2, \ldots, x_n)` (in `n \geq i+1` variables) to the polynomial .. MATH:: \frac{f - f_i}{x_i - x_{i+1}}, \qquad \text{ where } f_i = f(x_1, x_2, ..., x_{i-1}, x_{i+1}, x_i, x_{i+1}, ..., x_n) . If `\sigma` is a permutation in the `n`-th symmetric group, then the `\sigma`-th divided difference operator `\delta_\sigma` is the composition `\delta_{i_1} \delta_{i_2} \cdots \delta_{i_k}`, where `\sigma = s_{i_1} \circ s_{i_2} \circ \cdots \circ s_{i_k}` is any reduced expression for `\sigma` (the precise choice of reduced expression is immaterial). .. NOTE:: The :meth:`expand` method results in a polynomial in `n` variables named ``x0, x1, ..., x(n-1)`` rather than `x_1, x_2, \ldots, x_n`. The variable named ``xi`` corresponds to `x_{i+1}`. Thus, ``self.divided_difference(i)`` involves the variables ``x(i-1)`` and ``xi`` getting switched (in the numerator). EXAMPLES:: sage: X = SchubertPolynomialRing(ZZ) sage: a = X([3,2,1]) sage: a.divided_difference(1) X[2, 3, 1] sage: a.divided_difference([3,2,1]) X[1] sage: a.divided_difference(5) 0 Any divided difference of `0` is `0`:: sage: X.zero().divided_difference(2) 0 This is compatible when a permutation is given as input:: sage: a = X([3,2,4,1]) sage: a.divided_difference([2,3,1]) 0 sage: a.divided_difference(1).divided_difference(2) 0 :: sage: a = X([4,3,2,1]) sage: a.divided_difference([2,3,1]) X[3, 2, 4, 1] sage: a.divided_difference(1).divided_difference(2) X[3, 2, 4, 1] sage: a.divided_difference([4,1,3,2]) X[1, 4, 2, 3] sage: b = X([4, 1, 3, 2]) sage: b.divided_difference(1).divided_difference(2) X[1, 3, 4, 2] sage: b.divided_difference(1).divided_difference(2).divided_difference(3) X[1, 3, 2] sage: b.divided_difference(1).divided_difference(2).divided_difference(3).divided_difference(2) X[1] sage: b.divided_difference(1).divided_difference(2).divided_difference(3).divided_difference(3) 0 sage: b.divided_difference(1).divided_difference(2).divided_difference(1) 0 TESTS: Check that :trac:`23403` is fixed:: sage: X = SchubertPolynomialRing(ZZ) sage: a = X([3,2,4,1]) sage: a.divided_difference(2) 0 sage: a.divided_difference([3,2,1]) 0 sage: a.divided_difference(0) Traceback (most recent call last): ... ValueError: cannot apply \delta_{0} to a (= X[3, 2, 4, 1]) """ if not self: # if self is 0 return self Perms = Permutations() if i in ZZ: if algorithm == "sage": if i <= 0: raise ValueError(r"cannot apply \delta_{%s} to a (= %s)" % (i, self)) # The operator `\delta_i` sends the Schubert # polynomial `X_\pi` (where `\pi` is a finitely supported # permutation of `\{1, 2, 3, \ldots\}`) to: # - the Schubert polynomial X_\sigma`, where `\sigma` is # obtained from `\pi` by switching the values at `i` and `i+1`, # if `i` is a descent of `\pi` (that is, `\pi(i) > \pi(i+1)`); # - `0` otherwise. # Notice that distinct `\pi`s lead to distinct `\sigma`s, # so we can use `_from_dict` here. res_dict = {} for pi, coeff in self: pi = pi[:] n = len(pi) if n <= i: continue if pi[i-1] < pi[i]: continue pi[i-1], pi[i] = pi[i], pi[i-1] pi = Perms(pi).remove_extra_fixed_points() res_dict[pi] = coeff return self.parent()._from_dict(res_dict) else: # if algorithm == "symmetrica": return symmetrica.divdiff_schubert(i, self) elif i in Perms: if algorithm == "sage": i = Permutation(i) redw = i.reduced_word() res_dict = {} for pi, coeff in self: next_pi = False pi = pi[:] n = len(pi) for j in redw: if n <= j: next_pi = True break if pi[j-1] < pi[j]: next_pi = True break pi[j-1], pi[j] = pi[j], pi[j-1] if next_pi: continue pi = Perms(pi).remove_extra_fixed_points() res_dict[pi] = coeff return self.parent()._from_dict(res_dict) else: # if algorithm == "symmetrica": return symmetrica.divdiff_perm_schubert(i, self) else: raise TypeError("i must either be an integer or permutation")