'''
Identify module
---------------
Functions for identifying knots based on their name or invariants.
API documentation
~~~~~~~~~~~~~~~~~
'''
# Most imports are placed in from_invariants, so that the module can work
# even if no database is available.
from pyknotid.catalogue.getdb import require_database
[docs]@require_database
def get_knot(name):
'''Returns from the database the Knot with the given identifier.
For instance, :code:`trefoil = get_knot('3_1')`.
'''
k = first_from_invariants(id=name)
if k is None:
raise ValueError('No knot with this name was found')
return k
[docs]@require_database
def first_from_invariants(**kwargs):
'''Returns the first Knot by crossing number (and arbitrary
ordering within that) with the given invariant conditions.
Parameters
----------
**kwargs :
Any set of invariant conditions. The accepted arguments are
the same as for :func:`from_invariants`.
'''
return from_invariants(return_query=True, **kwargs).first()
[docs]@require_database
def from_invariants(return_query=False, **kwargs):
'''Takes invariants as kwargs, and does the appropriate conversion to
return a list of database objects matching all the given criteria.
.. note:: This only searches within the indexed database
available. Some invariant options return only results where the
invariant both matches *and* is known, others return those that
match *or* are not known. Check the source if depending on
accurate results.
Does *not* support all available invariants. Currently, searching
is supported by:
Parameters
----------
identifier or name or id : str
The name of the knot following knot atlas conventions, e.g. '3_1'
min_crossings : int
The minimal crossing number of the knot.
max_crossings : int
The maximal known crossing number of the knot. This may be higher
than its actual crossing number, it serves only to prune the
results list.
signature : int
The signature invariant.
unknotting_number : int
The unknotting number of the knot.
alexander or alex : sympy
The Alexander polynomial, provided as a sympy expression in a
single variable (ideally 't').
determinant or alexander_imag_2: int
The Alexander polynomial at -1 (== exp(Pi I))
alexander_imag_3 : int
The abs of the Alexander polynomial at exp(2 Pi I / 3)
alexander_imag_4 : int
The abs of the Alexander polynomial at exp(Pi I / 2)
roots : iterable
The abs of the Alexander polnomial at the given roots, assumed
to start at 2, e.g. passing (3, 2, 1) is the same as identifying
at determinant=3, alexander_imag_3=2, alexander_imag_4=1. An
entry of None means the value is ignored in the lookup.
jones : sympy
The Jones polynomial, provided as a sympy expression in a single
variable (ideally 'q').
homfly : sympy
The HOMFLY-PT polynomial, provided as a sympy expression in two
variables.
hyperbolic_volume or hyp_vol or hypvol : float or str
The hyperbolic volume of the knot complement. The lookup is a
string comparison based on the given number of significant digits.
vassiliev_order_2 or vassiliev_2 or v_2 or v2 : int
The Vassiliev invariant of order 2. This will not prune knots
where this invariant is not known (specifically, those with 12 or
more crossings).
vassiliev_order_3 or vassiliev_3 or v_3 or v3 : int
The Vassiliev invariant of order 3. This will not prune knots
where this invariant is not known (specifically, those with 12 or
more crossings).
symmetry : string
The symmetry of the knot, one of 'reversible',
'positive amphicheiral', 'negative amphicheiral', 'chiral'.
writhe or planar_writhe : int
The writhe of the knot's minimal diagram as recorded by its dt
code. This is not necessarily unique, only the value
of the dt code stored is given.
composite : bool
If True, will return only composite knots. If False, will return
only prime knots. Defaults to None, meaning it will return any knot
type.
prime : bool
If True, will return only prime knots. If False, will return
only composite knots. Defaults to None, meaning it will return any knot
type.
other : iterable
A list of other peewee terms that can be chained in ``where()``
calls, e.g. ``database.Knot.min_crossings < 5``. This provides
more flexibility than the other options.
return_query : bool
If True, returns the database iterator for the objects, otherwise
returns a list. Defaults to False (i.e. the list). This will
be much slower if the list is very large, but is convenient
for most searches.
'''
from . import database as db
from pyknotid.catalogue.database import Knot
from pyknotid.catalogue import converters
if hasattr(db.db, 'get_conn'):
connection_method = db.db.get_conn
else:
connection_method = db.db.connection
connection_method()
_root_to_attr = {2: Knot.determinant,
3: Knot.alexander_imag_3,
4: Knot.alexander_imag_4}
if 'composite' not in kwargs and 'prime' not in kwargs:
kwargs['composite'] = False
conditions = []
for invariant, value in kwargs.items():
invariant = invariant.lower()
if invariant in ['name', 'id', 'identifier']:
conditions.append(Knot.identifier == value)
elif invariant == 'min_crossings':
conditions.append(Knot.min_crossings == value)
elif invariant == 'max_crossings':
conditions.append(Knot.min_crossings <= value)
elif invariant == 'signature':
conditions.append(Knot.signature == value)
elif invariant in ['determinant', 'alexander_imag_2',
'alex_imag_2', 'weak_2']:
conditions.append(Knot.determinant == value)
elif invariant in ['alexander_imag_3', 'alex_imag_3', 'weak_3']:
conditions.append(Knot.alexander_imag_3 == value)
elif invariant in ['alexander_imag_4', 'alex_imag_4', 'weak_4']:
conditions.append(Knot.alexander_imag_4 == value)
elif invariant in ['roots']:
for root, result in zip(range(2, len(value)+2), value):
if result is not None:
conditions.append(_root_to_attr[root] == result)
elif invariant == 'unknotting_number':
conditions.append(Knot.unknotting_number == value)
elif invariant in ['alexander', 'alex']:
val = converters.py2db_alexander(value)
conditions.append(Knot.alexander == val)
elif invariant == 'jones':
val = converters.py2db_jones(value)
chiral_val = converters.py2db_jones(
converters.jones_other_chirality(value))
conditions.append(Knot.jones << [val, chiral_val])
elif invariant in ['homfly', ]:
val = converters.py2db_homfly(value)
chiral_val = converters.py2db_homfly(
converters.homfly_other_chirality(value))
conditions.append(Knot.homfly << [val, chiral_val])
elif invariant in ['hypvol', 'hyp_vol', 'hyperbolic_volume']:
conditions.append(Knot.hyperbolic_volume % '{}*'.format(str(value)))
elif invariant in ['vassiliev_order_2', 'vassiliev_2', 'v_2', 'v2']:
conditions.append((Knot.vassiliev_2 == value) |
(Knot.vassiliev_2 >> None))
elif invariant in ['vassiliev_order_3', 'vassiliev_3', 'v_3', 'v3']:
conditions.append((Knot.vassiliev_3 == value) |
(Knot.vassiliev_3 == -1*value) |
(Knot.vassiliev_3 >> None))
elif invariant == 'symmetry':
conditions.append(Knot.symmetry == value.lower())
elif invariant in ('writhe', 'planar_writhe'):
conditions.append(Knot.planar_writhe == value)
elif invariant == 'prime':
if 'composite' in kwargs:
assert kwargs['composite'] == (not value)
conditions.append(Knot.composite == (not value))
elif invariant == 'composite':
conditions.append(Knot.composite == value)
elif invariant == 'other':
for condition in value:
conditions.append(condition)
else:
raise ValueError('Unrecognised parameter {} with value {}'.format(
invariant, value))
selection = Knot.select()
for condition in conditions:
selection = selection.where(condition)
selection = selection.order_by(Knot.min_crossings)
if return_query:
return selection
selection = list(selection)
return sorted(selection, key=_sort_func)
def _sort_func(k):
ident = k.identifier
crossings = 0
number = 1
jump = 0
for component, index in k.components:
if component.startswith('K'):
component = component[1:]
if 'a' in component:
parts = component.split('a')
jump += 0 * index
elif 'n' in component:
parts = component.split('n')
jump += 1e-7 * index
crossings += int(parts[0]) * index
number *= int(parts[1]) * index
else:
parts = component.split('_')
crossings += int(parts[0]) * index
number *= int(parts[1]) * index
jump += 0 * index
num_components = sum([c[1] for c in k.components])
return (crossings, num_components, number, jump)
return crossings + len(k.components) * 1e-3 + number * 1e-15 + jump