@@ -3577,6 +3577,243 @@ def stanley_reisner_ring(self, base_ring=ZZ):
35773577 products .append (prod )
35783578 return R .quotient (products )
35793579
3580+ def algebraic_shift (self , form = "exterior" , iterations = 5 , certificate = False , check_shift = False , ** random_mat_options ):
3581+ r"""
3582+ Returns the algebraically shifted complex of this simplicial complex.
3583+
3584+ Given a total order on the vertices of ``self``, define the partial
3585+ order on `k`-faces as `f\leq g` if and only if `f_1\leq g_1, \dots, f_k\leq
3586+ g_k`. A `k`-family is called `\emph{shifted}` if it is a lower ideal of
3587+ this partially ordered set. There the ``exterior`` and ``symmetric``
3588+ shifting are two operations giving shifted complex from the original
3589+ simplicial complex.
3590+
3591+ INPUT:
3592+
3593+ - ``form`` -- string (default: ``'exterior'``); the type of shifting to
3594+ do. Can be either ``'exterior'`` or ``'symmetric'``.
3595+
3596+ - ``iterations`` -- integer (default: `5`); the number of iterations to be
3597+ used to certify the output.
3598+
3599+ - ``certificate`` - boolean: whether to return the number of occurences
3600+ of the different candidates.
3601+
3602+ - ``check_shift`` - boolean: whether to check if the output is a
3603+ shifted complex.
3604+
3605+ - ``random_mat_options`` - a dictionary; the options to create the
3606+ random matrix used. If set to ``None``, the algorithm uses the
3607+ default options of ``random_matrix``.
3608+
3609+ OUTPUT:
3610+
3611+ A shifted simplicial complex.
3612+
3613+ EXAMPLES::
3614+
3615+ sage: G = graphs.CompleteBipartiteGraph(3,3)
3616+ sage: K33 = SimplicialComplex([e[:2] for e in G.edges()])
3617+ sage: shifted_K33 = K33.algebraic_shift()
3618+ sage: sorted(shifted_K33.facets())
3619+ [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 2), (1, 3), (1, 4), (2, 3)]
3620+
3621+ sage: octahedron = SimplicialComplex([[0,1,2],[0,1,5],[0,2,4],[0,4,5],[1,2,3],[1,3,5],[2,3,4],[3,4,5]])
3622+ sage: shifted_octahedron = octahedron.algebraic_shift()
3623+ sage: shifted_octahedron.f_vector()
3624+ [1, 6, 12, 8]
3625+ sage: shifted_octahedron.homology()
3626+ {0: 0, 1: 0, 2: Z}
3627+ sage: print(sorted(shifted_octahedron.facets()))
3628+ [(0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 1, 5), (0, 2, 3), (0, 2, 4), (0, 2, 5), (1, 2, 3)]
3629+
3630+ sage: K4 = graphs.CompleteGraph(4)
3631+ sage: complement_K44 = SimplicialComplex([e[:2] for e in K4.disjoint_union(K4).edges()])
3632+ sage: shifted_complement_K44 = complement_K44.algebraic_shift()
3633+ sage: shifted_complement_K44.f_vector()
3634+ [1, 8, 12]
3635+ sage: shifted_complement_K44.homology()
3636+ {0: Z, 1: Z^6}
3637+ sage: print(sorted(shifted_complement_K44.facets())[-4:])
3638+ [((0, 1), (1, 1)), ((0, 2), (0, 3)), ((0, 2), (1, 0)), ((1, 3),)]
3639+
3640+ sage: cp = polytopes.cyclic_polytope(4,10)
3641+ sage: sc_cp = SimplicialComplex([tuple([cp.vertices().index(v) for v in f.vertices()]) for f in cp.faces(3)])
3642+ sage: shifted_sc_cp = sc_cp.algebraic_shift()
3643+ sage: shifted_sc_cp.f_vector()
3644+ [1, 10, 45, 70, 35]
3645+ sage: shifted_sc_cp.homology()
3646+ {0: 0, 1: 0, 2: 0, 3: Z}
3647+ sage: sorted(shifted_sc_cp.facets())[-5:]
3648+ [(0, 2, 3, 6), (0, 2, 3, 7), (0, 2, 3, 8), (0, 2, 3, 9), (1, 2, 3, 4)]
3649+
3650+ .. WARNING::
3651+
3652+ This method uses random matrices and is not guaranteed to give
3653+ the correct output. The higher the parameter `iterations` is, the
3654+ higher the probability of the output to be correct.
3655+
3656+ .. SEEALSO::
3657+
3658+ :meth:`sage.homology.examples.ShiftedComplex`
3659+
3660+ .. REFERENCES:
3661+
3662+ - [HH2011]_
3663+ - [Kal2001]_
3664+
3665+ TODO:
3666+
3667+ - implement symmetric shift
3668+ - put more example with certificate and different random matrices and
3669+ check shift
3670+ """
3671+ if form == "exterior" :
3672+ outputs = [ self ._ksets_exterior_shift (size , iterations , certificate ,
3673+ check_shift , ** random_mat_options )
3674+ for size in range (1 ,self .dimension ()+ 2 ) ]
3675+ if certificate :
3676+ shifted_sets , certifs = zip (* outputs )
3677+ faces = reduce (lambda x ,y : x + y , shifted_sets )
3678+ shifted_complex = SimplicialComplex (faces )
3679+ return shifted_complex , certifs
3680+ else :
3681+ shifted_sets = outputs
3682+ faces = reduce (lambda x ,y : x + y , shifted_sets )
3683+ shifted_complex = SimplicialComplex (faces )
3684+ return shifted_complex
3685+
3686+ def _ksets_exterior_shift (self , k , iterations , certificate , check_shift , ** random_mat_options ):
3687+ """
3688+ Returns a shifted `k`-set family obtained from the `k-1`-dim. faces of the
3689+ simplicial complex.
3690+
3691+ INPUT:
3692+
3693+ - ``k`` - positive integer; the size of the faces of ``self`` to shift.
3694+
3695+ - ``iterations`` - positive integer; the required number of iterations
3696+ giving the same result before giving the output.
3697+
3698+ - ``certificate`` - boolean: whether to return the
3699+ number of occurences of the different candidates.
3700+
3701+ - ``check_shift`` - boolean: whether to check if
3702+ the output is a shifted complex.
3703+
3704+ - ``random_mat_options`` - a dictionary; the options to create the
3705+ random matrix used. If set to ``None``, the algorithm uses the
3706+ default options of ``random_matrix``.
3707+
3708+ OUTPUT:
3709+
3710+ If ``certificate`` is true, returns a tuple containing:
3711+
3712+ 1. A shifted `k`-set family.
3713+ 2. A tuple giving the number of appearances of candidates, ordered
3714+ lexicographically.
3715+
3716+ If ``certificate`` is false:
3717+
3718+ - A shifted `k`-set family.
3719+
3720+ EXAMPLES:
3721+
3722+ .. WARNING::
3723+
3724+ This function is using a probabilistic algorithm. There is a (very)
3725+ small probability of returning a wrong output.
3726+
3727+ .. SEEALSO::
3728+
3729+ :meth:`algebraic_shift`
3730+ :meth:`sage.homology.examples.ShiftedComplex`
3731+ """
3732+ from sage .matrix .special import random_matrix
3733+ from sage .misc .flatten import flatten
3734+ from sage .homology .examples import ShiftedComplex
3735+
3736+ def is_shifted (kset_fam ):
3737+ if 0 in Set (flatten (kset_fam )): # Shifting operation does not like 0's
3738+ kset_fam = Set ([tuple ([i + 1 for i in kset ]) for kset in kset_fam ])
3739+ shifted_sc = SimplicialComplex (kset_fam )
3740+ new_shifted = ShiftedComplex (kset_fam )
3741+ if new_shifted != shifted_sc :
3742+ return False
3743+ else :
3744+ return True
3745+
3746+ kset_family = sorted (self .faces ()[k - 1 ])
3747+ size = len (kset_family )
3748+ vertices = self .vertices ()
3749+ n_vertices = len (vertices )
3750+ kset_as_indices = [tuple (vertices .index (i ) for i in kset ) for kset in kset_family ]
3751+
3752+ try :
3753+ ring = random_mat_options .pop ("ring" )
3754+ except KeyError :
3755+ ring = ZZ
3756+
3757+ found_candidate = False
3758+ candidates = {}
3759+ candidate = None
3760+ sorted_candidate = None
3761+ value_candidate = 0
3762+
3763+ while not found_candidate :
3764+ M = random_matrix (ring = ring , nrows = n_vertices , ** random_mat_options )
3765+
3766+ found_rank = 0
3767+ compound_matrix = matrix (size , 0 )
3768+ iter_cols = combinations (range (n_vertices ), k )
3769+ shifted_ksets = Set ()
3770+ while found_rank < size :
3771+ index_cols = iter_cols .next ()
3772+ new_column = matrix (size , 1 , [M .matrix_from_rows_and_columns (row_indices , index_cols ).det ()
3773+ for row_indices in kset_as_indices ])
3774+ compound_matrix = compound_matrix .augment (new_column )
3775+ new_rank = compound_matrix .rank ()
3776+ if new_rank > found_rank :
3777+ shifted_ksets += Set ([tuple (vertices [i ] for i in index_cols )])
3778+ found_rank = new_rank
3779+
3780+ if candidate is not None :
3781+ shifted = True
3782+ if check_shift :
3783+ shifted = is_shifted (shifted_ksets )
3784+ if shifted :
3785+ sorted_ksets = sorted (shifted_ksets )
3786+ if sorted_ksets < sorted_candidate : # Found a new candidate
3787+ candidate = shifted_ksets
3788+ sorted_candidate = sorted_ksets
3789+ candidates [candidate ] = 1
3790+ value_candidate = 1
3791+ elif sorted_ksets == sorted_candidate : # found the same candidate
3792+ candidates [candidate ] += 1
3793+ value_candidate = candidates [candidate ]
3794+ else : # is a bad candidate
3795+ if shifted_ksets in candidates .keys ():
3796+ candidates [shifted_ksets ] += 1
3797+ else :
3798+ candidates [shifted_ksets ] = 1
3799+ else :
3800+ shifted = True
3801+ if check_shift :
3802+ shifted = is_shifted (shifted_ksets )
3803+ if shifted :
3804+ candidate = shifted_ksets
3805+ sorted_candidate = sorted (shifted_ksets )
3806+ candidates [candidate ] = 1
3807+ value_candidate = 1
3808+
3809+ if value_candidate == iterations :
3810+ found_candidate = True
3811+
3812+ if certificate :
3813+ return candidate , sorted (candidates .values (), reverse = True )
3814+ else :
3815+ return candidate
3816+
35803817 def alexander_dual (self , is_mutable = True ):
35813818 """
35823819 The Alexander dual of this simplicial complex: according to
0 commit comments