Properties of GKM graphs

These are some of the main properties of GKM graphs.

General Properties

The following properties make sense for any abstract GKM graph.

GKMtools.valencyFunction
valency(G::AbstractGKM_graph) -> Int64

Return the valency of G, i.e. the number of flags at each vertex.

Warning

This function does not check if G is a valid GKM graph (use isvalid to check this). In particular, it does not check if every vertex has the same degree. The returned value is the degree of vertex 1.

Example:

The valency of the GKM graph of $\mathbb{P}^3$ is 3, since all of the fixed points $[1:0:0:0], \dots, [0:0:0:1]$ are connected to each other via some $T$-invariant $\mathbb{P}^1$'s. For example, $[1:0:0:0]$ and $[0:1:0:0]$ are connected by $\{[x:y:0:0] : x,y\in\mathbb{C}\}$.

julia> valency(projective_space(GKM_graph, 3))
3
julia> valency(grassmannian(GKM_graph, 2, 4)) # The Grassmannian of 2-planes in C^4
4
julia> valency(flag_variety(GKM_graph, [1, 1, 1, 1])) # The variety of full flags in C^4
6
source
GKMtools.rank_torusFunction
rank_torus(G::AbstractGKM_graph) -> Int64

Return the rank of the torus acting on G. That is, the rank of the character group.

Examples

By default, the torus acting on $\mathbb{P}^n$ is $(\mathbb{C}^\times)^{n+1}$, acting by rescaling the homogeneous coordinates.

julia> P3 = projective_space(GKM_graph, 3);

julia> rank_torus(P3)
4

Taking products adds the rank:

julia> H6 = gkm_graph_of_toric(hirzebruch_surface(NormalToricVariety, 6));

julia> rank_torus(H6)
4
julia> rank_torus(H6 * P3)
8
source
GKMtools.is_compactFunction
is_compact(G::AbstractGKM_graph) -> Bool

Return true if G is compact, i.e. all flags at all vertices are associated with edges (no standalone flags).

Example

julia> G = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> is_compact(G)
true

julia> M = G.M;

julia> add_standalone_flag!(G, 1, gens(M)[1]);

julia> add_standalone_flag!(G, 2, gens(M)[2]);

julia> add_standalone_flag!(G, 3, gens(M)[3]);

julia> is_compact(G)
false
source
GKMtools.is2_indepFunction
is2_indep(G::AbstractGKM_graph) -> Bool

Return true if G is 2-independent, i.e. the weights of every two flags at a vertex are linearly independent.

source
GKMtools.is3_indepFunction
is3_indep(G::AbstractGKM_graph) -> Bool

Return true if G is 3-independent, i.e. the weights of every three flags at a vertex are linearly independent.

Example

The weights of $\mathbb{P}^3$ at the fixed point $[1:0:0:0]$ are $\{t_i-t_0:i\in\{1, 2, 3\}\}$, which are linearly independent over $\mathbb{C}$.

julia> is3_indep(projective_space(GKM_graph, 3))
true

The variety of complete flags in $\mathbb{C}^3$ is an example of a GKM graph that is not 3-independent:

julia> G = flag_variety(GKM_graph, [1, 1, 1])
GKM graph with 6 nodes, valency 3 and axial function:
13 -> 12 => (0, -1, 1)
21 -> 12 => (-1, 1, 0)
23 -> 13 => (-1, 1, 0)
23 -> 21 => (-1, 0, 1)
31 -> 13 => (-1, 0, 1)
31 -> 21 => (0, -1, 1)
32 -> 12 => (-1, 0, 1)
32 -> 23 => (0, -1, 1)
32 -> 31 => (-1, 1, 0)

julia> is3_indep(G)
false
Warning

This function throws an error if the valency of G is less than 3, since in this case it is not possible to pick three different flags at a vertex.

source

Symplectic / almost complex properties

The following properties are well-defined as soon as there is a well-defined first Chern class. This holds for GKM graphs coming from $T$-compatibly almost complex manifolds and from $T$-compatibly symplectic manifolds. In the latter case, all choices of almost complex structures compatible with the symplectic form yield the same first Chern class, making the properties below intrinsic to the symplectic structure.

GKMtools.fano_indexFunction
fano_index(G::AbstractGKM_graph) -> ZZRingElem

Return the Fano index of the GKM graph, which is the gcd of the first Chern numbers of all its edges.

Examples

julia> P2 = projective_space(GKM_graph, 2);

julia> fano_index(P2)
3

julia> F3 = flag_variety(GKM_graph, [1, 1, 1]);

julia> fano_index(F3)
2
source
GKMtools.pseudo_indexFunction
pseudo_index(G::AbstractGKM_graph) -> ZZRingElem

Return the pseudo index of the GKM graph, which is the minimum of the first Chern numbers of all its edges.

Examples

The examples below show that the pseudo-index differs from the Fano index in general.

julia> P2 = projective_space(GKM_graph, 2);

julia> fano_index(P2), pseudo_index(P2)
(3, 3)

julia> P3 = projective_space(GKM_graph, 3);

julia> fano_index(P3), pseudo_index(P3)
(4, 4)

julia> G = P2 * P3;

julia> fano_index(G), pseudo_index(G)
(1, 3)

julia> T = gkm_3d_twisted_flag();

julia> fano_index(T), pseudo_index(T)
(2, 0)
source
GKMtools.is_strictly_nefFunction
is_strictly_nef(G::AbstractGKM_graph) -> Bool

Return true if and only if the Chern numbers of all curve classes corresponding to edges of the GKM graph are strictly positive.

Examples

julia> F3 = flag_variety(GKM_graph, [1,1,1]);

julia> print_curve_classes(F3)
13 -> 12: (0, 1), Chern number: 2
21 -> 12: (1, 0), Chern number: 2
23 -> 13: (1, 1), Chern number: 4
23 -> 21: (0, 1), Chern number: 2
31 -> 13: (1, 0), Chern number: 2
31 -> 21: (1, 1), Chern number: 4
32 -> 12: (1, 1), Chern number: 4
32 -> 23: (1, 0), Chern number: 2
32 -> 31: (0, 1), Chern number: 2

julia> is_strictly_nef(F3)
true

julia> H5 = gkm_graph_of_toric(hirzebruch_surface(NormalToricVariety, 5))
GKM graph with 4 nodes, valency 2 and axial function:
2 -> 1 => (1, 0, -1, 0)
3 -> 2 => (5, 1, 0, -1)
4 -> 1 => (0, 1, 5, -1)
4 -> 3 => (-1, 0, 1, 0)

julia> print_curve_classes(H5)
2 -> 1: (-5, 1), Chern number: -3
3 -> 2: (1, 0), Chern number: 2
4 -> 1: (1, 0), Chern number: 2
4 -> 3: (0, 1), Chern number: 7

julia> is_strictly_nef(H5)
false
source

Compact Hamiltonian properties

The following properties have natural geometric meaning for GKM graphs of compact Hamiltonian GKM spaces. That being said, they can be defined and checked for any abstract GKM graph, although one needs to be careful about their geometric interpretation outside of the Hamiltonian setting.

Oscar.betti_numbersFunction
betti_numbers(G::AbstractGKM_graph) -> Vector{Int64}

Return the array betti_numbers such that betti_numbers[i+1] is the 2i-th combinatorial Betti number for i from 0 to the valency of G, as defined in [GZ01, section 1.3].

Note
  • i ranges from 0 to the valency of G, that can be obtained by valency(G).
  • From [GZ01, Theorem 1.3.2], the combinatorial Betti numbers equal the Betti numbers of the underlying GKM space if the torus action is Hamiltonian. This holds automatically for smooth projective varieties with algebraic torus action (cf. [MFK94, Example 8.1 (ii)]).
Warning
  • betti_numbers[1] is the 0-th Betti number, since Julia arrays are 1-based and not 0-based.
  • Currently only implemented for compact GKM spaces.

Examples

julia> H6 = gkm_graph_of_toric(hirzebruch_surface(NormalToricVariety, 6));

julia> betti_numbers(H6)
3-element Vector{Int64}:
 1
 2
 1
source
GKMtools.index_periodic_bettiFunction
index_periodic_betti(G::AbstractGKM_graph) -> Vector{Int64}

Return the index-periodic Betti numbers of a compact GKM graph.

For a GKM graph with Fano index $p$, this function computes the sums of Betti numbers grouped by their residue class modulo $p$. Specifically, the $i$-th entry (for $i$ from $1$ to $p$) contains the sum of all Betti numbers $b_j$ where $j \equiv i$ (mod $p$).

This is as defined in [BGL+25, before Theorem 2.2].

Note
  • The returned vector has length equal to the Fano index $p$.
  • Entry i corresponds to the sum of Betti numbers with index congruent $i$ (mod $p$).
  • Requires the GKM graph to be compact.

Examples

julia> P2 = projective_space(GKM_graph, 2);

julia> index_periodic_betti(P2)
3-element Vector{Int64}:
 1
 1
 1
source

Index properties

GKMtools.is_genericFunction
is_generic(G::AbstractGKM_graph, xi) -> Bool

Return true if xi is a generic element of the weight lattice G.M, i.e. the Euclidean pairing of xi with every flag weight of G is nonzero.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> g1, g2, g3 = gens(P2.M);

julia> is_generic(P2, g1)
false

julia> is_generic(P2, g1 + g2)
false

julia> is_generic(P2, g1 + 2*g2 + 4*g3)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> h1, h2, h3 = gens(P2_Q.M);

julia> is_generic(P2_Q, h1 + 2*h2 + 4*h3)
true
source
GKMtools.xi_indexFunction
xi_index(G::AbstractGKM_graph, xi, v::Int64) -> Int64

Return the xi-index of vertex v of G, i.e. the number of flags at v whose weight pairs negatively with xi. Requires xi to be generic at v.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> g1, g2, g3 = gens(P2.M);

julia> xi = g1 + 2*g2 + 4*g3;

julia> [xi_index(P2, xi, v) for v in 1:3]
3-element Vector{Int64}:
 2
 1
 0

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> h1, h2, h3 = gens(P2_Q.M);

julia> [xi_index(P2_Q, h1 + 2*h2 + 4*h3, v) for v in 1:3]
3-element Vector{Int64}:
 2
 1
 0
source
GKMtools.is_index_increasingFunction
is_index_increasing(G::AbstractGKM_graph, xi) -> Bool

Return true if xi is generic and index increasing on G. For each edge e = {v, w}, orient it as (v, w) so that the weight of Edge(v, w) pairs positively with xi; then require the xi-index at w to be strictly greater than the xi-index at v.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> g1, g2, g3 = gens(P2.M);

julia> is_index_increasing(P2, g1 + 2*g2 + 4*g3)
true

julia> is_index_increasing(P2, g1)
false

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> h1, h2, h3 = gens(P2_Q.M);

julia> is_index_increasing(P2_Q, h1 + 2*h2 + 4*h3)
true
source
GKMtools.is_weakly_index_increasingFunction
is_weakly_index_increasing(G::AbstractGKM_graph, xi) -> Bool

Return true if xi is generic and weakly index increasing on G. For each edge e = {v, w}, orient it as (v, w) so that the weight of Edge(v, w) pairs positively with xi; then require the xi-index at w to be greater than or equal to the xi-index at v.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> g1, g2, g3 = gens(P2.M);

julia> is_weakly_index_increasing(P2, g1 + 2*g2 + 4*g3)
true

julia> is_weakly_index_increasing(P2, g1)
false

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> h1, h2, h3 = gens(P2_Q.M);

julia> is_weakly_index_increasing(P2_Q, h1 + 2*h2 + 4*h3)
true
source
GKMtools.generic_xi_representativesFunction
generic_xi_representatives(G::AbstractGKM_graph) -> Vector

Return one representative per connected component of the set of generic directions xi in the weight lattice of G. Each representative lives in G.M (as a primitive element of the lattice if G.M is a free ZZ-module).

The components of the generic locus are the open chambers of the hyperplane arrangement cut out by the flag weights of G, viewed in G.M tensored with QQ.

Example

On P^1 there is a single hyperplane so two chambers:

julia> P1 = projective_space(GKM_graph, 1)
GKM graph with 2 nodes, valency 1 and axial function:
2 -> 1 => (-1, 1)

julia> generic_xi_representatives(P1)
2-element Vector{AbstractAlgebra.Generic.FreeModuleElem{ZZRingElem}}:
 (-1, 0)
 (0, -1)

julia> P1_Q = convert_weights(P1); # Let's also test with QQ-weights

julia> generic_xi_representatives(P1_Q)
2-element Vector{AbstractAlgebra.Generic.FreeModuleElem{QQFieldElem}}:
 (-1, 0)
 (0, -1)

On P^2 there are 6 chambers corresponding to the 6 orderings of the three coordinates of xi:

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> reps = generic_xi_representatives(P2);

julia> length(reps)
6

julia> all(xi -> is_generic(P2, xi), reps)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> reps_Q = generic_xi_representatives(P2_Q);

julia> length(reps_Q)
6

julia> all(xi -> is_generic(P2_Q, xi), reps_Q)
true

Non-compact example: the total space of O(1) + O(-1) on P^1:

julia> T = total_space(vector_bundle_O(1, [1, -1]))
GKM graph with 2 nodes, valency 3 and axial function:
2 -> 1 => (-1, 1, 0, 0)
Standalone flags:
1.2 => (0, 0, 1, 0)
1.3 => (0, 0, 0, 1)
2.2 => (-1, 1, 1, 0)
2.3 => (1, -1, 0, 1)

julia> length(generic_xi_representatives(T))
18

julia> T_Q = convert_weights(T); # Let's also test with QQ-weights

julia> length(generic_xi_representatives(T_Q))
18
source
GKMtools.index_increasing_xi_representativesFunction
index_increasing_xi_representatives(G::AbstractGKM_graph) -> Vector

Return one representative per connected component of the generic locus on which xi is index increasing. Each representative lives in G.M.

Example

Every chamber of P^2 is index-increasing:

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> reps = index_increasing_xi_representatives(P2);

julia> length(reps)
6

julia> all(xi -> is_index_increasing(P2, xi), reps)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> reps_Q = index_increasing_xi_representatives(P2_Q);

julia> length(reps_Q)
6

julia> all(xi -> is_index_increasing(P2_Q, xi), reps_Q)
true

For the non-compact total space of O(1) + O(-1) on P^1, only some chambers are strictly index-increasing (compare the count with the 18 generic chambers):

julia> T = total_space(vector_bundle_O(1, [1, -1]));

julia> length(index_increasing_xi_representatives(T))
14

julia> T_Q = convert_weights(T); # Let's also test with QQ-weights

julia> length(index_increasing_xi_representatives(T_Q))
14
source
GKMtools.weakly_index_increasing_xi_representativesFunction
weakly_index_increasing_xi_representatives(G::AbstractGKM_graph) -> Vector

Return one representative per connected component of the generic locus on which xi is weakly index increasing. Each representative lives in G.M.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> reps = weakly_index_increasing_xi_representatives(P2);

julia> length(reps)
6

julia> all(xi -> is_weakly_index_increasing(P2, xi), reps)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> reps_Q = weakly_index_increasing_xi_representatives(P2_Q);

julia> length(reps_Q)
6

julia> all(xi -> is_weakly_index_increasing(P2_Q, xi), reps_Q)
true

On the non-compact total space of O(1) + O(-1) on P^1, every generic chamber turns out to be weakly index-increasing even though only 14 of them are strictly index-increasing:

julia> T = total_space(vector_bundle_O(1, [1, -1]));

julia> length(weakly_index_increasing_xi_representatives(T))
18

julia> T_Q = convert_weights(T); # Let's also test with QQ-weights

julia> length(weakly_index_increasing_xi_representatives(T_Q))
18
source
GKMtools.admits_index_increasing_xiFunction
admits_index_increasing_xi(G::AbstractGKM_graph) -> Tuple{Bool, FreeModuleElem}

Return (true, xi) if G admits some generic index-increasing direction xi in G.M, where xi is the first such direction found during chamber enumeration. Return (false, zero(G.M)) otherwise.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> ok, xi = admits_index_increasing_xi(P2);

julia> ok
true

julia> is_index_increasing(P2, xi)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> ok_Q, xi_Q = admits_index_increasing_xi(P2_Q);

julia> ok_Q && is_index_increasing(P2_Q, xi_Q)
true

julia> G = gkm_2d([1 0; 1 1; 0 1; -1 0; -1 -1; 0 -1]) # Blowup of P1 x P1 in 2 points
GKM graph with 6 nodes, valency 2 and axial function:
2 -> 1 => (-1, 0)
3 -> 2 => (-1, -1)
4 -> 3 => (0, -1)
5 -> 4 => (1, 0)
6 -> 1 => (0, -1)
6 -> 5 => (1, 1)

julia> admits_index_increasing_xi(G)
(false, (0, 0))

The same is true on the non-compact total space of O(1) + O(-1) on P^1:

julia> T = total_space(vector_bundle_O(1, [1, -1]));

julia> ok, xi = admits_index_increasing_xi(T);

julia> ok
true

julia> is_index_increasing(T, xi)
true

julia> T_Q = convert_weights(T); # Let's also test with QQ-weights

julia> ok_Q, xi_Q = admits_index_increasing_xi(T_Q);

julia> ok_Q && is_index_increasing(T_Q, xi_Q)
true
source
GKMtools.admits_weakly_index_increasing_xiFunction
admits_weakly_index_increasing_xi(G::AbstractGKM_graph) -> Tuple{Bool, FreeModuleElem}

Return (true, xi) if G admits some generic weakly-index-increasing direction xi in G.M, where xi is the first such direction found during chamber enumeration. Return (false, zero(G.M)) otherwise.

Example

julia> P2 = projective_space(GKM_graph, 2)
GKM graph with 3 nodes, valency 2 and axial function:
2 -> 1 => (-1, 1, 0)
3 -> 1 => (-1, 0, 1)
3 -> 2 => (0, -1, 1)

julia> ok, xi = admits_weakly_index_increasing_xi(P2);

julia> ok
true

julia> is_weakly_index_increasing(P2, xi)
true

julia> P2_Q = convert_weights(P2); # Let's also test with QQ-weights

julia> ok_Q, xi_Q = admits_weakly_index_increasing_xi(P2_Q);

julia> ok_Q && is_weakly_index_increasing(P2_Q, xi_Q)
true

julia> G = gkm_2d([1 0; 1 1; 0 1; -1 0; -1 -1; 0 -1]) # Blowup of P1 x P1 in 2 points
GKM graph with 6 nodes, valency 2 and axial function:
2 -> 1 => (-1, 0)
3 -> 2 => (-1, -1)
4 -> 3 => (0, -1)
5 -> 4 => (1, 0)
6 -> 1 => (0, -1)
6 -> 5 => (1, 1)

julia> admits_weakly_index_increasing_xi(G)
(true, (-1, -1))

Non-compact total space of O(1) + O(-1) on P^1:

julia> T = total_space(vector_bundle_O(1, [1, -1]));

julia> ok, xi = admits_weakly_index_increasing_xi(T);

julia> ok
true

julia> is_weakly_index_increasing(T, xi)
true

julia> T_Q = convert_weights(T); # Let's also test with QQ-weights

julia> ok_Q, xi_Q = admits_weakly_index_increasing_xi(T_Q);

julia> ok_Q && is_weakly_index_increasing(T_Q, xi_Q)
true
source