class TestGradedModule(unittest.TestCase):
 
  def setUp(self):
    self.poly_ring = PolynomialRing(QQ,"x",3);
    self.x = self.poly_ring.gens()[0];
    self.y = self.poly_ring.gens()[1];
    self.z = self.poly_ring.gens()[2];

  def test_monomial_basis_zero(self):
    one = self.poly_ring.one()
    zero = self.poly_ring.zero()
    gm = GradedModule([[one,one,one,one]],[0,1,2,3],[1,2,3])
    self.assertEqual(gm.monomial_basis(0),[(one,zero,zero,zero)])

  def test_monomial_basis(self):
    x = self.x
    y = self.y
    one = self.poly_ring.one()
    zero = self.poly_ring.zero()
    gm = GradedModule([[one,one]],[0,1],[1,2,3])
    true_basis  = [(x**2,zero),(y,zero),(zero,x)]
    self.assertEqual(Set(gm.monomial_basis(2)),Set(true_basis))

  def test_homogeneous_parts_A(self):
    one = self.poly_ring.one()
    zero = self.poly_ring.zero()
    gm = GradedModule([[one,one]],[0,1],[1,2,3])
    parts = gm.get_homogeneous_parts([one,one])
    parts_true = { 0:[one,zero] , 1:[zero,one] }
    self.assertEqual(parts,parts_true)

  def test_homogeneous_parts_B(self):
    x = self.x
    y = self.y
    z = self.z
    one = self.poly_ring.one()
    zero = self.poly_ring.zero()
    gm = GradedModule([[one,one]],[0,1],[1,2,3])
    parts = gm.get_homogeneous_parts([x*y,x**3+z*y])
    self.assertEqual(parts,{3:[x*y,zero],4:[zero,x**3],6:[zero,z*y]})
  
  def test_homogeneous_part_basisA(self):
    x = self.x
    y = self.y
    z = self.z
    one = self.poly_ring.one()
    gm = GradedModule([[z,one,x**2 + y]],[0,1,2],[1,2,3])
    basis = gm.homogeneous_part_basis(6);
    self.assertEqual(len(basis),10)
 
  def test_homogeneous_part_basisB(self):
    #From bug found with ncd
    x = self.x
    y = self.y
    z = self.z
    zero = self.poly_ring.zero()
    gm = GradedModule([[zero, x*z, x*y], [zero, -x*z, zero], [y*z, zero, zero]],[1, 1, 1],[1, 1, 1])
    basis = gm.homogeneous_part_basis(3);
    self.assertEqual(len(basis),3)
    def has_rep(self, n, restrict=None, force=False):
        """
        Returns `True` if there exists an `n`-dimensional representation of `self`, and `False`
        otherwise.

        The optional argument `restrict` may be used to restrict the possible images of the
        generators. To do so, `restrict` must be a tuple with entries of `None`, `'diagonal'`,
        `'lower'`, or `'upper'`. Its length must match the number of generators of `self`.

        Use `force=True` if the function does not recognize the base field as computable, but the
        field is computable.
        """
        if (not force and self.base_field() not in NumberFields
                and self.base_field() not in FiniteFields):
            raise TypeError(
                'Base field must be computable. If %s is computable' %
                self.base_field() + ' then use force=True to bypass this.')

        if n not in ZZ or n < 1:
            raise ValueError('Dimension must be a positive integer.')

        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        import math

        B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1),
                           'x',
                           order='deglex')
        M = MatrixSpace(B, n, sparse=True)

        gen_matrix = list()
        if not isinstance(restrict, (tuple, list)):
            restrict = [None for i in range(self.ngens())]
        if len(restrict) != self.ngens():
            raise ValueError(
                'Length of restrict does not match number of generators.')
        for i in range(self.ngens()):
            ith_gen_matrix = []
            for j in range(n):
                for k in range(n):
                    if restrict[i] == 'upper' and j > k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'lower' and j < k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'diagonal' and j != k:
                        ith_gen_matrix.append(B.zero())
                    else:
                        ith_gen_matrix.append(B.gen(j + (j + 1) * k +
                                                    i * n**2))
            gen_matrix.append(M(ith_gen_matrix))

        relB_list = list()
        for i in range(self.nrels()):
            relB_list += self._to_matrix(self.rel(i), M, gen_matrix).list()
        relB = B.ideal(relB_list)

        if relB.dimension() == -1: return False
        else: return True
示例#3
0
def rand_w_hom_divisor(n, degs=None, mon_num=None, var="z"):
    if degs == None:
        degs = [randrange(2, 6) for _ in range(n)]
    deg = sum(degs)
    if mon_num == None:
        mon_num = randrange(2, 8)
    poly_ring = PolynomialRing(QQ, n, var)
    div = poly_ring.zero()
    min_w = min(degs)
    for i in range(mon_num):
        expo = [0] * n
        cur_deg = 0
        while cur_deg != deg:
            if cur_deg > deg:
                expo = [0] * n
                cur_deg = 0
            if deg - cur_deg < min_w:
                expo = [0] * n
                cur_deg = 0
            next_g = randrange(0, n)
            expo[next_g] += 1
            cur_deg += degs[next_g]
        coeff = randrange(-n, n) / n
        mon = poly_ring.one()
        for i, e in enumerate(expo):
            mon *= poly_ring.gens()[i]**e
        div += coeff * mon
    return div
class TestConvertSymToPoly(unittest.TestCase):

  def setUp(self):
    self.poly_ring = PolynomialRing(QQ,"x",3);
    self.x = self.poly_ring.gens()[0];
    self.y = self.poly_ring.gens()[1];
    self.z = self.poly_ring.gens()[2];
    self.vars = var('x,y,z')
    
  def test_zero(self):
    zero = 0*self.vars[0]
    poly = convert_symbolic_to_polynomial(zero,self.poly_ring,self.vars)
    self.assertEqual(poly,self.poly_ring.zero())
    
  def test_convert(self):
    x = self.x
    y = self.y
    z = self.z
    sym_poly = 4*self.vars[0]**4 + self.vars[1]*self.vars[0]**12*self.vars[1]-self.vars[2]
    poly = convert_symbolic_to_polynomial(sym_poly,self.poly_ring,self.vars)
    self.assertEqual(poly,4*x**4+y*x**12*y-z)

  def test_convert_univarient(self):
    y = self.y
    sym_poly = 4*self.vars[1]**4 + self.vars[1]*self.vars[1]**12*self.vars[1]-self.vars[1]
    poly = convert_symbolic_to_polynomial(sym_poly,self.poly_ring,self.vars)
    self.assertEqual(poly,4*y**4+y*y**12*y-y)

  def test_convert_partial(self):
    y = self.y
    z = self.z
    sym_poly = 4*self.vars[2]**4 + self.vars[1]*self.vars[1]**12*self.vars[1]-self.vars[1]
    poly = convert_symbolic_to_polynomial(sym_poly,self.poly_ring,self.vars)
    self.assertEqual(poly,4*z**4+y*y**12*y-y)
def rand_w_hom_divisor(n,degs=None,mon_num=None,var="z"):
  if degs==None:
    degs = [randrange(2,6) for _ in range(n)]
  deg = sum(degs)
  if mon_num==None:
    mon_num = randrange(2,8)
  poly_ring = PolynomialRing(QQ,n,var)
  div = poly_ring.zero()
  min_w = min(degs)
  for i in range(mon_num):
    expo = [0]*n
    cur_deg = 0
    while cur_deg!=deg:
      if cur_deg>deg:
        expo = [0]*n
        cur_deg = 0
      if deg-cur_deg<min_w:
        expo = [0]*n
        cur_deg = 0
      next_g = randrange(0,n)
      expo[next_g] += 1
      cur_deg += degs[next_g]
    coeff = randrange(-n,n)/n
    mon = poly_ring.one()
    for i,e in enumerate(expo):
      mon *= poly_ring.gens()[i]**e
    div += coeff*mon
  return div
class TestWeightedMinDegree(unittest.TestCase):

  def setUp(self):
    self.poly_ring = PolynomialRing(QQ,"x",3);
    self.x = self.poly_ring.gens()[0];
    self.y = self.poly_ring.gens()[1];
    self.z = self.poly_ring.gens()[2];

  def test_zero(self):
    self.assertEqual(wieghted_min_degree(self.poly_ring.zero(),[1,1,1]),0);
  
  def test_homogeneous(self):
    x = self.x;
    y = self.y;
    z = self.z;
    f = x**4 + 4*y*z**3 - z**2;
    self.assertEqual(wieghted_min_degree(f,[1,1,1]),2);

  def test_non_homogeneous(self):
    x = self.x;
    y = self.y;
    z = self.z;
    f = x**4 + 4*y*z**3 - z**2;
    self.assertEqual(wieghted_min_degree(f,[2,1,2]),4);

  def test_negative_wieghts(self):
    x = self.x;
    y = self.y;
    z = self.z;
    f = x**4 + 4*y*z**3 - z**2;
    self.assertEqual(wieghted_min_degree(f,[-1,-1,1]),-4);
示例#7
0
class TestLogaritmicDerivations(unittest.TestCase):
    def setUp(self):
        self.poly_ring = PolynomialRing(QQ, "x", 3)
        self.x = self.poly_ring.gens()[0]
        self.y = self.poly_ring.gens()[1]
        self.z = self.poly_ring.gens()[2]

    def test_normal_crossing(self):
        zero = self.poly_ring.zero()
        log_der = LogarithmicDerivations(self.x * self.y * self.z)
        free = SingularModule([[self.x, zero, zero], [zero, self.y, zero],
                               [zero, zero, self.z]])
        self.assertTrue(log_der.equals(free))
示例#8
0
 def polynomial(self, g, n, ring=None):
     if ring is None:
         from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
         if n == 1:
             # Otherwise, the b is not numbered!
             ring = PolynomialRing(self._base_ring, 'b0', n)
         else:
             ring = PolynomialRing(self._base_ring, 'b', n)
         b = ring.gens()
     p = ring.zero()
     p += sum(
         self.F(g, n, I) * prod(b[i]**j for i, j in enumerate(I))
         for I in compositions(g, n))
     return p
    def Tokuyama_coefficient(self, name='t'):
        r"""
        Return the Tokuyama coefficient attached to ``self``.

        Following the exposition of [BBF]_, Tokuyama's formula asserts

        .. MATH::

            \sum_{G} (t+1)^{s(G)} t^{l(G)}
            z_1^{d_{n+1}} z_2^{d_{n}-d_{n+1}} \cdots z_{n+1}^{d_1-d_2}
            =
            s_{\lambda}(z_1,\dots,z_{n+1}) \prod_{i<j} (z_j+tz_i),

        where the sum is over all strict Gelfand-Tsetlin patterns with fixed
        top row `\lambda + \rho`, with `\lambda` a partition with at most
        `n+1` parts and `\rho = (n, n-1, \ldots, 1, 0)`, and `s_\lambda` is a
        Schur function.

        INPUT:

        - ``name`` -- (Default: ``'t'``) An alternative name for the
          variable `t`.

        EXAMPLES::

            sage: P = GelfandTsetlinPattern([[3,2,1],[2,2],[2]])
            sage: P.Tokuyama_coefficient()
            0
            sage: G = GelfandTsetlinPattern([[3,2,1],[3,1],[2]])
            sage: G.Tokuyama_coefficient()
            t^2 + t
            sage: G = GelfandTsetlinPattern([[2,1,0],[1,1],[1]])
            sage: G.Tokuyama_coefficient()
            0
            sage: G = GelfandTsetlinPattern([[5,3,2,1,0],[4,3,2,0],[4,2,1],[3,2],[3]])
            sage: G.Tokuyama_coefficient()
            t^8 + 3*t^7 + 3*t^6 + t^5
        """
        R = PolynomialRing(ZZ, name)
        t = R.gen(0)
        if not self.is_strict():
            return R.zero()
        return (t + 1)**(self.number_of_special_entries()) * t**(
            self.number_of_boxes())
示例#10
0
    def Tokuyama_coefficient(self, name='t'):
        r"""
        Return the Tokuyama coefficient attached to ``self``.

        Following the exposition of [BBF]_, Tokuyama's formula asserts

        .. MATH::

            \sum_{G} (t+1)^{s(G)} t^{l(G)}
            z_1^{d_{n+1}} z_2^{d_{n}-d_{n+1}} \cdots z_{n+1}^{d_1-d_2}
            =
            s_{\lambda}(z_1,\dots,z_{n+1}) \prod_{i<j} (z_j+tz_i),

        where the sum is over all strict Gelfand-Tsetlin patterns with fixed
        top row `\lambda + \rho`, with `\lambda` a partition with at most
        `n+1` parts and `\rho = (n, n-1, \ldots, 1, 0)`, and `s_\lambda` is a
        Schur function.

        INPUT:

        - ``name`` -- (Default: ``'t'``) An alternative name for the
          variable `t`.

        EXAMPLES::

            sage: P = GelfandTsetlinPattern([[3,2,1],[2,2],[2]])
            sage: P.Tokuyama_coefficient()
            0
            sage: G = GelfandTsetlinPattern([[3,2,1],[3,1],[2]])
            sage: G.Tokuyama_coefficient()
            t^2 + t
            sage: G = GelfandTsetlinPattern([[2,1,0],[1,1],[1]])
            sage: G.Tokuyama_coefficient()
            0
            sage: G = GelfandTsetlinPattern([[5,3,2,1,0],[4,3,2,0],[4,2,1],[3,2],[3]])
            sage: G.Tokuyama_coefficient()
            t^8 + 3*t^7 + 3*t^6 + t^5
        """
        R = PolynomialRing(ZZ, name)
        t = R.gen(0)
        if not self.is_strict():
            return R.zero()
        return (t+1)**(self.number_of_special_entries()) * t**(self.number_of_boxes())
class TestConvertPolyToSym(unittest.TestCase):

  def setUp(self):
    self.poly_ring = PolynomialRing(QQ,"x",3);
    self.x = self.poly_ring.gens()[0];
    self.y = self.poly_ring.gens()[1];
    self.z = self.poly_ring.gens()[2];
    self.vars = var('x,y,z')
    
  def test_zero(self):
    zero = self.poly_ring.zero()
    poly = convert_polynomial_to_symbolic(zero,self.vars)
    self.assertEqual(poly,0)
    
  def test_convert(self):
    x = self.x
    y = self.y
    z = self.z
    poly = 4*x**4+y*x**12*y-z
    sym_poly = 4*self.vars[0]**4 + self.vars[1]*self.vars[0]**12*self.vars[1]-self.vars[2]
    con_poly = convert_polynomial_to_symbolic(poly,self.vars)
    self.assertEqual(sym_poly,con_poly)
示例#12
0
class TestConvertPolyToSym(unittest.TestCase):
    def setUp(self):
        self.poly_ring = PolynomialRing(QQ, "x", 3)
        self.x = self.poly_ring.gens()[0]
        self.y = self.poly_ring.gens()[1]
        self.z = self.poly_ring.gens()[2]
        self.vars = var('x,y,z')

    def test_zero(self):
        zero = self.poly_ring.zero()
        poly = convert_polynomial_to_symbolic(zero, self.vars)
        self.assertEqual(poly, 0)

    def test_convert(self):
        x = self.x
        y = self.y
        z = self.z
        poly = 4 * x**4 + y * x**12 * y - z
        sym_poly = 4 * self.vars[0]**4 + self.vars[1] * self.vars[
            0]**12 * self.vars[1] - self.vars[2]
        con_poly = convert_polynomial_to_symbolic(poly, self.vars)
        self.assertEqual(sym_poly, con_poly)
示例#13
0
class TestConvertSymToPoly(unittest.TestCase):
    def setUp(self):
        self.poly_ring = PolynomialRing(QQ, "x", 3)
        self.x = self.poly_ring.gens()[0]
        self.y = self.poly_ring.gens()[1]
        self.z = self.poly_ring.gens()[2]
        self.vars = var('x,y,z')

    def test_zero(self):
        zero = 0 * self.vars[0]
        poly = convert_symbolic_to_polynomial(zero, self.poly_ring, self.vars)
        self.assertEqual(poly, self.poly_ring.zero())

    def test_convert(self):
        x = self.x
        y = self.y
        z = self.z
        sym_poly = 4 * self.vars[0]**4 + self.vars[1] * self.vars[
            0]**12 * self.vars[1] - self.vars[2]
        poly = convert_symbolic_to_polynomial(sym_poly, self.poly_ring,
                                              self.vars)
        self.assertEqual(poly, 4 * x**4 + y * x**12 * y - z)

    def test_convert_univarient(self):
        y = self.y
        sym_poly = 4 * self.vars[1]**4 + self.vars[1] * self.vars[
            1]**12 * self.vars[1] - self.vars[1]
        poly = convert_symbolic_to_polynomial(sym_poly, self.poly_ring,
                                              self.vars)
        self.assertEqual(poly, 4 * y**4 + y * y**12 * y - y)

    def test_convert_partial(self):
        y = self.y
        z = self.z
        sym_poly = 4 * self.vars[2]**4 + self.vars[1] * self.vars[
            1]**12 * self.vars[1] - self.vars[1]
        poly = convert_symbolic_to_polynomial(sym_poly, self.poly_ring,
                                              self.vars)
        self.assertEqual(poly, 4 * z**4 + y * y**12 * y - y)
示例#14
0
def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None):
    r"""
    Return the polynomial of the sums of permanental minors of ``A``.

    INPUT:

    - `A` -- a matrix

    - `permanent_only` -- if True, return only the permanent of `A`

    - `var` -- name of the polynomial variable

    - `prec` -- if prec is not None, truncate the polynomial at precision `prec`


    The polynomial of the sums of permanental minors is

    .. MATH::

        \sum_{i=0}^{min(nrows, ncols)} p_i(A) x^i

    where `p_i(A)` is the `i`-th permanental minor of `A` (that can also be
    obtained through the method
    :meth:`~sage.matrix.matrix2.Matrix.permanental_minor` via
    ``A.permanental_minor(i)``).

    The algorithm implemented by that function has been developed by P. Butera
    and M. Pernici, see [ButPer]. Its complexity is `O(2^n m^2 n)` where `m` and
    `n` are the number of rows and columns of `A`.  Moreover, if `A` is a banded
    matrix with width `w`, that is `A_{ij}=0` for `|i - j| > w` and `w < n/2`,
    then the complexity of the algorithm is `O(4^w (w+1) n^2)`.

    INPUT:

    - ``A`` -- matrix

    - ``permanent_only`` -- optional boolean. If ``True``, only the permanent
      is computed (might be faster).

    - ``var`` -- a variable name

    EXAMPLES::

        sage: from sage.matrix.matrix_misc import permanental_minor_polynomial
        sage: m = matrix([[1,1],[1,2]])
        sage: permanental_minor_polynomial(m)
        3*t^2 + 5*t + 1
        sage: permanental_minor_polynomial(m, permanent_only=True)
        3
        sage: permanental_minor_polynomial(m, prec=2)
        5*t + 1

    ::

        sage: M = MatrixSpace(ZZ,4,4)
        sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1])
        sage: permanental_minor_polynomial(A)
        84*t^3 + 114*t^2 + 28*t + 1
        sage: [A.permanental_minor(i) for i in range(5)]
        [1, 28, 114, 84, 0]

    An example over `\QQ`::

        sage: M = MatrixSpace(QQ,2,2)
        sage: A = M([1/5,2/7,3/2,4/5])
        sage: permanental_minor_polynomial(A, True)
        103/175

    An example with polynomial coefficients::

        sage: R.<a> = PolynomialRing(ZZ)
        sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]])
        sage: permanental_minor_polynomial(A, True)
        a^2 + 2*a

    A usage of the ``var`` argument::

        sage: m = matrix(ZZ,4,[0,1,2,3,1,2,3,0,2,3,0,1,3,0,1,2])
        sage: permanental_minor_polynomial(m, var='x')
        164*x^4 + 384*x^3 + 172*x^2 + 24*x + 1

    ALGORITHM:

        The permanent `perm(A)` of a `n \times n` matrix `A` is the coefficient
        of the `x_1 x_2 \ldots x_n` monomial in

        .. MATH::

            \prod_{i=1}^n \left( \sum_{j=1}^n A_{ij} x_j \right)

        Evaluating this product one can neglect `x_i^2`, that is `x_i`
        can be considered to be nilpotent of order `2`.

        To formalize this procedure, consider the algebra
        `R = K[\eta_1, \eta_2, \ldots, \eta_n]` where the `\eta_i` are
        commuting, nilpotent of order `2` (i.e. `\eta_i^2 = 0`).
        Formally it is the quotient ring of the polynomial
        ring in `\eta_1, \eta_2, \ldots, \eta_n` quotiented by the ideal
        generated by the `\eta_i^2`.

        We will mostly consider the ring `R[t]` of polynomials over `R`. We
        denote a generic element of `R[t]` by `p(\eta_1, \ldots, \eta_n)` or
        `p(\eta_{i_1}, \ldots, \eta_{i_k})` if we want to emphasize that some
        monomials in the `\eta_i` are missing.

        Introduce an "integration" operation `\langle p \rangle` over `R` and
        `R[t]` consisting in the sum of the coefficients of the non-vanishing
        monomials in `\eta_i` (i.e. the result of setting all variables `\eta_i`
        to `1`). Let us emphasize that this is *not* a morphism of algebras as
        `\langle \eta_1 \rangle^2 = 1` while `\langle \eta_1^2 \rangle = 0`!

        Let us consider an example of computation.
        Let `p_1 = 1 + t \eta_1 + t \eta_2` and
        `p_2 = 1 + t \eta_1 + t \eta_3`. Then

        .. MATH::

            p_1 p_2 = 1 + 2t \eta_1 +
                    t (\eta_2 + \eta_3) +
                    t^2 (\eta_1 \eta_2 + \eta_1 \eta_3 + \eta_2 \eta_3)

        and

        .. MATH::

            \langle p_1 p_2 \rangle = 1 + 4t + 3t^2

        In this formalism, the permanent is just

        .. MATH::

            perm(A) = \langle \prod_{i=1}^n \sum_{j=1}^n A_{ij} \eta_j \rangle

        A useful property of `\langle . \rangle` which makes this algorithm
        efficient for band matrices is the following: let
        `p_1(\eta_1, \ldots, \eta_n)` and `p_2(\eta_j, \ldots, \eta_n)` be
        polynomials in `R[t]` where `j \ge 1`.  Then one has

        .. MATH::

            \langle p_1(\eta_1, \ldots, \eta_n) p_2 \rangle =
            \langle p_1(1, \ldots, 1, \eta_j, \ldots, \eta_n) p_2 \rangle

        where `\eta_1,..,\eta_{j-1}` are replaced by `1` in `p_1`. Informally,
        we can "integrate" these variables *before* performing the product. More
        generally, if a monomial `\eta_i` is missing in one of the terms of a
        product of two terms, then it can be integrated in the other term.

        Now let us consider an `m \times n` matrix with `m \leq n`. The *sum of
        permanental `k`-minors of `A`* is

        .. MATH::

            perm(A, k) = \sum_{r,c} perm(A_{r,c})

        where the sum is over the `k`-subsets `r` of rows and `k`-subsets `c` of
        columns and `A_{r,c}` is the submatrix obtained from `A` by keeping only
        the rows `r` and columns `c`. Of course
        `perm(A, \min(m,n)) = perm(A)` and note that `perm(A,1)` is just the sum
        of all entries of the matrix.

        The generating function of these sums of permanental minors is

        .. MATH::

            g(t) = \left\langle
            \prod_{i=1}^m \left(1 + t \sum_{j=1}^n A_{ij} \eta_j\right)
            \right\rangle

        In fact the `t^k` coefficient of `g(t)` corresponds to choosing
        `k` rows of `A`;  `\eta_i` is associated to the i-th column;
        nilpotency avoids having twice the same column in a product of `A`'s.

        For more details, see the article [ButPer].

        From a technical point of view, the product in
        `K[\eta_1, \ldots, \eta_n][t]` is implemented as a subroutine in
        :func:`prm_mul`. The indices of the rows and columns actually start at
        `0`, so the variables are  `\eta_0, \ldots, \eta_{n-1}`. Polynomials are
        represented in dictionary form: to a variable `\eta_i` is associated
        the key `2^i` (or in Python ``1 << i``). The keys associated to products
        are obtained by considering the development in base `2`: to the monomial
        `\eta_{i_1} \ldots \eta_{i_k}` is associated the key
        `2^{i_1} + \ldots + 2^{i_k}`. So the product `\eta_1 \eta_2` corresponds
        to the key `6 = (110)_2` while `\eta_0 \eta_3` has key `9 = (1001)_2`.
        In particular all operations on monomials are implemented via bitwise
        operations on the keys.

    REFERENCES:

    .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors
       using Grassmann algebra", :arxiv:`1406.5337`
    """
    if permanent_only:
        prec = None
    elif prec is not None:
        prec = int(prec)
        if prec == 0:
            raise ValueError('the argument `prec` must be a positive integer')

    K = PolynomialRing(A.base_ring(), var)
    nrows = A.nrows()
    ncols = A.ncols()
    A = A.rows()
    p = {0: K.one()}
    t = K.gen()
    vars_to_do = range(ncols)
    for i in range(nrows):
        # build the polynomial p1 = 1 + t sum A_{ij} eta_j
        if permanent_only:
            p1 = {}
        else:
            p1 = {0: K.one()}
        a = A[i]   # the i-th row of A
        for j in range(len(a)):
            if a[j]:
                p1[1<<j] = a[j] * t

        # make the product with the preceding polynomials, taking care of
        # variables that can be integrated
        mask_free = 0
        j = 0
        while j < len(vars_to_do):
            jj = vars_to_do[j]
            if all(A[k][jj] == 0 for k in range(i+1, nrows)):
                mask_free += 1 << jj
                vars_to_do.remove(jj)
            else:
                j += 1
        p = prm_mul(p, p1, mask_free, prec)

    if not p:
        return K.zero()

    if len(p) != 1 or 0 not in p:
        raise RuntimeError("Something is wrong! Certainly a problem in the"
                           " algorithm... please contact [email protected]")

    p = p[0]
    return p[min(nrows,ncols)] if permanent_only else p
示例#15
0
文件: base_ZZ.py 项目: ozzie00/sage
    def ehrhart_polynomial(self,
                           verbose=False,
                           dual=None,
                           irrational_primal=None,
                           irrational_all_primal=None,
                           maxdet=None,
                           no_decomposition=None,
                           compute_vertex_cones=None,
                           smith_form=None,
                           dualization=None,
                           triangulation=None,
                           triangulation_max_height=None,
                           **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        INPUT:

        - ``verbose`` - (boolean, default to ``False``) if ``True``, print the
          whole output of the LattE command.

        The following options are passed to the LattE command, for details you
        should consult `the LattE documentation
        <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

        - ``dual`` - (boolean) triangulate and signed-decompose in the dual
          space

        - ``irrational_primal`` - (boolean) triangulate in the dual space,
          signed-decompose in the primal space using irrationalization.

        - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose
          in the primal space using irrationalization.

        - ``maxdet`` -- (integer) decompose down to an index (determinant) of
          ``maxdet`` instead of index 1 (unimodular cones).

        - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones.

        - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2'

        - ``smith_form`` -- (string) either 'ilio' or 'lidia'

        - ``dualization`` -- (string) either 'cdd' or '4ti2'

        - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom'

        - ``triangulation_max_height`` - (integer) use a uniform distribution of
          height from 1 to this number

        .. NOTE::

            Any additional argument is forwarded to LattE's executable
            ``count``. All occurrences of '_' will be replaced with a '-'.

        ALGORITHM:

        This method calls the program ``count`` from LattE integrale, a program
        for lattice point enumeration (see
        https://www.math.ucdavis.edu/~latte/).

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE integrale

        EXAMPLES::

            sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: p = P.ehrhart_polynomial()    # optional - latte_int
            sage: p                             # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: p(1)                          # optional - latte_int
            6
            sage: len(P.integral_points())
            6
            sage: p(2)                          # optional - latte_int
            36
            sage: len((2*P).integral_points())
            36

        The unit hypercubes::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: P = Polyhedron(ambient_dim=3, vertices=[])
            sage: P.ehrhart_polynomial()    # optional - latte_int
            0
            sage: parent(_)                 # optional - latte_int
            Univariate Polynomial Ring in t over Rational Field

        TESTS:

        Test options::

            sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)

            sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin
            ...
            sage: p    # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(dual=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True)   # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

        Test bad options::

            sage: P.ehrhart_polynomial(bim_bam_boum=19)   # optional - latte_int
            Traceback (most recent call last):
            ...
            RuntimeError: LattE integrale program failed (exit code 1):
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin
            Unknown command/option --bim-bam-boum=19
        """
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, 't')
            return R.zero()

        # note: the options below are explicitely written in the function
        # declaration in order to keep tab completion (see #18211).
        kwds.update({
            'dual': dual,
            'irrational_primal': irrational_primal,
            'irrational_all_primal': irrational_all_primal,
            'maxdet': maxdet,
            'no_decomposition': no_decomposition,
            'compute_vertex_cones': compute_vertex_cones,
            'smith_form': smith_form,
            'dualization': dualization,
            'triangulation': triangulation,
            'triangulation_max_height': triangulation_max_height
        })

        from sage.interfaces.latte import count
        ine = self.cdd_Hrepresentation()
        return count(ine,
                     cdd=True,
                     ehrhart_polynomial=True,
                     verbose=verbose,
                     **kwds)
示例#16
0
    def ehrhart_polynomial(self,
                           verbose=False,
                           dual=None,
                           irrational_primal=None,
                           irrational_all_primal=None,
                           maxdet=None,
                           no_decomposition=None,
                           compute_vertex_cones=None,
                           smith_form=None,
                           dualization=None,
                           triangulation=None,
                           triangulation_max_height=None,
                           **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        INPUT:

        - ``verbose`` - (boolean, default to ``False``) if ``True``, print the
          whole output of the LattE command.

        The following options are passed to the LattE command, for details you
        should consult `the LattE documentation
        <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

        - ``dual`` - (boolean) triangulate and signed-decompose in the dual
          space

        - ``irrational_primal`` - (boolean) triangulate in the dual space,
          signed-decompose in the primal space using irrationalization.

        - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose
          in the primal space using irrationalization.

        - ``maxdet`` -- (integer) decompose down to an index (determinant) of
          ``maxdet`` instead of index 1 (unimodular cones).

        - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones.

        - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2'

        - ``smith_form`` -- (string) either 'ilio' or 'lidia'

        - ``dualization`` -- (string) either 'cdd' or '4ti2'

        - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom'

        - ``triangulation_max_height`` - (integer) use a uniform distribution of
          height from 1 to this number

        .. NOTE::

            Any additional argument is forwarded to LattE's executable
            ``count``. All occurrences of '_' will be replaced with a '-'.

        ALGORITHM:

        This method calls the program ``count`` from LattE integrale, a program
        for lattice point enumeration (see
        https://www.math.ucdavis.edu/~latte/).

        EXAMPLES::

            sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: p = P.ehrhart_polynomial()    # optional - latte_int
            sage: p                             # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: p(1)                          # optional - latte_int
            6
            sage: len(P.integral_points())
            6
            sage: p(2)                          # optional - latte_int
            36
            sage: len((2*P).integral_points())
            36

        The unit hypercubes::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: P = Polyhedron(ambient_dim=3, vertices=[])
            sage: P.ehrhart_polynomial()    # optional - latte_int
            0
            sage: parent(_)                 # optional - latte_int
            Univariate Polynomial Ring in t over Rational Field

        TESTS:

        Test options::

            sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)

            sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' '--maxdet=5' --cdd ...
            ...
            sage: p    # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(dual=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --dual --cdd ...
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True)   # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-primal --cdd ...
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-all-primal --cdd ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

        Test bad options::

            sage: P.ehrhart_polynomial(bim_bam_boum=19)   # optional - latte_int
            Traceback (most recent call last):
            ...
            RuntimeError: LattE integrale failed with exit code 1 to execute...
        """
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        R = PolynomialRing(QQ, 't')
        if self.is_empty():
            return R.zero()

        from sage.misc.misc import SAGE_TMP
        from subprocess import Popen, PIPE

        ine = self.cdd_Hrepresentation()

        args = ['count', '--ehrhart-polynomial']
        if 'redundancy_check' not in kwds:
            args.append('--redundancy-check=none')

        # note: the options below are explicitely written in the function
        # declaration in order to keep tab completion (see #18211).
        kwds.update({
            'dual': dual,
            'irrational_primal': irrational_primal,
            'irrational_all_primal': irrational_all_primal,
            'maxdet': maxdet,
            'no_decomposition': no_decomposition,
            'compute_vertex_cones': compute_vertex_cones,
            'smith_form': smith_form,
            'dualization': dualization,
            'triangulation': triangulation,
            'triangulation_max_height': triangulation_max_height
        })

        for key, value in kwds.items():
            if value is None or value is False:
                continue

            key = key.replace('_', '-')
            if value is True:
                args.append('--{}'.format(key))
            else:
                args.append('--{}={}'.format(key, value))
        args += ['--cdd', '/dev/stdin']

        try:
            # The cwd argument is needed because latte
            # always produces diagnostic output files.
            latte_proc = Popen(args,
                               stdin=PIPE,
                               stdout=PIPE,
                               stderr=(None if verbose else PIPE),
                               cwd=str(SAGE_TMP))
        except OSError:
            from sage.misc.package import PackageNotFoundError
            raise PackageNotFoundError('latte_int')

        ans, err = latte_proc.communicate(ine)
        ret_code = latte_proc.poll()
        if ret_code:
            if err is None:
                err = ", see error message above"
            else:
                err = ":\n" + err
            raise RuntimeError(
                "LattE integrale failed with exit code {} to execute {}".
                format(ret_code, ' '.join(args)) + err.strip())

        p = ans.splitlines()[-2]

        return R(p)
示例#17
0
文件: base_ZZ.py 项目: Babyll/sage
    def ehrhart_polynomial(self, verbose=False, dual=None,
            irrational_primal=None, irrational_all_primal=None, maxdet=None,
            no_decomposition=None, compute_vertex_cones=None, smith_form=None,
            dualization=None, triangulation=None, triangulation_max_height=None,
            **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        INPUT:

        - ``verbose`` - (boolean, default to ``False``) if ``True``, print the
          whole output of the LattE command.

        The following options are passed to the LattE command, for details you
        should consult `the LattE documentation
        <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

        - ``dual`` - (boolean) triangulate and signed-decompose in the dual
          space

        - ``irrational_primal`` - (boolean) triangulate in the dual space,
          signed-decompose in the primal space using irrationalization.

        - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose
          in the primal space using irrationalization.

        - ``maxdet`` -- (integer) decompose down to an index (determinant) of
          ``maxdet`` instead of index 1 (unimodular cones).

        - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones.

        - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2'

        - ``smith_form`` -- (string) either 'ilio' or 'lidia'

        - ``dualization`` -- (string) either 'cdd' or '4ti2'

        - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom'

        - ``triangulation_max_height`` - (integer) use a uniform distribution of
          height from 1 to this number

        .. NOTE::

            Any additional argument is forwarded to LattE's executable
            ``count``. All occurrences of '_' will be replaced with a '-'.

        ALGORITHM:

        This method calls the program ``count`` from LattE integrale, a program
        for lattice point enumeration (see
        https://www.math.ucdavis.edu/~latte/).

        EXAMPLES::

            sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: p = P.ehrhart_polynomial()    # optional - latte_int
            sage: p                             # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: p(1)                          # optional - latte_int
            6
            sage: len(P.integral_points())
            6
            sage: p(2)                          # optional - latte_int
            36
            sage: len((2*P).integral_points())
            36

        The unit hypercubes::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: P = Polyhedron(ambient_dim=3, vertices=[])
            sage: P.ehrhart_polynomial()    # optional - latte_int
            0
            sage: parent(_)                 # optional - latte_int
            Univariate Polynomial Ring in t over Rational Field

        TESTS:

        Test options::

            sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)

            sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' '--maxdet=5' --cdd ...
            ...
            sage: p    # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(dual=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --dual --cdd ...
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True)   # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-primal --cdd ...
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --irrational-all-primal --cdd ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

        Test bad options::

            sage: P.ehrhart_polynomial(bim_bam_boum=19)   # optional - latte_int
            Traceback (most recent call last):
            ...
            RuntimeError: LattE integrale failed with exit code 1 to execute...
        """
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        R = PolynomialRing(QQ, 't')
        if self.is_empty():
            return R.zero()

        from sage.misc.misc import SAGE_TMP
        from subprocess import Popen, PIPE

        ine = self.cdd_Hrepresentation()

        args = ['count', '--ehrhart-polynomial']
        if 'redundancy_check' not in kwds:
            args.append('--redundancy-check=none')

        # note: the options below are explicitely written in the function
        # declaration in order to keep tab completion (see #18211).
        kwds.update({
            'dual'                    : dual,
            'irrational_primal'       : irrational_primal,
            'irrational_all_primal'   : irrational_all_primal,
            'maxdet'                  : maxdet,
            'no_decomposition'        : no_decomposition,
            'compute_vertex_cones'    : compute_vertex_cones,
            'smith_form'              : smith_form,
            'dualization'             : dualization,
            'triangulation'           : triangulation,
            'triangulation_max_height': triangulation_max_height})

        for key,value in kwds.items():
            if value is None or value is False:
                continue

            key = key.replace('_','-')
            if value is True:
                args.append('--{}'.format(key))
            else:
                args.append('--{}={}'.format(key, value))
        args += ['--cdd', '/dev/stdin']

        try:
            # The cwd argument is needed because latte
            # always produces diagnostic output files.
            latte_proc = Popen(args,
                               stdin=PIPE, stdout=PIPE,
                               stderr=(None if verbose else PIPE),
                               cwd=str(SAGE_TMP))
        except OSError:
            from sage.misc.package import PackageNotFoundError
            raise PackageNotFoundError('latte_int')

        ans, err = latte_proc.communicate(ine)
        ret_code = latte_proc.poll()
        if ret_code:
            if err is None:
                err = ", see error message above"
            else:
                err = ":\n" + err
            raise RuntimeError("LattE integrale failed with exit code {} to execute {}".format(ret_code, ' '.join(args)) + err.strip())

        p = ans.splitlines()[-2]

        return R(p)
示例#18
0
    def ehrhart_quasipolynomial(self, variable='t', engine=None, verbose=False,
            dual=None, irrational_primal=None, irrational_all_primal=None,
            maxdet=None, no_decomposition=None, compute_vertex_cones=None,
            smith_form=None, dualization=None, triangulation=None,
            triangulation_max_height=None, **kwds):
        r"""
        Compute the Ehrhart quasipolynomial of this polyhedron with rational
        vertices.

        If the polyhedron is a lattice polytope, returns the Ehrhart polynomial,
        a univariate polynomial in ``variable`` over a rational field.
        If the polyhedron  has rational, nonintegral vertices, returns a tuple
        of polynomials in ``variable`` over a rational field.
        The Ehrhart counting function of a polytope `P` with rational
        vertices is given by a *quasipolynomial*. That is, there exists a
        positive integer `l` and `l` polynomials
        `ehr_{P,i} \text{ for } i \in \{1,\dots,l \}` such that if `t` is
        equivalent to `i` mod `l` then `tP \cap \mathbb Z^d = ehr_{P,i}(t)`.

        INPUT:

        - ``variable`` -- string (default: 't'); The variable in which the
          Ehrhart polynomial should be expressed.

        - ``engine`` -- string; The backend to use. Allowed values are:

          * ``None`` (default); When no input is given the Ehrhart polynomial
            is computed using Normaliz (optional)
          * ``'latte'``; use LattE Integrale program (requires optional package
            'latte_int')
          * ``'normaliz'``; use the Normaliz program (requires optional package
            'pynormaliz'). The backend of ``self`` must be set to 'normaliz'.

        - When the ``engine`` is 'latte', the additional input values are:

          * ``verbose`` - boolean (default: ``False``); If ``True``, print the
            whole output of the LattE command.

          The following options are passed to the LattE command, for details
          consult `the LattE documentation
          <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

          * ``dual`` - boolean; triangulate and signed-decompose in the dual
            space
          * ``irrational_primal`` - boolean; triangulate in the dual space,
            signed-decompose in the primal space using irrationalization.
          * ``irrational_all_primal`` - boolean; triangulate and signed-decompose
            in the primal space using irrationalization.
          * ``maxdet`` -- integer; decompose down to an index (determinant) of
            ``maxdet`` instead of index 1 (unimodular cones).
          * ``no_decomposition`` -- boolean; do not signed-decompose
            simplicial cones.
          * ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
          * ``smith_form`` -- string; either 'ilio' or 'lidia'
          * ``dualization`` -- string; either 'cdd' or '4ti2'
          * ``triangulation`` - string; 'cddlib', '4ti2' or 'topcom'
          * ``triangulation_max_height`` - integer; use a uniform distribution of
            height from 1 to this number

        OUTPUT:

        A univariate polynomial over a rational field or a tuple of such
        polynomials.

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE Integrale
            `PyNormaliz <https://pypi.org/project/PyNormaliz>`_

        .. WARNING::

            If the polytope has rational, non integral vertices,
            it must have ``backend='normaliz'``.

        EXAMPLES:

        As a first example, consider the line segment [0,1/2]. If we
        dilate this line segment by an even integral factor `k`,
        then the dilated line segment will contain `k/2 +1` lattice points.
        If `k` is odd then there will be `k/2+1/2` lattice points in
        the dilated line segment. Note that it is necessary to set the
        backend of the polytope to 'normaliz'::

            sage: line_seg = Polyhedron(vertices=[[0],[1/2]],backend='normaliz') # optional - pynormaliz
            sage: line_seg                                                       # optional - pynormaliz
            A 1-dimensional polyhedron in QQ^1 defined as the convex hull of 2 vertices
            sage: line_seg.ehrhart_quasipolynomial()                             # optional - pynormaliz
            (1/2*t + 1, 1/2*t + 1/2)

        For a more exciting example, let us look at the subpolytope of the
        3 dimensional permutahedron fixed by the reflection
        across the hyperplane `x_1 = x_4`::

            sage: verts = [[3/2, 3, 4, 3/2],
            ....:  [3/2, 4, 3, 3/2],
            ....:  [5/2, 1, 4, 5/2],
            ....:  [5/2, 4, 1, 5/2],
            ....:  [7/2, 1, 2, 7/2],
            ....:  [7/2, 2, 1, 7/2]]
            sage: subpoly = Polyhedron(vertices=verts, backend='normaliz') # optional - pynormaliz
            sage: eq = subpoly.ehrhart_quasipolynomial()    # optional - pynormaliz
            sage: eq                                        # optional - pynormaliz
            (4*t^2 + 3*t + 1, 4*t^2 + 2*t)
            sage: eq = subpoly.ehrhart_quasipolynomial()    # optional - pynormaliz
            sage: eq                                        # optional - pynormaliz
            (4*t^2 + 3*t + 1, 4*t^2 + 2*t)
            sage: even_ep = eq[0]                           # optional - pynormaliz
            sage: odd_ep  = eq[1]                           # optional - pynormaliz
            sage: even_ep(2)                                # optional - pynormaliz
            23
            sage: ts = 2*subpoly                            # optional - pynormaliz
            sage: ts.integral_points_count()                # optional - pynormaliz latte_int
            23
            sage: odd_ep(1)                                 # optional - pynormaliz
            6
            sage: subpoly.integral_points_count()           # optional - pynormaliz latte_int
            6

        A polytope with rational nonintegral vertices must have
        ``backend='normaliz'``::

            sage: line_seg = Polyhedron(vertices=[[0],[1/2]])
            sage: line_seg.ehrhart_quasipolynomial()
            Traceback (most recent call last):
            ...
            TypeError: The backend of the polyhedron should be 'normaliz'

        The polyhedron should be compact::

            sage: C = Polyhedron(backend='normaliz',rays=[[1/2,2],[2,1]])  # optional - pynormaliz
            sage: C.ehrhart_quasipolynomial()                              # optional - pynormaliz
            Traceback (most recent call last):
            ...
            ValueError: Ehrhart quasipolynomial only defined for compact polyhedra

        If the polytope happens to be a lattice polytope, the Ehrhart
        polynomial is returned::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz
            sage: simplex = simplex.change_ring(QQ)                                                       # optional - pynormaliz
            sage: poly = simplex.ehrhart_quasipolynomial(engine='normaliz')                               # optional - pynormaliz
            sage: poly                                                                                    # optional - pynormaliz
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: simplex.ehrhart_polynomial()                                                            # optional - pynormaliz latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1

        TESTS:

        The cache of the Ehrhart quasipolynomial is being pickled::

            sage: P = polytopes.cuboctahedron(backend='normaliz')/2           # optional - pynormaliz
            sage: P.ehrhart_quasipolynomial()                                 # optional - pynormaliz
            (5/6*t^3 + 2*t^2 + 5/3*t + 1, 5/6*t^3 + 1/2*t^2 + 1/6*t - 1/2)
            sage: Q = loads(dumps(P))                                         # optional - pynormaliz
            sage: Q.ehrhart_quasipolynomial.is_in_cache()                     # optional - pynormaliz
            True

            sage: P = polytopes.cuboctahedron().change_ring(QQ)               # optional - latte_int
            sage: P.ehrhart_quasipolynomial(engine='latte')                   # optional - latte_int
            20/3*t^3 + 8*t^2 + 10/3*t + 1
            sage: Q = loads(dumps(P))                                         # optional - latte_int
            sage: Q.ehrhart_quasipolynomial.is_in_cache(engine='latte')       # optional - latte_int
            True
        """
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, 't')
            return R.zero()

        if not self.is_compact():
            raise ValueError("Ehrhart quasipolynomial only defined for compact polyhedra")

        if engine is None:
            # setting the default to 'normaliz'
            engine = 'normaliz'
        if engine == 'normaliz':
            return self._ehrhart_quasipolynomial_normaliz(variable)
        if engine == 'latte':
            if any(not v.is_integral() for v in self.vertex_generator()):
                raise TypeError("the polytope has nonintegral vertices, the engine and backend of self should be 'normaliz'")
            poly = self._ehrhart_polynomial_latte(verbose, dual,
            irrational_primal, irrational_all_primal, maxdet,
            no_decomposition, compute_vertex_cones, smith_form,
            dualization, triangulation, triangulation_max_height,
            **kwds)
            return poly.change_variable_name(variable)
            # TO DO: replace this change of variable by creating the appropriate
            #        polynomial ring in the latte interface.
        else:
            raise TypeError("the engine should be 'latte' or 'normaliz'")
示例#19
0
def rand_pham_divisor(n, var="z"):
    poly_ring = PolynomialRing(QQ, n, var)
    div = poly_ring.zero()
    for g in poly_ring.gens():
        div += g**(randrange(2, 9))
    return div
示例#20
0
class DifferentialPolynomialRing:
    element_class = DifferentialPolynomial

    def __init__(self, base_ring, fibre_names, base_names,
                 max_differential_orders):
        self._fibre_names = tuple(fibre_names)
        self._base_names = tuple(base_names)
        self._max_differential_orders = tuple(max_differential_orders)
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        jet_names = []
        idx_to_name = {}
        for fibre_idx in range(fibre_dim):
            u = self._fibre_names[fibre_idx]
            idx_to_name[(fibre_idx, ) + tuple([0] * base_dim)] = u
            for d in range(1, max_differential_orders[fibre_idx] + 1):
                for multi_index in IntegerVectors(d, base_dim):
                    v = '{}_{}'.format(
                        u, ''.join(self._base_names[i] * multi_index[i]
                                   for i in range(base_dim)))
                    jet_names.append(v)
                    idx_to_name[(fibre_idx, ) + tuple(multi_index)] = v
        self._polynomial_ring = PolynomialRing(
            base_ring, base_names + fibre_names + tuple(jet_names))
        self._idx_to_var = {
            idx: self._polynomial_ring(idx_to_name[idx])
            for idx in idx_to_name
        }
        self._var_to_idx = {
            jet: idx
            for (idx, jet) in self._idx_to_var.items()
        }
        # for conversion:
        base_vars = [var(b) for b in self._base_names]
        symbolic_functions = [
            function(f)(*base_vars) for f in self._fibre_names
        ]
        self._subs_jet_vars = SubstituteJetVariables(symbolic_functions)
        self._subs_tot_ders = SubstituteTotalDerivatives(symbolic_functions)

    def __repr__(self):
        return 'Differential Polynomial Ring in {} over {}'.format(
            ', '.join(map(repr, self._polynomial_ring.gens())),
            self._polynomial_ring.base_ring())

    def _latex_(self):
        return self._polynomial_ring._latex_()

    def base_ring(self):
        return self._polynomial_ring.base_ring()

    def _first_ngens(self, n):
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(n))

    def gens(self):
        return self._first_ngens(self._polynomial_ring.ngens())

    def gen(self, i):
        return self.element_class(self, self._polynomial_ring.gen(i))

    def base_variables(self):
        return self._first_ngens(len(self._base_names))

    def base_dim(self):
        return len(self._base_names)

    def fibre_variable(self, i):
        return self.element_class(
            self, self._polynomial_ring.gen(len(self._base_names) + i))

    def fibre_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(base_dim + i))
            for i in range(fibre_dim))

    def fibre_dim(self):
        return len(self._fibre_names)

    def jet_variables(self):
        base_dim = len(self._base_names)
        fibre_dim = len(self._fibre_names)
        whole_dim = self._polynomial_ring.ngens()
        return tuple(
            self.element_class(self, self._polynomial_ring.gen(i))
            for i in range(base_dim + fibre_dim, whole_dim))

    def max_differential_orders(self):
        return self._max_differential_orders

    def _single_var_weights(self, u):
        return self._var_to_idx[u][1:]

    def _diff_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        du_idx = list(u_idx)
        du_idx[1 + x_idx] += 1
        du_idx = tuple(du_idx)
        if du_idx in self._idx_to_var:
            return self._idx_to_var[du_idx]
        else:
            raise ValueError(
                "can't differentiate {} any further with respect to {}".format(
                    u, x))

    def _integrate_single_var(self, u, x):
        x_idx = self._polynomial_ring.gens().index(x)
        u_idx = self._var_to_idx[u]
        if u_idx[1 + x_idx] == 0:
            raise ValueError(
                "can't integrate {} any further with respect to {}".format(
                    u, x))
        iu_idx = list(u_idx)
        iu_idx[1 + x_idx] -= 1
        iu_idx = tuple(iu_idx)
        return self._idx_to_var[iu_idx]

    def __contains__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return True
        if arg in self._polynomial_ring.base_ring():
            return True
        return False

    def __call__(self, arg):
        if isinstance(arg, self.element_class) and arg.parent() is self:
            return arg
        if is_Expression(arg):
            arg = self._subs_jet_vars(arg)
        return self.element_class(self, self._polynomial_ring(arg))

    def zero(self):
        return self.element_class(self, self._polynomial_ring.zero())

    def one(self):
        return self.element_class(self, self._polynomial_ring.one())

    def homogeneous_monomials(self,
                              fibre_degrees,
                              weights,
                              max_differential_orders=None):
        fibre_vars = self.fibre_variables()
        if not len(fibre_degrees) == len(fibre_vars):
            raise ValueError(
                'length of fibre_degrees vector must match number of fibre variables'
            )
        base_vars = self.base_variables()
        if not len(weights) == len(base_vars):
            raise ValueError(
                'length of weights vector must match number of base variables')
        monomials = []
        fibre_degree = sum(fibre_degrees)
        fibre_indexes = {}
        fibre_idx = 0
        for i in range(len(fibre_degrees)):
            for j in range(fibre_degrees[i]):
                fibre_indexes[fibre_idx] = i
                fibre_idx += 1
        proto = sum([[fibre_vars[i]] * fibre_degrees[i]
                     for i in range(len(fibre_degrees))], [])
        for V in product(*[IntegerVectors(w, fibre_degree) for w in weights]):
            total_differential_order = [0 for i in range(fibre_degree)]
            term = [p for p in proto]
            skip = False
            for j in range(fibre_degree):
                fibre_idx = fibre_indexes[j]
                for i in range(len(base_vars)):
                    if V[i][j] > 0:
                        total_differential_order[j] += V[i][j]
                        if max_differential_orders is not None and total_differential_order[
                                j] > max_differential_orders[fibre_idx]:
                            skip = True
                            break
                        term[j] = term[j].total_derivative(*([base_vars[i]] *
                                                             V[i][j]))
                if skip:
                    break
            if not skip:
                monomials.append(prod(term))
        return monomials
示例#21
0
class TestSingularModule(unittest.TestCase):
    def setUp(self):
        self.poly_ring = PolynomialRing(QQ, "x", 3)
        self.x = self.poly_ring.gens()[0]
        self.y = self.poly_ring.gens()[1]
        self.z = self.poly_ring.gens()[2]

    def test_creation(self):
        x = self.x
        y = self.y
        z = self.z
        SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        #Check this did cause an error
        self.assertTrue(True)

    def test_create_ring_str(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        self.assertEqual(sm.create_ring_str(), "ring r=0,x(1..3),dp;\n")

    def test_create_module_str(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x, 2 * y**2, 3 * z**3]])
        self.assertEqual(
            sm.create_module_str("MT"),
            "module MT=[1*x(1)^1,2*x(2)^2,3*x(3)^3];\n groebner(MT);\n")

    def test_contains_zero(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        zero = self.poly_ring.zero()
        #Assert we always contain zero
        self.assertTrue(sm.contains([zero, zero, zero]))

    def test_contains_gen(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        #Assert we contain our generators
        self.assertTrue(sm.contains([x, y + z, z**3 - 2 * y]))
        self.assertTrue(sm.contains([x, y, z]))

    def test_contains_combination(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        #Assert we contain a combination of the generorators
        self.assertTrue(sm.contains([2 * x, 2 * y + z, z**3 - 2 * y + z]))

    def test_contains_fail(self):
        x = self.x
        y = self.y
        z = self.z
        one = self.poly_ring.one()
        sm = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        #Detect that certain vectors are not contained
        self.assertFalse(sm.contains([one, x, z]))
        self.assertFalse(sm.contains([y, y, y]))
        self.assertFalse(sm.contains([z, y, x]))

    def test_contains_fail_multiple(self):
        x = self.x
        y = self.y
        z = self.z
        sm = SingularModule([[x**2, x * y, x * z]])
        #Detect that this is not contianed even though a multiple is
        self.assertFalse(sm.contains([x, y, z]))

    def test_contains_module(self):
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        smA = SingularModule([[x**2, x * y, y * z], [x, y, z]])
        smB = SingularModule([[x**2 + x, x * y + y, y * z + z],
                              [zero, zero, y * z - x * z]])
        self.assertTrue(smA.contains(smB))

    def test_contained_in_ambient(self):
        x = self.x
        y = self.y
        z = self.z
        smA = SingularModule([[x, y**3, z + x], [z, x, x**2]])
        am = smA.ambient_free_module()
        self.assertTrue(am.contains(smA))

    def test_intersect(self):
        x = self.x
        y = self.y
        z = self.z
        smA = SingularModule([[x, y**3, z + x], [z, x, x**2]])
        smB = SingularModule([[z, y, x], [-z, x**2, 4 * y + z]])
        smI = smA.intersection(smB)
        gen_1_1 = x**5 * z + x * y**3 * z**2 + 4 * y**4 * z**2 + y**3 * z**3 + x**3 * y * z - x**3 * z**2 - x**2 * z**3 - x**3 * z - 4 * x**2 * y * z - x**2 * z**2 - x * y * z**2 - y * z**3
        gen_2_1 = x**4 * y**3 * z - x**3 * y**3 * z + x**2 * y**4 * z + 4 * y**5 * z + y**4 * z**2 + x**5 - x**4 * z - x**3 * z**2 - 4 * x**2 * y**2 - 2 * x**2 * y * z - x * y * z**2
        gen_3_1 = x**3 * y**3 * z - x**3 * y * z + 4 * x**2 * y**4 * z + x**2 * y**3 * z**2 + x**6 - 4 * x**3 * y**2 - x**4 * z - x**3 * z**2 - x**3 * z - 4 * x**2 * y * z + 4 * x * y**2 * z - 2 * x**2 * z**2 - 3 * x * y * z**2 + 4 * y**2 * z**2 - x * z**3 + y * z**3
        gens = [[gen_1_1, gen_2_1, gen_3_1]]
        #Test this example
        self.assertEqual(gens, smI.gens)

    def test_intersect_contains(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2 = SingularModule([[x, y**2, z**3]])
        #Assert the intersection is contained in both modules
        gens = (sm1.intersection(sm2)).gens
        for gen in gens:
            self.assertTrue(sm1.contains(gen))
        for gen in gens:
            self.assertTrue(sm2.contains(gen))

    def test_intersection_symetry(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2 = SingularModule([[x, y**2, z**3]])
        sm_1_2 = sm1.intersection(sm2)
        sm_2_1 = sm2.intersection(sm1)
        #Assert that equals is symetric
        self.assertTrue(sm_1_2.equals(sm_2_1))

    def test_reduce_lossless(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2.reduce_generators()
        self.assertTrue(sm1.equals(sm2))

    def test_standard_basis_irredundent(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        std_gens = sm1.standard_basis()
        std_mod = SingularModule(std_gens)
        self.assertTrue(sm1.equals(std_mod))

    def test_equals_A(self):
        x = self.x
        y = self.y
        z = self.z
        #Assert these are equal
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2 = SingularModule([[x, y, z], [x, y + z, z**3 - 2 * y],
                              [x, y, z**2]])
        self.assertTrue(sm1.equals(sm2))

    def test_equals_B(self):
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        #Assert these are equal - from crossing divisor
        sm1 = SingularModule([[x, zero, zero], [zero, y, zero],
                              [zero, zero, z]])
        sm2 = SingularModule([[zero, y, z], [zero, zero, z], [x, zero, zero]])
        self.assertTrue(sm1.equals(sm2))

    def test_equals_not(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        sm2 = SingularModule([[x, y, z]])
        #Assert these are not equal
        self.assertFalse(sm1.equals(sm2))

    def test_ambient_free_module(self):
        x = self.x
        y = self.y
        z = self.z
        one = self.poly_ring.one()
        zero = self.poly_ring.zero()
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2]])
        self.assertEqual(
            sm1.ambient_free_module().gens,
            [[one, zero, zero], [zero, one, zero], [zero, zero, one]])

    def test_is_free_trivial(self):
        free = SingularModule.create_free_module(3, self.poly_ring)
        self.assertTrue(free.is_free())

    def test_is_free(self):
        x = self.x
        y = self.y
        zero = self.poly_ring.zero()
        free = SingularModule([[x, zero, zero], [zero, y, zero],
                               [zero, zero, x**2]])
        self.assertTrue(free.is_free())

    def test_is_free_not(self):
        x = self.x
        y = self.y
        sm1 = SingularModule([[x, x, x], [y, y, y]])
        self.assertFalse(sm1.is_free())

    def test_create_relationA(self):
        x = self.x
        y = self.y
        z = self.z
        one = self.poly_ring.one()
        zero = self.poly_ring.zero()
        relation = [x**2, one + z**2, y]
        ideal = Ideal(self.poly_ring, [x**2 * y - z**2])
        mod = SingularModule.create_from_relation(relation, ideal)
        true_mod = SingularModule([[one, -x**2, x**4], [zero, y, -z**2 - 1],
                                   [zero, z**2, -x**2 * z**2 - x**2],
                                   [zero, zero, x**2 * y - z**2]])
        self.assertTrue(mod.equals(true_mod))

    def test_create_relationB(self):
        #From an error uncovered in log derivations
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        relation = [y * z, x * z, x * y]
        ideal = Ideal(self.poly_ring, [x * y * z])
        mod = SingularModule.create_from_relation(relation, ideal)
        true_mod = SingularModule([[x, zero, zero], [zero, y, zero],
                                   [zero, zero, z]])
        self.assertTrue(mod.equals(true_mod))

    def test_create_relation_satisfy_A(self):
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        relation = [x + x**4 - y, (y + z)**3, -2 * y + self.poly_ring.one()]
        ideal = Ideal(self.poly_ring, [x**2 * y - z**2])
        mod = SingularModule.create_from_relation(relation, ideal)
        for gen in mod.gens:
            sum = zero
            for g, rel in zip(gen, relation):
                sum = sum + g * rel
        self.assertTrue(sum in ideal)

    def test_create_relation_satisfy_B(self):
        x = self.x
        y = self.y
        z = self.z
        one = self.poly_ring.one()
        zero = self.poly_ring.zero()
        relation = [x**2, one + z**2, y]
        ideal = Ideal(self.poly_ring, [x**2 * y - z**2])
        mod = SingularModule.create_from_relation(relation, ideal)
        for gen in mod.gens:
            sum = zero
            for g, rel in zip(gen, relation):
                sum = sum + g * rel
        self.assertTrue(sum in ideal)

    def test_create_relations_satisfy(self):
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        relations = [[x**2, z * y], [y**2, -x]]
        ideals = [
            Ideal(self.poly_ring, [x**2 * y - z**2]),
            Ideal(self.poly_ring, [x * y - z])
        ]
        mod = SingularModule.create_from_relations(relations, ideals)
        for relation, ideal in zip(relations, ideals):
            for gen in mod.gens:
                sum = zero
                for g, rel in zip(gen, relation):
                    sum = sum + g * rel
            self.assertTrue(sum in ideal)

    def test_create_singular_free(self):
        free_matrix = "MM[1,1]=1\nMM[1,2]=0\nMM[1,3]=0\n"
        free_matrix = free_matrix + "MM[2,1]=0\nMM[2,2]=1\nMM[2,3]=0\n"
        free_matrix = free_matrix + "MM[3,1]=0\nMM[3,2]=0\nMM[3,3]=1\n"
        free_c = SingularModule.create_from_singular_matrix(
            self.poly_ring, free_matrix)
        free = SingularModule.create_free_module(3, self.poly_ring)
        self.assertTrue(free.equals(free_c))

    def test_create_singular(self):
        out = "mat_inter[1,1]=0\n"
        out = out + "mat_inter[1,2]=0\n"
        out = out + "mat_inter[1,3]=x(1)*x(2)*x(3)\n"
        out = out + "mat_inter[2,1]=x(2)\n"
        out = out + "mat_inter[2,2]=0\n"
        out = out + "mat_inter[2,3]=0\n"
        out = out + "mat_inter[3,1]=-x(3)\n"
        out = out + "mat_inter[3,2]=x(3)\n"
        out = out + "mat_inter[3,3]=0\n"
        mod = SingularModule.create_from_singular_matrix(
            self.poly_ring, out, "mat_inter")
        x = self.x
        y = self.y
        z = self.z
        zero = self.poly_ring.zero()
        mod_true = [[zero, y, -z], [zero, zero, z], [x * y * z, zero, zero]]
        self.assertEqual(mod.gens, mod_true)

    def test_lift_zero(self):
        x = self.x
        y = self.y
        z = self.z
        one = self.poly_ring.one()
        zero = self.poly_ring.zero()
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z],
                              [x, y, z**2], [one, x, y]])
        vec = sm1.lift([zero, zero, zero])
        self.assertEqual(vec, [zero for _ in range(4)])

    def test_lift_satisfy(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        vec = sm1.lift([
            x**2 + x * z + x, x * y + x * z + y * z + y,
            z**3 * x - 2 * y * x + z**2 + z
        ])
        self.assertEqual(vec[0] * x + vec[1] * x, x**2 + x * z + x)
        self.assertEqual(vec[0] * (y + z) + vec[1] * y,
                         x * y + x * z + y * z + y)
        self.assertEqual(vec[0] * (z**3 - 2 * y) + vec[1] * z,
                         z**3 * x - 2 * y * x + z**2 + z)

    def test_lift_linear(self):
        x = self.x
        y = self.y
        z = self.z
        sm1 = SingularModule([[x, y + z, z**3 - 2 * y], [x, y, z]])
        vec = sm1.lift([3 * x, 3 * y + z, z**3 - 2 * y + 2 * z], True)
        self.assertEqual(vec, [1, 2])
示例#22
0
        def demazure_character(self, w, f=None):
            r"""
            Returns the Demazure character associated to ``w``.

            INPUT:

            - ``w`` -- an element of the ambient weight lattice
              realization of the crystal, or a reduced word, or an element
              in the associated Weyl group

            OPTIONAL:

            - ``f`` -- a function from the crystal to a module

            This is currently only supported for crystals whose underlying
            weight space is the ambient space.

            The Demazure character is obtained by applying the Demazure operator
            `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`)
            to the highest weight element of the classical crystal. The simple
            Demazure operators `D_i` (see
            :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`)
            do not braid on the level of crystals, but on the level of characters they do.
            That is why it makes sense to input ``w`` either as a weight, a reduced word,
            or as an element of the underlying Weyl group.

            EXAMPLES::

                sage: T = crystals.Tableaux(['A',2], shape = [2,1])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = e[0] + 2*e[2]
                sage: weight.reduced_word()
                [2, 1]
                sage: T.demazure_character(weight)
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2

                sage: T = crystals.Tableaux(['A',3],shape=[2,1])
                sage: T.demazure_character([1,2,3])
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3
                sage: W = WeylGroup(['A',3])
                sage: w = W.from_reduced_word([1,2,3])
                sage: T.demazure_character(w)
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3

                sage: T = crystals.Tableaux(['B',2], shape = [2])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = -2*e[1]
                sage: T.demazure_character(weight)
                x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1

                sage: T = crystals.Tableaux("B2",shape=[1/2,1/2])
                sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient()
                sage: T.demazure_character([1,2],f=lambda x:b2(x.weight()))
                b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2)

            REFERENCES:

            .. [D1974] \M. Demazure, Desingularisation des varietes de Schubert,
               Ann. E. N. S., Vol. 6, (1974), p. 163-172

            .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms,
               Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313.
               :arXiv:`0707.4267`
            """
            from sage.misc.misc_c import prod
            from sage.rings.integer_ring import ZZ
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            if hasattr(w, 'reduced_word'):
                word = w.reduced_word()
            else:
                word = w
            n = self.weight_lattice_realization().n
            u = self.algebra(ZZ).sum_of_monomials(self.module_generators)
            u = self.demazure_operator(u, word)
            if f is None:
                x = ['x%s' % i for i in range(1, n + 1)]
                P = PolynomialRing(ZZ, x)
                # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis
                return sum((coeff * prod((x[i]**(c.weight()[i])
                                          for i in range(n)), P.one())
                            for c, coeff in u), P.zero())
            else:
                return sum((coeff * f(c)) for c, coeff in u)
示例#23
0
        def demazure_character(self, weight, reduced_word = False):
            r"""
            Returns the Demazure character associated to the specified
            weight in the ambient weight lattice.

            INPUT:

                - ``weight`` -- an element of the weight lattice
                  realization of the crystal, or a reduced word
                - ``reduced_word`` -- a boolean (default: ``False``)
                  whether ``weight`` is given as a reduced word

            This is currently only supported for crystals whose
            underlying weight space is the ambient space.

            EXAMPLES::

                sage: T = CrystalOfTableaux(['A',2], shape = [2,1])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = e[0] + 2*e[2]
                sage: weight.reduced_word()
                [2, 1]
                sage: T.demazure_character(weight)
                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x1*x3^2

                sage: T = CrystalOfTableaux(['A',3],shape=[2,1])
                sage: T.demazure_character([1,2,3], reduced_word = True)
                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x2^2*x3

                sage: T = CrystalOfTableaux(['B',2], shape = [2])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = -2*e[1]
                sage: T.demazure_character(weight)
                x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1

            TODO: detect automatically if weight is a reduced word,
            and remove the (untested!) ``reduced_word`` option.

            REFERENCES::

                .. [D1974] M. Demazure, Desingularisation des varietes de Schubert,
                   Ann. E. N. S., Vol. 6, (1974), p. 163-172

                .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms,
                   Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313
                   (arXiv:0707.4267)

            """
            from sage.misc.misc_c import prod
            from sage.rings.rational_field import QQ
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            if reduced_word:
                word = weight
            else:
                word = weight.reduced_word()
            n = self.weight_lattice_realization().n
            u = list( self.module_generators )
            for i in reversed(word):
                u = u + sum((x.demazure_operator(i, truncated = True) for x in u), [])
            x = ['x%s'%i for i in range(1,n+1)]
            P = PolynomialRing(QQ, x)
            u = [b.weight() for b in u]
            return sum((prod((x[i]**(la[i]) for i in range(n)), P.one()) for la in u), P.zero())
示例#24
0
def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001):
    r"""
    Return the covariant and Julia invariant from Cremona-Stoll [CS2003]_.

    In [CS2003]_ and [HS2018]_ the Julia invariant is denoted as `\Theta(F)`
    or `R(F, z(F))`. Note that you may get faster convergence if you first move
    `z_0(F)` to the fundamental domain before computing the true covariant

    INPUT:

    - ``F`` -- binary form of degree at least 3 with no multiple roots

    - ``z0_cov`` -- boolean, compute only the `z_0` invariant. Otherwise, solve
      the minimization problem

    - ``prec``-- positive integer. precision to use in CC

    - ``emb`` -- embedding into CC

    - ``error_limit`` -- sets the error tolerance (default:0.000001)


    OUTPUT: a complex number, a real number

    EXAMPLES::

        sage: from sage.rings.polynomial.binary_form_reduce import covariant_z0
        sage: R.<x,y> = QQ[]
        sage: F = 19*x^8 - 262*x^7*y + 1507*x^6*y^2 - 4784*x^5*y^3 + 9202*x^4*y^4\
        ....: - 10962*x^3*y^5 + 7844*x^2*y^6 - 3040*x*y^7 + 475*y^8
        sage: covariant_z0(F, prec=80, z0_cov=True)
        (1.3832330115323681438175 + 0.31233552177413614978744*I,
         3358.4074848663492819259)
        sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\
        ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8
        sage: covariant_z0(F, prec=80)
        (0.64189877107807122203366 + 1.1852516565091601348355*I,
         3134.5148284344627168276)

    ::

        sage: R.<x,y> = QQ[]
        sage: covariant_z0(x^3 + 2*x^2*y - 3*x*y^2, z0_cov=True)[0]
        0.230769230769231 + 0.799408065031789*I
        sage: -1/covariant_z0(-y^3 + 2*y^2*x + 3*y*x^2, z0_cov=True)[0]
        0.230769230769231 + 0.799408065031789*I

    ::

        sage: R.<x,y> = QQ[]
        sage: covariant_z0(2*x^2*y - 3*x*y^2, z0_cov=True)[0]
        0.750000000000000 + 1.29903810567666*I
        sage: -1/covariant_z0(-x^3 - x^2*y + 2*x*y^2, z0_cov=True)[0] + 1
        0.750000000000000 + 1.29903810567666*I

    ::

        sage: R.<x,y> = QQ[]
        sage: covariant_z0(x^2*y - x*y^2, prec=100) # tol 1e-28
         (0.50000000000000000000000000003 + 0.86602540378443864676372317076*I,
         1.5396007178390020386910634147)

    TESTS::

        sage: R.<x,y>=QQ[]
        sage: covariant_z0(x^2 + 24*x*y + y^2)
        Traceback (most recent call last):
        ...
        ValueError: must be at least degree 3
        sage: covariant_z0((x+y)^3, z0_cov=True)
        Traceback (most recent call last):
        ...
        ValueError: cannot have multiple roots for z0 invariant
        sage: covariant_z0(x^3 + 3*x*y + y)
        Traceback (most recent call last):
        ...
        TypeError: must be a binary form
        sage: covariant_z0(-2*x^2*y^3 + 3*x*y^4 + 127*y^5)
        Traceback (most recent call last):
        ...
        ValueError: cannot have a root with multiplicity >= 5/2
        sage: covariant_z0((x^2+2*y^2)^2)
        Traceback (most recent call last):
        ...
        ValueError: must have at least 3 distinct roots
    """
    R = F.parent()
    d = ZZ(F.degree())
    if R.ngens() != 2 or any(sum(t) != d for t in F.exponents()):
        raise TypeError('must be a binary form')
    if d < 3:
        raise ValueError('must be at least degree 3')

    f = F.subs({R.gen(1): 1}).univariate_polynomial()
    if f.degree() < d:
        # we have a root at infinity
        if f.constant_coefficient() != 0:
            # invert so we find all roots!
            mat = matrix(ZZ, 2, 2, [0, -1, 1, 0])
        else:
            t = 0
            while f(t) == 0:
                t += 1
            mat = matrix(ZZ, 2, 2, [t, -1, 1, 0])
    else:
        mat = matrix(ZZ, 2, 2, [1, 0, 0, 1])
    f = F(list(mat * vector(R.gens()))).subs({R.gen(1): 1}).univariate_polynomial()
    # now we have a single variable polynomial with all the roots of F
    K = ComplexField(prec=prec)
    if f.base_ring() != K:
        if emb is None:
            f = f.change_ring(K)
        else:
            f = f.change_ring(emb)
    roots = f.roots()
    if max(ex for _, ex in roots) > 1 or f.degree() < d - 1:
        if z0_cov:
            raise ValueError('cannot have multiple roots for z0 invariant')
        else:
            # just need a starting point for Newton's method
            f = f.lc() * prod(p for p, ex in f.factor())  # removes multiple roots
            if f.degree() < 3:
                raise ValueError('must have at least 3 distinct roots')
            roots = f.roots()
    roots = [p for p, _ in roots]

    # finding quadratic Q_0, gives us our covariant, z_0
    dF = f.derivative()
    n = ZZ(f.degree())
    PR = PolynomialRing(K, 'x,y')
    x, y = PR.gens()
    # finds Stoll and Cremona's Q_0
    q = sum([(1/(dF(r).abs()**(2/(n-2)))) * ((x-(r*y)) * (x-(r.conjugate()*y)))
             for r in roots])
    # this is Q_0 , always positive def as long as F has distinct roots
    A = q.monomial_coefficient(x**2)
    B = q.monomial_coefficient(x * y)
    C = q.monomial_coefficient(y**2)
    # need positive root
    try:
        z = ((-B + ((B**2)-(4*A*C)).sqrt()) / (2 * A))
    except ValueError:
        raise ValueError("not enough precision")
    if z.imag() < 0:
        z = (-B - ((B**2)-(4*A*C)).sqrt()) / (2 * A)

    if z0_cov:
        FM = f  # for Julia's invariant
    else:
        # solve the minimization problem for 'true' covariant
        CF = ComplexIntervalField(prec=prec)  # keeps trac of our precision error
        z = CF(z)
        FM = F(list(mat * vector(R.gens()))).subs({R.gen(1): 1}).univariate_polynomial()
        from sage.rings.polynomial.complex_roots import complex_roots
        L1 = complex_roots(FM, min_prec=prec)
        L = []
        # making sure multiplicity isn't too large using convergence conditions in paper
        for p, e in L1:
            if e >= d / 2:
                raise ValueError('cannot have a root with multiplicity >= %s/2' % d)
            for _ in range(e):
                L.append(p)
        RCF = PolynomialRing(CF, 'u,t')
        a = RCF.zero()
        c = RCF.zero()
        u, t = RCF.gens()
        for l in L:
            denom = ((t - l) * (t - l.conjugate()) + u**2)
            a += u**2 / denom
            c += (t - l.real()) / denom
        # Newton's Method, to find solutions. Error bound is less than diameter of our z
        err = z.diameter()
        zz = z.diameter()
        g1 = a.numerator() - d / 2 * a.denominator()
        g2 = c.numerator()
        G = vector([g1, g2])
        J = jacobian(G, [u, t])
        v0 = vector([z.imag(), z.real()])  # z0 as starting point
        # finds our correct z
        while err <= zz:
            NJ = J.subs({u: v0[0], t: v0[1]})
            NJinv = NJ.inverse()
            # inverse for CIF matrix seems to return fractions not CIF elements, fix them
            if NJinv.base_ring() != CF:
                NJinv = matrix(CF, 2, 2, [CF(zw.numerator() / zw.denominator())
                                          for zw in NJinv.list()])
            w = z
            v0 = v0 - NJinv*G.subs({u: v0[0], t: v0[1]})
            z = v0[1].constant_coefficient() + v0[0].constant_coefficient()*CF.gen(0)
            err = z.diameter() # precision
            zz = (w - z).abs().lower() # difference in w and z
        else:
            # despite there is no break, this happens
            if err > error_limit or err.is_NaN():
                raise ValueError("accuracy of Newton's root not within tolerance(%s > %s), increase precision" % (err, error_limit))
        if z.imag().upper() <= z.diameter():
            raise ArithmeticError("Newton's method converged to z not in the upper half plane")
        z = z.center()

    # Julia's invariant
    if FM.base_ring() != ComplexField(prec=prec):
        FM = FM.change_ring(ComplexField(prec=prec))
    tF = z.real()
    uF = z.imag()
    th = FM.lc().abs()**2
    for r, ex in FM.roots():
        for _ in range(ex):
            th = th * ((((r-tF).abs())**2 + uF**2)/uF)

    # undo shift and invert (if needed)
    # since F \cdot m ~ m^(-1)\cdot z
    # we apply m to z to undo m acting on F
    l = mat * vector([z, 1])
    return l[0] / l[1], th
def rand_pham_divisor(n,var="z"):
  poly_ring = PolynomialRing(QQ,n,var)
  div = poly_ring.zero()
  for g in poly_ring.gens():
    div += g**(randrange(2,9))
  return div
示例#26
0
    def ehrhart_polynomial(self, engine=None, variable='t', verbose=False,
            dual=None, irrational_primal=None, irrational_all_primal=None,
            maxdet=None, no_decomposition=None, compute_vertex_cones=None,
            smith_form=None, dualization=None, triangulation=None,
            triangulation_max_height=None, **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        The polyhedron must be a lattice polytope. Let `P` be a lattice
        polytope in `\RR^d` and define `L(P,t) = \# (tP\cap \ZZ^d)`.
        Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`. The Ehrhart polynomial may be computed
        using either LattE Integrale or Normaliz by setting ``engine``  to
        'latte' or 'normaliz' respectively.

        INPUT:

        - ``engine`` -- string; The backend to use. Allowed values are:

          * ``None`` (default); When no input is given the Ehrhart polynomial
            is computed using LattE Integrale (optional)
          * ``'latte'``; use LattE integrale program (optional)
          * ``'normaliz'``; use Normaliz program (optional package pynormaliz).
            The backend of ``self`` must be set to 'normaliz'.

        -  ``variable`` -- string (default: 't'); The variable in which the
           Ehrhart polynomial should be expressed.

        - When the ``engine`` is 'latte', the additional input values are:

          * ``verbose`` - boolean (default: ``False``); If ``True``, print the
            whole output of the LattE command.

          The following options are passed to the LattE command, for details
          consult `the LattE documentation
          <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

          * ``dual`` - boolean; triangulate and signed-decompose in the dual
            space
          * ``irrational_primal`` - boolean; triangulate in the dual space,
            signed-decompose in the primal space using irrationalization.
          * ``irrational_all_primal`` - boolean; triangulate and signed-decompose
            in the primal space using irrationalization.
          * ``maxdet`` -- integer; decompose down to an index (determinant) of
            ``maxdet`` instead of index 1 (unimodular cones).
          * ``no_decomposition`` -- boolean; do not signed-decompose
            simplicial cones.
          * ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
          * ``smith_form`` -- string; either 'ilio' or 'lidia'
          * ``dualization`` -- string; either 'cdd' or '4ti2'
          * ``triangulation`` - string; 'cddlib', '4ti2' or 'topcom'
          * ``triangulation_max_height`` - integer; use a uniform distribution
            of height from 1 to this number

        OUTPUT:

        A univariate polynomial in ``variable`` over a rational field.

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE Integrale
            `PyNormaliz <https://pypi.org/project/PyNormaliz>`_

        EXAMPLES:

        To start, we find the Ehrhart polynomial of a three-dimensional
        ``simplex``, first using ``engine='latte'``. Leaving the engine
        unspecified sets the ``engine`` to 'latte' by default::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: simplex = simplex.change_ring(QQ)
            sage: poly = simplex.ehrhart_polynomial(engine='latte')  # optional - latte_int
            sage: poly                                               # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: poly(1)                                            # optional - latte_int
            6
            sage: len(simplex.integral_points())                     # optional - latte_int
            6
            sage: poly(2)                                            # optional - latte_int
            36
            sage: len((2*simplex).integral_points())                 # optional - latte_int
            36

        Now we find the same Ehrhart polynomial, this time using
        ``engine='normaliz'``. To use the Normaliz engine, the ``simplex`` must
        be defined with ``backend='normaliz'``::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz
            sage: simplex = simplex.change_ring(QQ)                                                       # optional - pynormaliz
            sage: poly = simplex.ehrhart_polynomial(engine = 'normaliz')                                  # optional - pynormaliz
            sage: poly                                                                                    # optional - pynormaliz
            7/2*t^3 + 2*t^2 - 1/2*t + 1

        If the ``engine='normaliz'``, the backend should be ``'normaliz'``, otherwise
        it returns an error::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: simplex = simplex.change_ring(QQ)
            sage: simplex.ehrhart_polynomial(engine='normaliz')  # optional - pynormaliz
            Traceback (most recent call last):
            ...
            TypeError: The backend of the polyhedron should be 'normaliz'

        The polyhedron should be compact::

            sage: C = Polyhedron(backend='normaliz',rays=[[1,2],[2,1]])  # optional - pynormaliz
            sage: C = C.change_ring(QQ)                                  # optional - pynormaliz
            sage: C.ehrhart_polynomial()                                 # optional - pynormaliz
            Traceback (most recent call last):
            ...
            ValueError: Ehrhart polynomial only defined for compact polyhedra

        The polyhedron should have integral vertices::

            sage: L = Polyhedron(vertices = [[0],[1/2]])
            sage: L.ehrhart_polynomial()
            Traceback (most recent call last):
            ...
            TypeError: the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz'

        TESTS:

        The cache of the Ehrhart polynomial is being pickled::

            sage: P = polytopes.cube().change_ring(QQ)  # optional - latte_int
            sage: P.ehrhart_polynomial()                # optional - latte_int
            8*t^3 + 12*t^2 + 6*t + 1
            sage: Q = loads(dumps(P))                   # optional - latte_int
            sage: Q.ehrhart_polynomial.is_in_cache()    # optional - latte_int
            True
        """
        # check if ``self`` is compact and has vertices in ZZ
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, variable)
            return R.zero()

        if not self.is_compact():
            raise ValueError("Ehrhart polynomial only defined for compact polyhedra")

        if any(not v.is_integral() for v in self.vertex_generator()):
            raise TypeError("the polytope has nonintegral vertices, use ehrhart_quasipolynomial with backend 'normaliz'")
        # Passes to specific latte or normaliz subfunction depending on engine
        if engine is None:
            # set default engine to latte
            engine = 'latte'
        if engine == 'latte':
            poly = self._ehrhart_polynomial_latte(verbose, dual,
            irrational_primal, irrational_all_primal, maxdet,
            no_decomposition, compute_vertex_cones, smith_form,
            dualization, triangulation, triangulation_max_height,
            **kwds)
            return poly.change_variable_name(variable)
            # TO DO: replace this change of variable by creating the appropriate
            #        polynomial ring in the latte interface.

        elif engine == 'normaliz':
            return self._ehrhart_polynomial_normaliz(variable)
        else:
            raise ValueError("engine must be 'latte' or 'normaliz'")
示例#27
0
        def demazure_character(self, w, f = None):
            r"""
            Returns the Demazure character associated to ``w``.

            INPUT:

            - ``w`` -- an element of the ambient weight lattice
              realization of the crystal, or a reduced word, or an element
              in the associated Weyl group

            OPTIONAL:

            - ``f`` -- a function from the crystal to a module

            This is currently only supported for crystals whose underlying
            weight space is the ambient space.

            The Demazure character is obtained by applying the Demazure operator
            `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`)
            to the highest weight element of the classical crystal. The simple
            Demazure operators `D_i` (see
            :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`)
            do not braid on the level of crystals, but on the level of characters they do.
            That is why it makes sense to input ``w`` either as a weight, a reduced word,
            or as an element of the underlying Weyl group.

            EXAMPLES::

                sage: T = crystals.Tableaux(['A',2], shape = [2,1])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = e[0] + 2*e[2]
                sage: weight.reduced_word()
                [2, 1]
                sage: T.demazure_character(weight)
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2

                sage: T = crystals.Tableaux(['A',3],shape=[2,1])
                sage: T.demazure_character([1,2,3])
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3
                sage: W = WeylGroup(['A',3])
                sage: w = W.from_reduced_word([1,2,3])
                sage: T.demazure_character(w)
                x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3

                sage: T = crystals.Tableaux(['B',2], shape = [2])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = -2*e[1]
                sage: T.demazure_character(weight)
                x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1

                sage: T = crystals.Tableaux("B2",shape=[1/2,1/2])
                sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient()
                sage: T.demazure_character([1,2],f=lambda x:b2(x.weight()))
                b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2)

            REFERENCES:

            - [De1974]_

            - [Ma2009]_
            """
            from sage.misc.misc_c import prod
            from sage.rings.integer_ring import ZZ
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            if hasattr(w, 'reduced_word'):
                word = w.reduced_word()
            else:
                word = w
            n = self.weight_lattice_realization().n
            u = self.algebra(ZZ).sum_of_monomials(self.module_generators)
            u = self.demazure_operator(u, word)
            if f is None:
                x = ['x%s'%i for i in range(1,n+1)]
                P = PolynomialRing(ZZ, x)
                # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis
                return sum((coeff*prod((x[i]**(c.weight()[i]) for i in range(n)), P.one()) for c, coeff in u), P.zero())
            else:
                return sum((coeff*f(c)) for c, coeff in u)
    def has_irred_rep(self, n, gen_set=None, restrict=None, force=False):
        """
        Returns `True` if there exists an `n`-dimensional irreducible representation of `self`,
        and `False` otherwise. Of course, this function runs `has_rep(n, restrict)` to verify
        there is a representation in the first place, and returns `False` if not.

        The argument `restrict` may be used equivalenty to its use in `has_rep()`.

        The argument `gen_set` may be set to `'PBW'` or `'pbw'`, if `self` has an algebra basis
        similar to that of a Poincaré-Birkhoff-Witt basis.

        Alternatively, an explicit generating set for the algorithm implemented by this function
        can be given, as a tuple or array of `FreeAlgebraElements`. This is only useful if the
        package cannot reduce the elements of `self`, but they can be reduced in theory.

        Use `force=True` if the function does not recognize the base field as computable, but the
        field is computable.
        """
        if (not force and self.base_field() not in NumberFields
                and self.base_field() not in FiniteFields):
            raise TypeError(
                'Base field must be computable. If %s is computable' %
                self.base_field() + ' then use force=True to bypass this.')

        if n not in ZZ or n < 1:
            raise ValueError('Dimension must be a positive integer.')

        if not self.has_rep(n, restrict): return False
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        from sage.groups.all import SymmetricGroup
        import math

        B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1),
                           'x',
                           order='deglex')
        M = MatrixSpace(B, n, sparse=True)

        gen_matrix = list()
        if not isinstance(restrict, (tuple, list)):
            restrict = [None for i in range(self.ngens())]
        if len(restrict) != self.ngens():
            raise ValueError(
                'Length of restrict does not match number of generators.')
        for i in range(self.ngens()):
            ith_gen_matrix = []
            for j in range(n):
                for k in range(n):
                    if restrict[i] == 'upper' and j > k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'lower' and j < k:
                        ith_gen_matrix.append(B.zero())
                    elif restrict[i] == 'diagonal' and j != k:
                        ith_gen_matrix.append(B.zero())
                    else:
                        ith_gen_matrix.append(B.gen(j + (j + 1) * k +
                                                    i * n**2))
            gen_matrix.append(M(ith_gen_matrix))

        relB = list()
        for i in range(self.nrels()):
            relB += self._to_matrix(self.rel(i), M, gen_matrix).list()

        Z = FreeAlgebra(ZZ, 2 * n - 2, 'Y')
        standard_poly = Z(0)
        for s in SymmetricGroup(2 * n - 2).list():
            standard_poly += s.sign() * reduce(
                lambda x, y: x * y, [Z('Y%s' % (i - 1)) for i in s.tuple()])

        if n <= 6 and is_NumberField(self.base_field()): p = 2 * n
        else:
            p = int(
                math.floor(n *
                           math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) +
                           n / float(2) - 3))

        if isinstance(gen_set, (tuple, list)):
            try:
                gen_set = [
                    self._to_matrix(elt, M, gen_matrix) for elt in gen_set
                ]
            except (NameError, TypeError) as error:
                print(error)

        if gen_set == None:
            word_gen_set = list(self._create_rep_gen_set(n, p))
            gen_set = [
                self._to_matrix(_to_element(self, [[word, self.one()]]), M,
                                gen_matrix) for word in word_gen_set
            ]

        elif gen_set == 'pbw' or gen_set == 'PBW':
            word_gen_set = list(self._create_pbw_rep_gen_set(n, p))
            gen_set = [
                self._to_matrix(_to_element(self, [[word, self.one()]]), M,
                                gen_matrix) for word in word_gen_set
            ]

        else:
            raise TypeError('Invalid generating set.')

        ordering = [i for i in range(2 * n - 2)]
        max_ordering = [
            len(gen_set) - (2 * n - 2) + i for i in range(2 * n - 2)
        ]
        ordering.insert(0, 0)
        max_ordering.insert(0, len(gen_set))
        rep_exists = False
        z = B.gen(B.ngens() - 1)

        while ordering[0] != max_ordering[0]:
            y = gen_set[ordering[0]].trace_of_product(
                standard_poly.subs({
                    Z('Y%s' % (j - 1)): gen_set[ordering[j]]
                    for j in range(1, 2 * n - 1)
                }))
            radB_test = relB + [B(1) - z * y]
            if B.one() not in B.ideal(radB_test):
                rep_exists = True
                break
            for i in range(2 * n - 2, -1, -1):
                if i != 0 and ordering[i] != max_ordering[i]:
                    ordering[i] += 1
                    break
                elif i == 0:
                    ordering[i] += 1
                    if ordering[i] != max_ordering[i]:
                        for j in range(1, 2 * n - 1):
                            ordering[j] = j - 1

        return rep_exists
示例#29
0
    def ehrhart_polynomial(self,
                           engine=None,
                           variable='t',
                           verbose=False,
                           dual=None,
                           irrational_primal=None,
                           irrational_all_primal=None,
                           maxdet=None,
                           no_decomposition=None,
                           compute_vertex_cones=None,
                           smith_form=None,
                           dualization=None,
                           triangulation=None,
                           triangulation_max_height=None,
                           **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        The Ehrhart polynomial may be computed using either  LattE Integrale
        or Normaliz by setting ``engine``  to 'latte' or 'normaliz' respectively.

        INPUT:

        - ``engine`` -- string; The backend to use. Allowed values are:

          * ``None`` (default); When no input is given the Ehrhart polynomial
            is computed using LattE Integrale (optional)
          * ``'latte'``; use LattE integrale program (optional)
          * ``'normaliz'``; use Normaliz program (optional). The backend of
            ``self`` must be set to 'normaliz'.

        - ``variable`` -- string (default: 't'); The variable in which the
          Ehrhart polynomial should be expressed.

        - When the ``engine`` is 'latte' or None, the additional input values are:

          * ``verbose`` - boolean (default: ``False``); if ``True``, print the
            whole output of the LattE command.

          The following options are passed to the LattE command, for details
          consult `the LattE documentation
          <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

          * ``dual`` - boolean; triangulate and signed-decompose in the dual
            space
          * ``irrational_primal`` - boolean; triangulate in the dual space,
            signed-decompose in the primal space using irrationalization.
          * ``irrational_all_primal`` - boolean; Triangulate and signed-decompose
            in the primal space using irrationalization.
          * ``maxdet`` -- integer; decompose down to an index (determinant) of
            ``maxdet`` instead of index 1 (unimodular cones).
          * ``no_decomposition`` -- boolean; do not signed-decompose
            simplicial cones.
          * ``compute_vertex_cones`` -- string; either 'cdd' or 'lrs' or '4ti2'
          * ``smith_form`` -- string; either 'ilio' or 'lidia'
          * ``dualization`` -- string; either 'cdd' or '4ti2'
          * ``triangulation`` - string; 'cddlib', '4ti2' or 'topcom'
          * ``triangulation_max_height`` - integer; use a uniform distribution of
            height from 1 to this number

        OUTPUT:

        The Ehrhart polynomial as a a univariate polynomial in ``variable``
        over a rational field.

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE Integrale
            `PyNormaliz <https://pypi.python.org/pypi/PyNormaliz/1.5>`_

        EXAMPLES:

        To start, we find the Ehrhart polynomial of a three-dimensional
        ``simplex``, first using ``engine='latte'``. Leaving the engine
        unspecified sets the ``engine`` to 'latte' by default::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: poly = simplex.ehrhart_polynomial(engine = 'latte')  # optional - latte_int
            sage: poly                                                 # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: poly(1)                                              # optional - latte_int
            6
            sage: len(simplex.integral_points())                       # optional - latte_int
            6
            sage: poly(2)                                              # optional - latte_int
            36
            sage: len((2*simplex).integral_points())                   # optional - latte_int
            36

        Now we find the same Ehrhart polynomial, this time using
        ``engine='normaliz'``. To use the Normaliz engine, the ``simplex`` must
        be defined with ``backend='normaliz'``::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)], backend='normaliz') # optional - pynormaliz
            sage: poly = simplex.ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
            sage: poly                                                 # optional - pynormaliz
            7/2*t^3 + 2*t^2 - 1/2*t + 1

        If the ``engine='normaliz'``, the backend should be ``'normaliz'``, otherwise
        it returns an error::

            sage: simplex = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: simplex.ehrhart_polynomial(engine='normaliz')        # optional - pynormaliz
            Traceback (most recent call last):
            ...
            TypeError: The polyhedron's backend should be 'normaliz'

        Now we find the Ehrhart polynomials of the unit hypercubes of
        dimensions three through six. They are computed first with
        ``engine='latte'`` and then with ``engine='normaliz'``.
        The degree of the Ehrhart polynomial matches the dimension of the
        hypercube, and the coefficient of the leading monomial equals the
        volume of the unit hypercube::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)),backend='normaliz') # optional - pynormaliz
            sage: hypercube(3).ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial(engine='normaliz') # optional - pynormaliz
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: p = Polyhedron(ambient_dim=3, vertices=[])
            sage: p.ehrhart_polynomial()
            0
            sage: parent(_)
            Univariate Polynomial Ring in t over Rational Field

        The polyhedron should be compact::

            sage: C = Polyhedron(rays=[[1,2],[2,1]])
            sage: C.ehrhart_polynomial()
            Traceback (most recent call last):
            ...
            ValueError: Ehrhart polynomial only defined for compact polyhedra
        """
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, variable)
            return R.zero()

        if not self.is_compact():
            raise ValueError(
                "Ehrhart polynomial only defined for compact polyhedra")

        if engine is None:
            # setting the default to 'latte'
            engine = 'latte'
        if engine == 'latte':
            poly = self._ehrhart_polynomial_latte(
                verbose, dual, irrational_primal, irrational_all_primal,
                maxdet, no_decomposition, compute_vertex_cones, smith_form,
                dualization, triangulation, triangulation_max_height, **kwds)
            return poly.change_variable_name(variable)
            # TO DO: replace this change of variable by creating the appropriate
            #        polynomial ring in the latte interface.

        elif engine == 'normaliz':
            return self._ehrhart_polynomial_normaliz(variable)
        else:
            raise ValueError("engine must be 'latte' or 'normaliz'")
示例#30
0
    def ehrhart_polynomial(self, verbose=False, dual=None,
            irrational_primal=None, irrational_all_primal=None, maxdet=None,
            no_decomposition=None, compute_vertex_cones=None, smith_form=None,
            dualization=None, triangulation=None, triangulation_max_height=None,
            **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        INPUT:

        - ``verbose`` - (boolean, default to ``False``) if ``True``, print the
          whole output of the LattE command.

        The following options are passed to the LattE command, for details you
        should consult `the LattE documentation
        <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

        - ``dual`` - (boolean) triangulate and signed-decompose in the dual
          space

        - ``irrational_primal`` - (boolean) triangulate in the dual space,
          signed-decompose in the primal space using irrationalization.

        - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose
          in the primal space using irrationalization.

        - ``maxdet`` -- (integer) decompose down to an index (determinant) of
          ``maxdet`` instead of index 1 (unimodular cones).

        - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones.

        - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2'

        - ``smith_form`` -- (string) either 'ilio' or 'lidia'

        - ``dualization`` -- (string) either 'cdd' or '4ti2'

        - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom'

        - ``triangulation_max_height`` - (integer) use a uniform distribution of
          height from 1 to this number

        .. NOTE::

            Any additional argument is forwarded to LattE's executable
            ``count``. All occurrences of '_' will be replaced with a '-'.

        ALGORITHM:

        This method calls the program ``count`` from LattE integrale, a program
        for lattice point enumeration (see
        https://www.math.ucdavis.edu/~latte/).

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE integrale

        EXAMPLES::

            sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: p = P.ehrhart_polynomial()    # optional - latte_int
            sage: p                             # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: p(1)                          # optional - latte_int
            6
            sage: len(P.integral_points())
            6
            sage: p(2)                          # optional - latte_int
            36
            sage: len((2*P).integral_points())
            36

        The unit hypercubes::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: P = Polyhedron(ambient_dim=3, vertices=[])
            sage: P.ehrhart_polynomial()    # optional - latte_int
            0
            sage: parent(_)                 # optional - latte_int
            Univariate Polynomial Ring in t over Rational Field

        TESTS:

        Test options::

            sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)

            sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin
            ...
            sage: p    # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(dual=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True)   # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

        Test bad options::

            sage: P.ehrhart_polynomial(bim_bam_boum=19)   # optional - latte_int
            Traceback (most recent call last):
            ...
            RuntimeError: LattE integrale program failed (exit code 1):
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin
            Unknown command/option --bim-bam-boum=19
        """
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, 't')
            return R.zero()

        # note: the options below are explicitely written in the function
        # declaration in order to keep tab completion (see #18211).
        kwds.update({
            'dual'                    : dual,
            'irrational_primal'       : irrational_primal,
            'irrational_all_primal'   : irrational_all_primal,
            'maxdet'                  : maxdet,
            'no_decomposition'        : no_decomposition,
            'compute_vertex_cones'    : compute_vertex_cones,
            'smith_form'              : smith_form,
            'dualization'             : dualization,
            'triangulation'           : triangulation,
            'triangulation_max_height': triangulation_max_height})

        from sage.interfaces.latte import count
        ine = self.cdd_Hrepresentation()
        return count(ine, cdd=True, ehrhart_polynomial=True, verbose=verbose, **kwds)
示例#31
0
def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None):
    r"""
    Return the polynomial of the sums of permanental minors of ``A``.

    INPUT:

    - `A` -- a matrix

    - `permanent_only` -- if True, return only the permanent of `A`

    - `var` -- name of the polynomial variable

    - `prec` -- if prec is not None, truncate the polynomial at precision `prec`


    The polynomial of the sums of permanental minors is

    .. MATH::

        \sum_{i=0}^{min(nrows, ncols)} p_i(A) x^i

    where `p_i(A)` is the `i`-th permanental minor of `A` (that can also be
    obtained through the method
    :meth:`~sage.matrix.matrix2.Matrix.permanental_minor` via
    ``A.permanental_minor(i)``).

    The algorithm implemented by that function has been developed by P. Butera
    and M. Pernici, see [ButPer]. Its complexity is `O(2^n m^2 n)` where `m` and
    `n` are the number of rows and columns of `A`.  Moreover, if `A` is a banded
    matrix with width `w`, that is `A_{ij}=0` for `|i - j| > w` and `w < n/2`,
    then the complexity of the algorithm is `O(4^w (w+1) n^2)`.

    INPUT:

    - ``A`` -- matrix

    - ``permanent_only`` -- optional boolean. If ``True``, only the permanent
      is computed (might be faster).

    - ``var`` -- a variable name

    EXAMPLES::

        sage: from sage.matrix.matrix_misc import permanental_minor_polynomial
        sage: m = matrix([[1,1],[1,2]])
        sage: permanental_minor_polynomial(m)
        3*t^2 + 5*t + 1
        sage: permanental_minor_polynomial(m, permanent_only=True)
        3
        sage: permanental_minor_polynomial(m, prec=2)
        5*t + 1

    ::

        sage: M = MatrixSpace(ZZ,4,4)
        sage: A = M([1,0,1,0,1,0,1,0,1,0,10,10,1,0,1,1])
        sage: permanental_minor_polynomial(A)
        84*t^3 + 114*t^2 + 28*t + 1
        sage: [A.permanental_minor(i) for i in range(5)]
        [1, 28, 114, 84, 0]

    An example over `\QQ`::

        sage: M = MatrixSpace(QQ,2,2)
        sage: A = M([1/5,2/7,3/2,4/5])
        sage: permanental_minor_polynomial(A, True)
        103/175

    An example with polynomial coefficients::

        sage: R.<a> = PolynomialRing(ZZ)
        sage: A = MatrixSpace(R,2)([[a,1], [a,a+1]])
        sage: permanental_minor_polynomial(A, True)
        a^2 + 2*a

    A usage of the ``var`` argument::

        sage: m = matrix(ZZ,4,[0,1,2,3,1,2,3,0,2,3,0,1,3,0,1,2])
        sage: permanental_minor_polynomial(m, var='x')
        164*x^4 + 384*x^3 + 172*x^2 + 24*x + 1

    ALGORITHM:

        The permanent `perm(A)` of a `n \times n` matrix `A` is the coefficient
        of the `x_1 x_2 \ldots x_n` monomial in

        .. MATH::

            \prod_{i=1}^n \left( \sum_{j=1}^n A_{ij} x_j \right)

        Evaluating this product one can neglect `x_i^2`, that is `x_i`
        can be considered to be nilpotent of order `2`.

        To formalize this procedure, consider the algebra
        `R = K[\eta_1, \eta_2, \ldots, \eta_n]` where the `\eta_i` are
        commuting, nilpotent of order `2` (i.e. `\eta_i^2 = 0`).
        Formally it is the quotient ring of the polynomial
        ring in `\eta_1, \eta_2, \ldots, \eta_n` quotiented by the ideal
        generated by the `\eta_i^2`.

        We will mostly consider the ring `R[t]` of polynomials over `R`. We
        denote a generic element of `R[t]` by `p(\eta_1, \ldots, \eta_n)` or
        `p(\eta_{i_1}, \ldots, \eta_{i_k})` if we want to emphasize that some
        monomials in the `\eta_i` are missing.

        Introduce an "integration" operation `\langle p \rangle` over `R` and
        `R[t]` consisting in the sum of the coefficients of the non-vanishing
        monomials in `\eta_i` (i.e. the result of setting all variables `\eta_i`
        to `1`). Let us emphasize that this is *not* a morphism of algebras as
        `\langle \eta_1 \rangle^2 = 1` while `\langle \eta_1^2 \rangle = 0`!

        Let us consider an example of computation.
        Let `p_1 = 1 + t \eta_1 + t \eta_2` and
        `p_2 = 1 + t \eta_1 + t \eta_3`. Then

        .. MATH::

            p_1 p_2 = 1 + 2t \eta_1 +
                    t (\eta_2 + \eta_3) +
                    t^2 (\eta_1 \eta_2 + \eta_1 \eta_3 + \eta_2 \eta_3)

        and

        .. MATH::

            \langle p_1 p_2 \rangle = 1 + 4t + 3t^2

        In this formalism, the permanent is just

        .. MATH::

            perm(A) = \langle \prod_{i=1}^n \sum_{j=1}^n A_{ij} \eta_j \rangle

        A useful property of `\langle . \rangle` which makes this algorithm
        efficient for band matrices is the following: let
        `p_1(\eta_1, \ldots, \eta_n)` and `p_2(\eta_j, \ldots, \eta_n)` be
        polynomials in `R[t]` where `j \ge 1`.  Then one has

        .. MATH::

            \langle p_1(\eta_1, \ldots, \eta_n) p_2 \rangle =
            \langle p_1(1, \ldots, 1, \eta_j, \ldots, \eta_n) p_2 \rangle

        where `\eta_1,..,\eta_{j-1}` are replaced by `1` in `p_1`. Informally,
        we can "integrate" these variables *before* performing the product. More
        generally, if a monomial `\eta_i` is missing in one of the terms of a
        product of two terms, then it can be integrated in the other term.

        Now let us consider an `m \times n` matrix with `m \leq n`. The *sum of
        permanental `k`-minors of `A`* is

        .. MATH::

            perm(A, k) = \sum_{r,c} perm(A_{r,c})

        where the sum is over the `k`-subsets `r` of rows and `k`-subsets `c` of
        columns and `A_{r,c}` is the submatrix obtained from `A` by keeping only
        the rows `r` and columns `c`. Of course
        `perm(A, \min(m,n)) = perm(A)` and note that `perm(A,1)` is just the sum
        of all entries of the matrix.

        The generating function of these sums of permanental minors is

        .. MATH::

            g(t) = \left\langle
            \prod_{i=1}^m \left(1 + t \sum_{j=1}^n A_{ij} \eta_j\right)
            \right\rangle

        In fact the `t^k` coefficient of `g(t)` corresponds to choosing
        `k` rows of `A`;  `\eta_i` is associated to the i-th column;
        nilpotency avoids having twice the same column in a product of `A`'s.

        For more details, see the article [ButPer].

        From a technical point of view, the product in
        `K[\eta_1, \ldots, \eta_n][t]` is implemented as a subroutine in
        :func:`prm_mul`. The indices of the rows and columns actually start at
        `0`, so the variables are  `\eta_0, \ldots, \eta_{n-1}`. Polynomials are
        represented in dictionary form: to a variable `\eta_i` is associated
        the key `2^i` (or in Python ``1 << i``). The keys associated to products
        are obtained by considering the development in base `2`: to the monomial
        `\eta_{i_1} \ldots \eta_{i_k}` is associated the key
        `2^{i_1} + \ldots + 2^{i_k}`. So the product `\eta_1 \eta_2` corresponds
        to the key `6 = (110)_2` while `\eta_0 \eta_3` has key `9 = (1001)_2`.
        In particular all operations on monomials are implemented via bitwise
        operations on the keys.

    REFERENCES:

    .. [ButPer] \P. Butera and M. Pernici "Sums of permanental minors
       using Grassmann algebra", :arxiv:`1406.5337`
    """
    if permanent_only:
        prec = None
    elif prec is not None:
        prec = int(prec)
        if prec == 0:
            raise ValueError('the argument `prec` must be a positive integer')

    K = PolynomialRing(A.base_ring(), var)
    nrows = A.nrows()
    ncols = A.ncols()
    A = A.rows()
    p = {0: K.one()}
    t = K.gen()
    vars_to_do = range(ncols)
    for i in range(nrows):
        # build the polynomial p1 = 1 + t sum A_{ij} eta_j
        if permanent_only:
            p1 = {}
        else:
            p1 = {0: K.one()}
        a = A[i]  # the i-th row of A
        for j in range(len(a)):
            if a[j]:
                p1[1 << j] = a[j] * t

        # make the product with the preceding polynomials, taking care of
        # variables that can be integrated
        mask_free = 0
        j = 0
        while j < len(vars_to_do):
            jj = vars_to_do[j]
            if all(A[k][jj] == 0 for k in range(i + 1, nrows)):
                mask_free += 1 << jj
                vars_to_do.remove(jj)
            else:
                j += 1
        p = prm_mul(p, p1, mask_free, prec)

    if not p:
        return K.zero()

    if len(p) != 1 or 0 not in p:
        raise RuntimeError(
            "Something is wrong! Certainly a problem in the"
            " algorithm... please contact [email protected]")

    p = p[0]
    return p[min(nrows, ncols)] if permanent_only else p
示例#32
0
def CyclicSievingPolynomial(L, cyc_act=None, order=None, get_order=False):
    """
    Return the unique polynomial ``p`` of degree smaller than ``order`` such
    that the triple ``(L, cyc_act, p)`` exhibits the Cyclic Sieving Phenomenon.

    If ``cyc_act`` is None, ``L`` is expected to contain the orbit lengths.

    INPUT:

    - ``L`` -- if ``cyc_act`` is ``None``: list of orbit sizes,
      otherwise list of objects

    - ``cyc_act`` -- (default:``None``) bijective function from ``L`` to ``L``

    - ``order`` -- (default:``None``) if set to an integer, this
        cyclic order of ``cyc_act`` is used (must be an integer multiple
        of the order of ``cyc_act``) otherwise, the order of ``cyc_action`` is
        used

    - ``get_order`` -- (default:``False``) if ``True``, a tuple ``[p,n]``
      is returned where ``p`` is as above, and ``n`` is the order

    EXAMPLES::

        sage: from sage.combinat.cyclic_sieving_phenomenon import CyclicSievingPolynomial
        sage: S42 = Subsets([1,2,3,4], 2)
        sage: def cyc_act(S): return Set(i.mod(4) + 1 for i in S)
        sage: cyc_act([1,3])
        {2, 4}
        sage: cyc_act([1,4])
        {1, 2}
        sage: CyclicSievingPolynomial(S42, cyc_act)
        q^3 + 2*q^2 + q + 2
        sage: CyclicSievingPolynomial(S42, cyc_act, get_order=True)
        [q^3 + 2*q^2 + q + 2, 4]
        sage: CyclicSievingPolynomial(S42, cyc_act, order=8)
        q^6 + 2*q^4 + q^2 + 2
        sage: CyclicSievingPolynomial([4,2])
        q^3 + 2*q^2 + q + 2

    TESTS:

    We check that :trac:`13997` is handled::

        sage: CyclicSievingPolynomial(S42, cyc_act, order=8, get_order=True)
        [q^6 + 2*q^4 + q^2 + 2, 8]
        sage: CyclicSievingPolynomial(S42, cyc_act, order=11)
        Traceback (most recent call last):
        ...
        ValueError: order is not a multiple of the order of the cyclic action
    """
    if cyc_act:
        orbits = orbit_decomposition(L, cyc_act)
    else:
        orbits = [list(range(k)) for k in L]

    R = PolynomialRing(ZZ, 'q')
    q = R.gen()
    p = R.zero()

    orbit_sizes = {}
    for orbit in orbits:
        length = len(orbit)
        if length in orbit_sizes:
            orbit_sizes[length] += 1
        else:
            orbit_sizes[length] = 1

    n = lcm(list(orbit_sizes))

    if order:
        if order.mod(n):
            raise ValueError("order is not a multiple of the order"
                             " of the cyclic action")
    else:
        order = n

    for i in range(n):
        if i == 0:
            j = sum(orbit_sizes.values())
        else:
            j = sum(orbit_sizes[l] for l in orbit_sizes
                    if ZZ(i).mod(n / l) == 0)
        p += j * q**i

    p = p(q**(order // n))

    if get_order:
        return [p, order]
    else:
        return p
示例#33
0
        def demazure_character(self, weight, reduced_word = False):
            r"""
            Returns the Demazure character associated to the specified
            weight in the ambient weight lattice.

            INPUT:

                - ``weight`` -- an element of the weight lattice
                  realization of the crystal, or a reduced word
                - ``reduced_word`` -- a boolean (default: ``False``)
                  whether ``weight`` is given as a reduced word

            This is currently only supported for crystals whose
            underlying weight space is the ambient space.

            EXAMPLES::

                sage: T = CrystalOfTableaux(['A',2], shape = [2,1])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = e[0] + 2*e[2]
                sage: weight.reduced_word()
                [2, 1]
                sage: T.demazure_character(weight)
                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x1*x3^2

                sage: T = CrystalOfTableaux(['A',3],shape=[2,1])
                sage: T.demazure_character([1,2,3], reduced_word = True)
                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x2^2*x3

                sage: T = CrystalOfTableaux(['B',2], shape = [2])
                sage: e = T.weight_lattice_realization().basis()
                sage: weight = -2*e[1]
                sage: T.demazure_character(weight)
                x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1

            TODO: detect automatically if weight is a reduced word,
            and remove the (untested!) ``reduced_word`` option.

            REFERENCES::

                .. [D1974] M. Demazure, Desingularisation des varietes de Schubert,
                   Ann. E. N. S., Vol. 6, (1974), p. 163-172

                .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms,
                   Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313
                   (arXiv:0707.4267)

            """
            from sage.misc.misc_c import prod
            from sage.rings.rational_field import QQ
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            if reduced_word:
                word = weight
            else:
                word = weight.reduced_word()
            n = self.weight_lattice_realization().n
            u = list( self.module_generators )
            for i in reversed(word):
                u = u + sum((x.demazure_operator(i, truncated = True) for x in u), [])
            x = ['x%s'%i for i in range(1,n+1)]
            P = PolynomialRing(QQ, x)
            u = [b.weight() for b in u]
            return sum((prod((x[i]**(la[i]) for i in range(n)), P.one()) for la in u), P.zero())
class TestLogartihmicDifferentialForms(unittest.TestCase):

  def setUp(self):
    self.poly_ring = PolynomialRing(QQ,"x",3);
    self.x = self.poly_ring.gens()[0];
    self.y = self.poly_ring.gens()[1];
    self.z = self.poly_ring.gens()[2];
    self.vars = var('x,y,z')
    
  def test_p_forms_crossing_ngens(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    self.assertEqual(len(logdf.p_form_generators(0)),1)
    self.assertEqual(len(logdf.p_form_generators(1)),3)
    self.assertEqual(len(logdf.p_form_generators(2)),3)
    self.assertEqual(len(logdf.p_form_generators(3)),1)
    
  def test_0_modules_crossing_ngens(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    crossing_0_module = SingularModule([[crossing]])
    self.assertTrue(crossing_0_module.equals(logdf.p_module(0)))
    
  def test_1_modules_crossing_ngens(self):
    x = self.x
    y = self.y
    z = self.z
    zero = self.poly_ring.zero()
    crossing = x*y*z
    logdf = LogarithmicDifferentialForms(crossing)
    crossing_1_module = SingularModule([[y*z,zero,zero],[zero,x*z,zero],[zero,zero,x*y]])
    self.assertTrue(crossing_1_module.equals(logdf.p_module(1)))
    
  def test_2_modules_crossing_ngens(self):
    x = self.x
    y = self.y
    z = self.z
    zero = self.poly_ring.zero()
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    crossing_2_module = SingularModule([[z,zero,zero],[zero,y,zero],[zero,zero,x]])
    self.assertTrue(crossing_2_module.equals(logdf.p_module(2)))
    
  def test_3_modules_crossing_ngens(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    crossing_3_module = SingularModule([[self.poly_ring.one()]])
    self.assertTrue(crossing_3_module.equals(logdf.p_module(3)))

  def test_p_module_n_crossing(self):
    #Make sure this doesnt throw an error - fix bug
    for i in range(4,5):
      p_ring = PolynomialRing(QQ,i,"z")
      crossing = p_ring.one()
      for g in p_ring.gens():
        crossing *= g
      logdf = LogarithmicDifferentialForms(crossing)
      logdf.p_module(i-1)

  def test_complement_complex_crossing(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    complex = logdf.chain_complex("complement")
    complex_size = {}
    for i,c in complex.iteritems():
      complex_size[i] = len(c)
    self.assertEqual(complex_size,{0:1,1:3,2:3,3:1})

  def test_complement_complex_whitney(self):
    whitney = self.x**2*self.y - self.z**2
    logdf = LogarithmicDifferentialForms(whitney)
    complex = logdf.chain_complex("complement")
    complex_size = {}
    for i,c in complex.iteritems():
      complex_size[i] = len(c)
    self.assertEqual(complex_size,{0:1,1:1,2:0,3:0})

  def test_equi_complex_crossing(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    complex = logdf.chain_complex("equivarient")
    complex_size = {}
    for i,c in complex.iteritems():
      complex_size[i] = len(c)
    self.assertEqual(complex_size,{0:1,1:3,2:4,3:4})

  def test_equi_complex_whitney(self):
    whitney = self.x**2*self.y - self.z**2
    logdf = LogarithmicDifferentialForms(whitney)
    complex = logdf.chain_complex("equivarient")
    complex_size = {}
    for i,c in complex.iteritems():
      complex_size[i] = len(c)
    self.assertEqual(complex_size,{0:1,1:1,2:1,3:1})

  def test_complement_homology_crossing(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    homology = logdf.homology("complement")
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:3,2:3,3:1})

  def test_equi_homology_whitney(self):
    whitney = self.x**2*self.y-self.z**2
    logdf = LogarithmicDifferentialForms(whitney)
    homology = logdf.homology("equivarient")
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:0,2:0,3:0})

  def test_relative_complex_0_crossing(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    homology = logdf.chain_complex("relative",None,0)
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:2,2:1,3:0})

  def test_relative_complex_0_whitney(self):
    whitney = self.x**2*self.y-self.z**2
    logdf = LogarithmicDifferentialForms(whitney)
    homology = logdf.chain_complex("relative",None,0)
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:0,2:0,3:0})

  def test_relative_homology_0_crossing(self):
    crossing = self.x*self.y*self.z
    logdf = LogarithmicDifferentialForms(crossing)
    homology = logdf.homology("relative",0)
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:2,2:1,3:0})

  def test_relative_homology_0_whitney(self):
    whitney = self.x**2*self.y-self.z**2
    logdf = LogarithmicDifferentialForms(whitney)
    homology = logdf.homology("relative",0)
    homology_size = {}
    for i,c in homology.iteritems():
      homology_size[i] = len(c)
    self.assertEqual(homology_size,{0:1,1:0,2:0,3:0})