Base class for polyhedra over \(\QQ\)#
- class sage.geometry.polyhedron.base_QQ.Polyhedron_QQ(parent, Vrep, Hrep, Vrep_minimal=None, Hrep_minimal=None, pref_rep=None, mutable=False, **kwds)#
Bases:
Polyhedron_base
Base class for Polyhedra over \(\QQ\)
- Hstar_function(acting_group=None, output=None)#
Return \(H^*\) as a rational function in \(t\) with coefficients in the ring of class functions of the
acting_group
of this polytope.Here, \(H^*(t) = \sum_{m} \chi_{m\text{self}}t^m \det(Id-\rho(t))\). The irreducible characters of
acting_group
form an orthonormal basis for the ring of class functions with values in \(\CC\). The coefficients of \(H^*(t)\) are expressed in this basis.INPUT:
acting_group
– (default=None) a permgroup object. A subgroup of the polytope’srestricted_automorphism_group
. IfNone
, it is set to the fullrestricted_automorphism_group
of the polytope. The acting group should always use output=’permutation’.output
– string. an output option. The allowed values are:None
(default): returns the rational function \(H^*(t)\). \(H^*\) is a rational function in \(t\) with coefficients in the ring of class functions.'e_series_list'
: Returns a list of the ehrhart_series for the fixed_subpolytopes of each conjugacy class representative.'determinant_vec'
: Returns a list of the determinants of \(Id-\rho*t\) for each conjugacy class representative.'Hstar_as_lin_comb'
: Returns a vector of the coefficients of the irreducible representations in the expression of \(H^*\).'prod_det_es'
: Returns a vector of the product of determinants and the Ehrhart series.'complete'
: Returns a list with Hstar, Hstar_as_lin_comb, character table of the acting group, and whether Hstar is effective.
OUTPUT:
The default output is the rational function \(H^*\). \(H^*\) is a rational function in \(t\) with coefficients in the ring of class functions. There are several output options to see the intermediary outputs of the function.
EXAMPLES:
The \(H^*\)-polynomial of the standard (\(d-1\))-dimensional simplex \(S = conv(e_1, \dots, e_d)\) under its
restricted_automorphism_group
is equal to 1 = \(\chi_{trivial}\) (Prop 6.1 [Stap2011]). Here is the computation for the 3-dimensional standard simplex:sage: S = polytopes.simplex(3, backend = 'normaliz'); S # optional - pynormaliz A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices sage: G = S.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: G.is_isomorphic(SymmetricGroup(4)) # optional - pynormaliz True sage: Hstar = S._Hstar_function_normaliz(G); Hstar # optional - pynormaliz chi_4 sage: G.character_table() # optional - pynormaliz [ 1 -1 1 1 -1] [ 3 -1 0 -1 1] [ 2 0 -1 2 0] [ 3 1 0 -1 -1] [ 1 1 1 1 1]
The next example is Example 7.6 in [Stap2011], and shows that \(H^*\) is not always a polynomial. Let P be the polytope with vertices \(\pm(0,0,1),\pm(1,0,1), \pm(0,1,1), \pm(1,1,1)\) and let G = \(\Zmod{2}\) act on P as follows:
sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: K = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: G = K.subgroup(gens = [K([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: conj_reps = G.conjugacy_classes_representatives() # optional - pynormaliz sage: Dict = P.permutations_to_matrices(conj_reps, acting_group = G) # optional - pynormaliz sage: list(Dict.keys())[0] # optional - pynormaliz (0,2)(1,3)(4,6)(5,7) sage: list(Dict.values())[0] # optional - pynormaliz [-1 0 1 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] sage: len(G) # optional - pynormaliz 2 sage: G.character_table() # optional - pynormaliz [ 1 1] [ 1 -1]
Then we calculate the rational function \(H^*(t)\):
sage: Hst = P._Hstar_function_normaliz(G); Hst # optional - pynormaliz (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3 + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1)
To see the exact as written in [Stap2011], we can format it as
'Hstar_as_lin_comb'
. The first coordinate is the coefficient of the trivial character; the second is the coefficient of the sign character:sage: lin = P._Hstar_function_normaliz(G,output = 'Hstar_as_lin_comb'); lin # optional - pynormaliz ((t^4 + 3*t^3 + 8*t^2 + 3*t + 1)/(t + 1), (3*t^3 + 2*t^2 + 3*t)/(t + 1))
- ehrhart_polynomial(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)#
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 article 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 ofself
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
); IfTrue
, print the whole output of the LattE command.
The following options are passed to the LattE command, for details consult the LattE documentation:
dual
- boolean; triangulate and signed-decompose in the dual spaceirrational_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) ofmaxdet
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.See also
latte
the interface to LattE Integrale PyNormalizEXAMPLES:
To start, we find the Ehrhart polynomial of a three-dimensional
simplex
, first usingengine='latte'
. Leaving the engine unspecified sets theengine
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, thesimplex
must be defined withbackend='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'
- ehrhart_quasipolynomial(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)#
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 invariable
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 ofself
must be set to ‘normaliz’.
When the
engine
is ‘latte’, the additional input values are:verbose
- boolean (default:False
); IfTrue
, print the whole output of the LattE command.
The following options are passed to the LattE command, for details consult the LattE documentation:
dual
- boolean; triangulate and signed-decompose in the dual spaceirrational_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) ofmaxdet
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.
See also
latte
the interface to LattE Integrale PyNormalizWarning
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
- fixed_subpolytope(vertex_permutation)#
Return the fixed subpolytope of this polytope by the cyclic action of
vertex_permutation
.The fixed subpolytope of this polytope under the
vertex_permutation
is the subset of this polytope that is fixed pointwise.INPUT:
vertex_permutation
– permutation; a permutation of the vertices ofself
.
OUTPUT:
A subpolytope of
self
.Note
The vertex_permutation is obtained as a permutation of the vertices represented as a permutation. For example, vertex_permutation = self.restricted_automorphism_group(output=’permutation’).
Requiring a lattice polytope as opposed to a rational polytope as input is purely conventional.
EXAMPLES:
The fixed subpolytopes of the cube can be obtained as follows:
sage: Cube = polytopes.cube(backend = 'normaliz') # optional - pynormaliz sage: AG = Cube.restricted_automorphism_group(output='permutation') # optional - pynormaliz sage: reprs = AG.conjugacy_classes_representatives() # optional - pynormaliz
The fixed subpolytope of the identity element of the group is the entire cube:
sage: reprs[0] # optional - pynormaliz () sage: Cube.fixed_subpolytope(vertex_permutation = reprs[0]) # optional - pynormaliz A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 8 vertices sage: _.vertices() # optional - pynormaliz (A vertex at (-1, -1, -1), A vertex at (-1, -1, 1), A vertex at (-1, 1, -1), A vertex at (-1, 1, 1), A vertex at (1, -1, -1), A vertex at (1, -1, 1), A vertex at (1, 1, -1), A vertex at (1, 1, 1))
You can obtain non-trivial examples:
sage: fsp = Cube.fixed_subpolytope(AG([(0,1),(2,3),(4,5),(6,7)]));fsp # optional - pynormaliz A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 4 vertices sage: fsp.vertices() # optional - pynormaliz (A vertex at (-1, -1, 0), A vertex at (-1, 1, 0), A vertex at (1, -1, 0), A vertex at (1, 1, 0))
The next example shows that fixed_subpolytope works for rational polytopes:
sage: P = Polyhedron(vertices=[[0],[1/2]], backend='normaliz') # optional - pynormaliz sage: P.vertices() # optional - pynormaliz (A vertex at (0), A vertex at (1/2)) sage: G = P.restricted_automorphism_group(output='permutation');G # optional - pynormaliz Permutation Group with generators [(0,1)] sage: len(G) # optional - pynormaliz 2 sage: fixed_set = P.fixed_subpolytope(G.gens()[0]); fixed_set # optional - pynormaliz A 0-dimensional polyhedron in QQ^1 defined as the convex hull of 1 vertex sage: fixed_set.vertices_list() # optional - pynormaliz [[1/4]]
- fixed_subpolytopes(conj_class_reps)#
Return the fixed subpolytopes of this polytope under the actions of the given conjugacy class representatives.
The
conj_class_reps
are representatives of the conjugacy classes of a subgroup of the automorphism group of this polytope. For an element of the automorphism group, the fixed subpolytope is the subset of this polytope that is fixed pointwise.INPUT:
conj_class_reps
– a list of representatives of the conjugacy classes of the subgroup of therestricted_automorphism_group
of the polytope. Each element is written as a permutation of the vertices of the polytope.
OUTPUT:
A dictionary where the elements of
conj_class_reps
are keys and the fixed subpolytopes are values.Note
Two elements in the same conjugacy class fix lattice-isomorphic subpolytopes.
EXAMPLES:
Here is an example for the square:
sage: p = polytopes.hypercube(2, backend = 'normaliz'); p # optional - pynormaliz A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices sage: aut_p = p.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: aut_p.order() # optional - pynormaliz 8 sage: conj_list = aut_p.conjugacy_classes_representatives(); # optional - pynormaliz sage: fixedpolytopes_dictionary = p.fixed_subpolytopes(conj_list) # optional - pynormaliz sage: fixedpolytopes_dictionary[aut_p([(0,3),(1,2)])] # optional - pynormaliz A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex
- integral_points_count(verbose=False, use_Hrepresentation=False, explicit_enumeration_threshold=1000, preprocess=True, **kwds)#
Return the number of integral points in the polyhedron.
This method uses the optional package
latte_int
if an estimate for lattice points based on bounding boxes exceedsexplicit_enumeration_threshold
.INPUT:
verbose
(boolean;False
by default) – whether to display verbose output.use_Hrepresentation
- (boolean;False
by default) – whether to send the H or V representation to LattEpreprocess
- (boolean;True
by default) – whether, if the integral hull is known to lie in a coordinate hyperplane, to tighten bounds to reduce dimension
See also
latte
the interface to LattE interfacesEXAMPLES:
sage: P = polytopes.cube() sage: P.integral_points_count() 27 sage: P.integral_points_count(explicit_enumeration_threshold=0) # optional - latte_int 27
We enlarge the polyhedron to force the use of the generating function methods implemented in LattE integrale, rather than explicit enumeration.
sage: (1000000000*P).integral_points_count(verbose=True) # optional - latte_int This is LattE integrale… … Total time:… 8000000012000000006000000001
We shrink the polyhedron a little bit:
sage: Q = P*(8/9) sage: Q.integral_points_count() 1 sage: Q.integral_points_count(explicit_enumeration_threshold=0) # optional - latte_int 1
Unbounded polyhedra (with or without lattice points) are not supported:
sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) sage: P.integral_points_count() Traceback (most recent call last): ... NotImplementedError: ... sage: P = Polyhedron(vertices=[[1, 1]], rays=[[1, 1]]) sage: P.integral_points_count() Traceback (most recent call last): ... NotImplementedError: ...
“Fibonacci” knapsacks (preprocessing helps a lot):
sage: def fibonacci_knapsack(d, b, backend=None): ....: lp = MixedIntegerLinearProgram(base_ring=QQ) ....: x = lp.new_variable(nonnegative=True) ....: lp.add_constraint(lp.sum(fibonacci(i+3)*x[i] for i in range(d)) <= b) ....: return lp.polyhedron(backend=backend) sage: fibonacci_knapsack(20, 12).integral_points_count() # does not finish with preprocess=False 33
- is_effective(Hstar, Hstar_as_lin_comb)#
Test for the effectiveness of the
Hstar
series of this polytope.The
Hstar
series of the polytope is determined by the action of a subgroup of the polytope’srestricted_automorphism_group
. TheHstar
series is effective if it is a polynomial in \(t\) and the coefficient of each \(t^i\) is an effective character in the ring of class functions of the acting group. A character \(\rho\) is effective if the coefficients of the irreducible representations in the expression of \(\rho\) are non-negative integers.INPUT:
Hstar
– a rational function in \(t\) with coefficients in the ring of class functions.Hstar_as_lin_comb
– vector. The coefficients of the irreducible representations of the acting group in the expression ofHstar
as a linear combination of irreducible representations with coefficients in the field of rational functions in \(t\).
OUTPUT:
Boolean. Whether the
Hstar
series is effective.See also
EXAMPLES:
The \(H^*\) series of the two-dimensional permutahedron under the action of the symmetric group is effective:
sage: p3 = polytopes.permutahedron(3, backend = 'normaliz') # optional - pynormaliz sage: G = p3.restricted_automorphism_group(output='permutation') # optional - pynormaliz sage: reflection12 = G([(0,2),(1,4),(3,5)]) # optional - pynormaliz sage: reflection23 = G([(0,1),(2,3),(4,5)]) # optional - pynormaliz sage: S3 = G.subgroup(gens=[reflection12, reflection23]) # optional - pynormaliz sage: S3.is_isomorphic(SymmetricGroup(3)) # optional - pynormaliz True sage: [Hstar, Hlin] = [p3.Hstar_function(S3), p3.Hstar_function(S3, output = 'Hstar_as_lin_comb')] # optional - pynormaliz sage: p3.is_effective(Hstar,Hlin) # optional - pynormaliz True
If the \(H^*\)-series is not polynomial, then it is not effective:
sage: P = Polyhedron(vertices=[[0,0,1],[0,0,-1],[1,0,1],[-1,0,-1],[0,1,1], # optional - pynormaliz ....: [0,-1,-1],[1,1,1],[-1,-1,-1]],backend='normaliz') # optional - pynormaliz sage: G = P.restricted_automorphism_group(output = 'permutation') # optional - pynormaliz sage: H = G.subgroup(gens = [G([(0,2),(1,3),(4,6),(5,7)])]) # optional - pynormaliz sage: Hstar = P.Hstar_function(H); Hstar # optional - pynormaliz (chi_0*t^4 + (3*chi_0 + 3*chi_1)*t^3 + (8*chi_0 + 2*chi_1)*t^2 + (3*chi_0 + 3*chi_1)*t + chi_0)/(t + 1) sage: Hstar_lin = P.Hstar_function(H, output = 'Hstar_as_lin_comb') # optional - pynormaliz sage: P.is_effective(Hstar, Hstar_lin) # optional - pynormaliz False