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