Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
moves helper to utilities.py, added a few generators for lift maps
Browse files Browse the repository at this point in the history
  • Loading branch information
Rudi Pendavingh committed Jun 9, 2015
1 parent 109e5bb commit 3f41454
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 166 deletions.
4 changes: 2 additions & 2 deletions src/sage/matroids/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
from rank_matroid import RankMatroid
from circuit_closures_matroid import CircuitClosuresMatroid
from basis_matroid import BasisMatroid
from linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid, lift_cross_ratios
from utilities import setprint, newlabel, get_nonisomorphic_matroids
from linear_matroid import LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid
from utilities import setprint, newlabel, get_nonisomorphic_matroids, lift_cross_ratios
import lean_matrix
from extension import LinearSubclasses, MatroidExtensions
165 changes: 1 addition & 164 deletions src/sage/matroids/linear_matroid.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ from sage.matroids.matroid cimport Matroid
from basis_exchange_matroid cimport BasisExchangeMatroid
from lean_matrix cimport LeanMatrix, GenericMatrix, BinaryMatrix, TernaryMatrix, QuaternaryMatrix, IntegerMatrix, generic_identity
from set_system cimport SetSystem
from utilities import newlabel
from utilities import newlabel, lift_cross_ratios

from sage.matrix.matrix2 cimport Matrix
import sage.matrix.constructor
Expand Down Expand Up @@ -2700,169 +2700,6 @@ cdef class LinearMatroid(BasisExchangeMatroid):
data = (A, gs, reduced, getattr(self, '__custom_name'))
return sage.matroids.unpickling.unpickle_linear_matroid, (version, data)

def lift_cross_ratios(A, lift_map = None):
"""
Return a matrix which arises from the given matrix by lifting cross ratios.
INPUT:
- ``A`` -- a matrix over a ring ``source_ring``.
- ``lift_map`` -- a python dictionary, mapping each cross ratio of ``A`` to some element
of a target ring, and such that ``lift_map[source_ring(1)] = target_ring(1)``.
OUTPUT:
- ``Z`` -- a matrix over the ring ``target_ring``.
The intended use of this method is to create a (reduced) matrix representation of a
matroid ``M`` over a ring ``target_ring``, given a (reduced) matrix representation of
``A`` of ``M`` over a ring ``source_ring`` and a map ``lift_map`` from ``source_ring``
to ``target_ring``.
This method will create a unique candidate representation ``Z``, but will not verify
if ``Z`` is indeed a representation of ``M``. However, this is guaranteed if the
conditions of the lift theorem hold for the lift_map in combination with the matrix
``A``. These conditions that all cross ratios of ``A`` as well as ``1`` are keys of
``lift_map``, and
- if ``x, y`` are keys of ``lift_map``, and ``x+y == 1`` then
``lift_map[x] + lift_map[y] = lift_map[1]``
- if ``x, y, z`` are keys of ``lift_map``, and ``x*y == z`` then
``lift_map[x]*lift_map[y] = lift_map[z]``
- if ``x, y`` are keys of ``lift_map``, and ``x*y`` is not, then there is no key of
``lift_map`` such that ``lift_map[x]*lift_map[y] = lift_map[z]``
Finally, there are global conditions on the target field which depend on the matroid
``M`` represented by ``[ I A ]``. If ``M`` has a Fano minor, then in the target ring we
must have ``1+1 == 0``. If ``M`` has a NonFano minor, then in the target ring we must
have ``1+1 != 0``.
EXAMPLES::
sage: from sage.matroids.advanced import lift_cross_ratios, LinearMatroid
sage: R = GF(7)
sage: z = QQ['z'].0
sage: S = NumberField(z^2-z+1, 'z')
sage: to_sixth_root_of_unity = { R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**(-1)}
sage: A = Matrix(R, [[1, 0, 6, 1, 2],[6, 1, 0, 0, 1],[0, 6, 3, 6, 0]])
sage: A
[1 0 6 1 2]
[6 1 0 0 1]
[0 6 3 6 0]
sage: Z = lift_cross_ratios(A, to_sixth_root_of_unity)
sage: Z
[ 1 0 1 1 1]
[ 1 1 0 0 z]
[ 0 1 -z -1 0]
sage: M = LinearMatroid(reduced_matrix = A)
sage: sorted(M.cross_ratios())
[3, 5]
sage: N = LinearMatroid(reduced_matrix = Z)
sage: sorted(N.cross_ratios())
[-z + 1, z]
sage: M.is_isomorphism(N, {e:e for e in M.groundset()})
True
"""

for s,t in lift_map.iteritems():
source_ring = s.parent()
target_ring = t.parent()
break
plus_one1 = source_ring(1)
minus_one1 = source_ring(-1)
plus_one2 = target_ring(1)
minus_one2 = target_ring(-1)

G = sage.graphs.graph.Graph([((r,0),(c,1),(r,c)) for r,c in A.nonzero_positions()])

# write the entries of (a scaled version of) A as products of cross ratios of A
T = G.min_spanning_tree()
# - fix a tree of the support graph G to units (= empty dict, product of 0 terms)
F = {entry[2]: dict() for entry in T}
W = set(G.edges()) - set(T)
H = G.subgraph(edges = T)
while W:
# - find an edge in W to process, closing a circuit in H which is induced in G
edge = W.pop()
path = H.shortest_path(edge[0], edge[1])
retry = True
while retry:
retry = False
for edge2 in W:
if edge2[0] in path and edge2[1] in path:
W.add(edge)
edge = edge2
W.remove(edge)
path = H.shortest_path(edge[0], edge[1])
retry = True
break
entry = edge[2]
entries = []
for i in range(len(path) - 1):
v = path[i]
w = path[i+1]
if v[1] == 0:
entries.append((v[0],w[0]))
else:
entries.append((w[0],v[0]))
# - compute the cross ratio `cr` of this whirl
cr = A[entry]
div = True
for entry2 in entries:
if div:
cr = cr/A[entry2]
else:
cr = cr* A[entry2]
div = not div

monomial = dict()
if len(path) % 4 == 0:
if not cr == plus_one1:
monomial[cr] = 1
else:
cr = -cr
if not cr ==plus_one1:
monomial[cr] = 1
if monomial.has_key(minus_one1):
monomial[minus_one1] = monomial[minus_one1] + 1
else:
monomial[minus_one1] = 1

if cr != plus_one1 and not cr in lift_map:
raise ValueError("Input matrix has a cross ratio "+str(cr)+", which is not in the lift_map")
# - write the entry as a product of cross ratios of A
div = True
for entry2 in entries:
if div:
for cr, degree in F[entry2].iteritems():
if monomial.has_key(cr):
monomial[cr] = monomial[cr]+ degree
else:
monomial[cr] = degree
else:
for cr, degree in F[entry2].iteritems():
if monomial.has_key(cr):
monomial[cr] = monomial[cr] - degree
else:
monomial[cr] = -degree
div = not div
F[entry] = monomial
# - current edge is done, can be used in next iteration
H.add_edge(edge)

# compute each entry of Z as the product of lifted cross ratios
Z = sage.matrix.constructor.Matrix(target_ring, A.nrows(), A.ncols())
for entry, monomial in F.iteritems():
Z[entry] = plus_one2
for cr,degree in monomial.iteritems():
if cr == minus_one1:
Z[entry] = Z[entry] * (minus_one2**degree)
else:
Z[entry] = Z[entry] * (lift_map[cr]**degree)

return Z


# Binary matroid

cdef class BinaryMatroid(LinearMatroid):
Expand Down
186 changes: 186 additions & 0 deletions src/sage/matroids/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,189 @@ def get_nonisomorphic_matroids(MSet):
if not seen:
OutSet.append(M)
return OutSet


# Partial fields and lifting

def lift_cross_ratios(A, lift_map = None):
"""
Return a matrix which arises from the given matrix by lifting cross ratios.
INPUT:
- ``A`` -- a matrix over a ring ``source_ring``.
- ``lift_map`` -- a python dictionary, mapping each cross ratio of ``A`` to some element
of a target ring, and such that ``lift_map[source_ring(1)] = target_ring(1)``.
OUTPUT:
- ``Z`` -- a matrix over the ring ``target_ring``.
The intended use of this method is to create a (reduced) matrix representation of a
matroid ``M`` over a ring ``target_ring``, given a (reduced) matrix representation of
``A`` of ``M`` over a ring ``source_ring`` and a map ``lift_map`` from ``source_ring``
to ``target_ring``.
This method will create a unique candidate representation ``Z``, but will not verify
if ``Z`` is indeed a representation of ``M``. However, this is guaranteed if the
conditions of the lift theorem hold for the lift_map in combination with the matrix
``A``. These conditions that all cross ratios of ``A`` as well as ``1`` are keys of
``lift_map``, and
- if ``x, y`` are keys of ``lift_map``, and ``x+y == 1`` then
``lift_map[x] + lift_map[y] = lift_map[1]``
- if ``x, y, z`` are keys of ``lift_map``, and ``x*y == z`` then
``lift_map[x]*lift_map[y] = lift_map[z]``
- if ``x, y`` are keys of ``lift_map``, and ``x*y`` is not, then there is no key ``z``
of ``lift_map`` such that ``lift_map[x]*lift_map[y] = lift_map[z]``
Finally, there are global conditions on the target ring which depend on the matroid
``M`` represented by ``[ I A ]``. If ``M`` has a Fano minor, then in the target ring we
must have ``1+1 == 0``. If ``M`` has a NonFano minor, then in the target ring we must
have ``1+1 != 0``.
EXAMPLES::
sage: from sage.matroids.advanced import lift_cross_ratios, LinearMatroid
sage: R = GF(7)
sage: z = QQ['z'].0
sage: S = NumberField(z^2-z+1, 'z')
sage: to_sixth_root_of_unity = { R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**(-1)}
sage: A = Matrix(R, [[1, 0, 6, 1, 2],[6, 1, 0, 0, 1],[0, 6, 3, 6, 0]])
sage: A
[1 0 6 1 2]
[6 1 0 0 1]
[0 6 3 6 0]
sage: Z = lift_cross_ratios(A, to_sixth_root_of_unity)
sage: Z
[ 1 0 1 1 1]
[ 1 1 0 0 z]
[ 0 1 -z -1 0]
sage: M = LinearMatroid(reduced_matrix = A)
sage: sorted(M.cross_ratios())
[3, 5]
sage: N = LinearMatroid(reduced_matrix = Z)
sage: sorted(N.cross_ratios())
[-z + 1, z]
sage: M.is_isomorphism(N, {e:e for e in M.groundset()})
True
"""

for s,t in lift_map.iteritems():
source_ring = s.parent()
target_ring = t.parent()
break
plus_one1 = source_ring(1)
minus_one1 = source_ring(-1)
plus_one2 = target_ring(1)
minus_one2 = target_ring(-1)

G = sage.graphs.graph.Graph([((r,0),(c,1),(r,c)) for r,c in A.nonzero_positions()])

# write the entries of (a scaled version of) A as products of cross ratios of A
T = G.min_spanning_tree()
# - fix a tree of the support graph G to units (= empty dict, product of 0 terms)
F = {entry[2]: dict() for entry in T}
W = set(G.edges()) - set(T)
H = G.subgraph(edges = T)
while W:
# - find an edge in W to process, closing a circuit in H which is induced in G
edge = W.pop()
path = H.shortest_path(edge[0], edge[1])
retry = True
while retry:
retry = False
for edge2 in W:
if edge2[0] in path and edge2[1] in path:
W.add(edge)
edge = edge2
W.remove(edge)
path = H.shortest_path(edge[0], edge[1])
retry = True
break
entry = edge[2]
entries = []
for i in range(len(path) - 1):
v = path[i]
w = path[i+1]
if v[1] == 0:
entries.append((v[0],w[0]))
else:
entries.append((w[0],v[0]))
# - compute the cross ratio `cr` of this whirl
cr = A[entry]
div = True
for entry2 in entries:
if div:
cr = cr/A[entry2]
else:
cr = cr* A[entry2]
div = not div

monomial = dict()
if len(path) % 4 == 0:
if not cr == plus_one1:
monomial[cr] = 1
else:
cr = -cr
if not cr ==plus_one1:
monomial[cr] = 1
if monomial.has_key(minus_one1):
monomial[minus_one1] = monomial[minus_one1] + 1
else:
monomial[minus_one1] = 1

if cr != plus_one1 and not cr in lift_map:
raise ValueError("Input matrix has a cross ratio "+str(cr)+", which is not in the lift_map")
# - write the entry as a product of cross ratios of A
div = True
for entry2 in entries:
if div:
for cr, degree in F[entry2].iteritems():
if monomial.has_key(cr):
monomial[cr] = monomial[cr]+ degree
else:
monomial[cr] = degree
else:
for cr, degree in F[entry2].iteritems():
if monomial.has_key(cr):
monomial[cr] = monomial[cr] - degree
else:
monomial[cr] = -degree
div = not div
F[entry] = monomial
# - current edge is done, can be used in next iteration
H.add_edge(edge)

# compute each entry of Z as the product of lifted cross ratios
Z = sage.matrix.constructor.Matrix(target_ring, A.nrows(), A.ncols())
for entry, monomial in F.iteritems():
Z[entry] = plus_one2
for cr,degree in monomial.iteritems():
if cr == minus_one1:
Z[entry] = Z[entry] * (minus_one2**degree)
else:
Z[entry] = Z[entry] * (lift_map[cr]**degree)

return Z

def to_sixth_root_of_unity():
R = GF(7)
z = QQ['z'].gen()
S = NumberField(z^2-z+1, 'z')
return { R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**(-1)}

def to_dyadic():
R = GF(11)
return {R(1):QQ(1), R(-1):QQ(-1), R(2):QQ(2), R(6): QQ(1/2)}

def to_golden_mean():
R = GF(19)
t = QQ['t'].gen()
G = NumberField(t^2-t-1, 't')
return { R(1): G(1), R(5): G(t), R(1)/R(5): G(1)/G(t), R(-5): G(-t),
R(5)**(-1): G(t)**(-1), R(5)**2: G(t)**2, R(5)**(-2)): G(t)**(-2) }




0 comments on commit 3f41454

Please sign in to comment.