Skip to content

Commit 186a03e

Browse files
committed
First version of k-shift
some more improvement on shift changed method for shifting First draft of exterior shifting Repaired counter and added references
1 parent 5c8d9e9 commit 186a03e

File tree

2 files changed

+244
-2
lines changed

2 files changed

+244
-2
lines changed

src/doc/en/reference/references/index.rst

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,6 +3406,8 @@ REFERENCES:
34063406
International Algorithmic Number Theory Symposium (pp. 357-371).
34073407
Springer Berlin Heidelberg, 2002.
34083408
3409+
.. [HH2011] Jürgen Herzog, Takayuki Hibi, *Monomial Ideals*. Springer GTM 260, 2011.
3410+
34093411
.. [HH2012] Victoria Horan and Glenn Hurlbert,
34103412
*Overlap Cycles for Steiner Quadruple Systems*,
34113413
2012, :arxiv:`1204.3215`
@@ -3908,8 +3910,11 @@ REFERENCES:
39083910
*The MD2 message-digest algorithm*; in
39093911
RFS 1319, (1992).
39103912
3911-
.. [Ka1993] Masaki Kashiwara, *The crystal base and Littelmann's
3912-
refined Demazure character formula*, Duke Math. J. 71
3913+
.. [Kal2001] Gil Kalai. *Algebraic Shifting*. Computational Commutative
3914+
Algebra and Combinatorics. (2001). 121--163.
3915+
3916+
.. [Kas1993] Masaki Kashiwara, The crystal base and Littelmann's
3917+
refined Demazure character formula, Duke Math. J. 71
39133918
(1993), no. 3, 839--858.
39143919
39153920
.. [Ka2003] \M. Kashiwara.

src/sage/topology/simplicial_complex.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)