import logging
from collections import namedtuple
from copy import copy
from random import shuffle
import numpy as np
from . import fastlatticedefect as fld
from . import util
from .glues import Glue
from .tiles import TileList
[docs]FTile = namedtuple(
"FTile", ("color", "use", "glues", "name", "used", "structure", "dfake", "sfake")
)
[docs]FTilesArray = namedtuple(
"FTilesArray",
("color", "use", "glues", "name", "used", "structure", "dfake", "sfake"),
)
# Use is now going to have 4 possible values: Null, Input, Output, Both, Permanent
# null, input, output, both, permanent
[docs]class FGlueList:
def __init__(self, glues):
self.name = []
self.strength = []
self.structure = []
self.complement = []
self.use = []
self.tonum = {}
for i, g in enumerate(glues):
self.name.append(g.name)
self.name.append(g.name + "/")
self.complement.append(2 * i + 1)
self.complement.append(2 * i)
self.structure.append(type(g))
self.structure.append(type(g))
self.tonum.update({g.name: 2 * i, g.name + "/": 2 * i + 1})
self.strength.append(
g.abstractstrength if g.abstractstrength is not None else 1
)
self.strength.append(
g.abstractstrength if g.abstractstrength is not None else 1
)
self.use.append(g.use)
self.use.append((g.use // 2) + (g.use % 2) * 2)
self.name = np.array(self.name)
self.strength = np.array(self.strength)
self.structure = np.array(self.structure)
self.complement = np.array(self.complement)
self.use = np.array(self.use)
[docs] def blankequiv(self):
return np.arange(0, len(self.name))
[docs] def iseq(self, equiv, a, b):
return equiv[a] == equiv[b]
[docs] def domerge(self, equiv, a, b, preserveuse=False):
if self.structure[a] != self.structure[b]:
raise ValueError("structure")
elif self.strength[a] != self.strength[b]:
raise ValueError("strength")
elif equiv[a] == equiv[self.complement[b]]:
raise ValueError("self-comp")
elif preserveuse and (self.use[a] != self.use[b]):
raise ValueError("use")
else:
equiv = copy(equiv)
newg, oldg = sorted((equiv[a], equiv[b]))
newc, oldc = sorted((equiv[self.complement[a]], equiv[self.complement[b]]))
equiv[equiv == oldg] = newg
equiv[equiv == oldc] = newc
return equiv
[docs]class FTileList:
def __init__(self, tiles, gluelist):
self.tiles = []
self.totile = {}
for t in tiles:
glues = np.array([gluelist.tonum[x] for x in t.ends])
if "fake" in t.keys():
continue
if "input" not in t.keys():
used = False
use = np.array([U_UNUSED for _ in t.ends])
else:
used = True
use = np.array([int(x) for x in t["input"]])
color = "label" in t.keys()
self.tiles.append(
FTile(
name=t.name,
color=color,
use=use,
glues=glues,
used=used,
structure=t.structure,
dfake=0,
sfake=0,
)
)
assert t.name not in self.totile.keys()
self.totile[t.name] = self.tiles[-1]
stiles = []
htiles = []
vtiles = []
for t in self.tiles:
if isinstance(t.structure, tilestructures.tile_daoe_single):
stiles.append(t)
elif isinstance(t.structure, tilestructures.tile_daoe_doublehoriz):
stiles += _ffakesingle(t, gluelist)
htiles.append(t)
elif isinstance(t.structure, tilestructures.tile_daoe_doublevert):
stiles += _ffakesingle(t, gluelist)
vtiles.append(t)
else:
raise NotImplementedError
self.stiles = _ft_to_fta(stiles)
for i in range(0, len(self.stiles)):
x, y = _ffakedouble_n(i, self.stiles, gluelist)
htiles += x
vtiles += y
self.htiles = _ft_to_fta(htiles)
self.vtiles = _ft_to_fta(vtiles)
# FTS = (
# tilestructures.tile_doublevert(),
# tilestructures.tile_doublehoriz(),
# tilestructures.tile_doublevert(),
# tilestructures.tile_doublehoriz(),
# )
[docs]def _fdg(dir, gs1, gs2):
if dir == 0:
return [gs2[0], gs2[1], gs1[1], gs1[2], gs1[3], gs2[3]]
elif dir == 1:
return [gs1[0], gs2[0], gs2[1], gs2[2], gs1[2], gs1[3]]
elif dir == 2:
return [gs1[0], gs1[1], gs2[1], gs2[2], gs2[3], gs1[3]]
elif dir == 3:
return [gs2[0], gs1[0], gs1[1], gs1[2], gs2[2], gs2[3]]
else:
raise ValueError(dir)
[docs]def _ffakedouble_n(tn, sta, gluelist, outputonly=True, dir4=False, equiv=None):
if equiv is None:
equiv = gluelist.blankequiv()
if not dir4:
faketiles = ([], [])
fti = FTI
else:
faketiles = ([], [], [], [])
fti = (0, 1, 2, 3)
if sta.sfake[tn]:
ddir = np.flatnonzero(gluelist.tonum["fakedouble"] == sta.glues[tn])[0]
for dir in range(0, 4):
if (sta.use[tn, dir] == 1) and outputonly:
continue
if outputonly:
oti = np.nonzero(
(
equiv[gluelist.complement[sta.glues[tn, dir]]]
== equiv[sta.glues[:, RSEL[dir]]]
)
& (sta.use[:, RSEL[dir]] == 1)
& (sta.used)
)
else:
oti = np.nonzero(
(
equiv[gluelist.complement[sta.glues[tn, dir]]]
== equiv[sta.glues[:, RSEL[dir]]]
)
)
for i in oti[0]:
faketiles[fti[dir]].append(
FTile(
color=False,
used=True,
name=sta.name[tn] + "_{}_".format(dir) + sta.name[i],
structure=FTS[dir],
glues=np.array(_fdg(dir, sta.glues[tn, :], sta.glues[i])),
use=np.array(_fdg(dir, sta.use[tn, :], sta.use[i])),
dfake=dir + 1,
sfake=(sta.sfake[tn] or sta.sfake[i]),
)
)
if sta.sfake[tn] and (dir == ddir):
faketiles[fti[dir]].append(
FTile(
color=False,
used=True,
name=sta.name[tn]
+ "_{}_".format(dir)
+ sta.name[tn + sta.sfake[tn]],
structure=FTS[dir],
glues=np.array(
_fdg(dir, sta.glues[tn, :], sta.glues[tn + sta.sfake[tn]])
),
use=np.array(
_fdg(dir, sta.use[tn, :], sta.use[tn + sta.sfake[tn]])
),
dfake=dir + 1,
sfake=True,
)
)
return faketiles
# THESE SELECTORS ARE 1-INDEXED!!! 0 corresponds to fake double tile bond.
[docs]HSEL = ((1, 0, 5, 6), (2, 3, 4, 0))
[docs]VSEL = ((1, 2, 0, 6), (0, 3, 4, 5))
[docs]def _ffakesingle(ftile, gluelist):
# FIXME: should be more generalized. Currently only tau=2
if isinstance(ftile.structure, tilestructures.tile_daoe_doublehoriz):
sel = HSEL
elif isinstance(ftile.structure, tilestructures.tile_daoe_doublevert):
sel = VSEL
else:
raise NotImplementedError
# Start by making the tiles, then change around the inputs
fdb = gluelist.tonum["fakedouble"]
glues = [[([fdb] + list(ftile.glues))[x] for x in y] for y in sel]
fuse = [[([-1] + list(ftile.use))[x] for x in y] for y in sel]
use = []
used = []
names = [ftile.name + "_fakedouble_a", ftile.name + "_fakedouble_b"]
for gu in zip(glues, fuse):
if sum(gluelist.strength[g] for g, u in zip(*gu) if u == 1) >= TAU:
use.append(gu[1])
used.append(True)
else:
use.append([-1, -1, -1, -1])
used.append(False)
return [
FTile(
color=ftile.color,
use=u,
glues=g,
name=n,
used=ud,
structure=tilestructures.tile_daoe_single(),
dfake=0,
sfake=sfo,
)
for u, g, n, ud, sfo in zip(use, glues, names, used, [1, -1])
]
[docs]def _ft_to_fta(ftiles):
return FTilesArray(
color=np.array([x.color for x in ftiles]),
name=np.array([x.name for x in ftiles]),
glues=np.array([x.glues for x in ftiles]),
use=np.array([x.use for x in ftiles]),
used=np.array([x.used for x in ftiles]),
structure=np.array([x.structure.name for x in ftiles]),
dfake=np.array([x.dfake for x in ftiles]),
sfake=np.array([x.sfake for x in ftiles]),
)
[docs]class _FastTileSet:
def __init__(self, tilesystem):
self.gluelist = FGlueList(
tilesystem.allglues
# + [
# Glue({"name": "hp", "type": "hairpin", "strength": 0}),
# Glue({"name": "fakedouble", "type": "fakedouble", "strength": 0}),
# ]
)
self.tilelist = FTileList(
tilesystem.tiles
# + sum([x.named_rotations() for x in tilesystem.tiles], TileList()), # FIXME
,
self.gluelist,
)
[docs] def applyequiv(self, ts, equiv):
ts = ts.copy()
alreadythere = []
for tile in ts.tiles:
tile.ends = [
self.gluelist.name[equiv[self.gluelist.tonum[e]]] for e in tile.ends
]
if (tile.ends, "label" in tile.keys()) in alreadythere:
tile["fake"] = True
continue
rs = [tile] + tile.rotations
alreadythere.extend((t.ends, "label" in t.keys()) for t in rs)
if "seed" in ts.keys():
for t in ts.seed["adapters"]:
if "ends" in t.keys():
t["ends"] = [
self.gluelist.name[equiv[self.gluelist.tonum[e]]]
for e in t["ends"]
]
ts["info"] = ts.get("info", dict())
ts["info"]["fgluemerge"] = ts["info"].get("fgluemerge", list())
ts["info"]["fgluemerge"].append([int(x) for x in equiv])
return ts
[docs] def togluemergespec(self, ts, equiv):
gms = util.GlueMergeSpec()
for i in range(0, len(equiv)):
if i != equiv[i]:
gms.add(self.gluelist.name[equiv[i]], self.gluelist.name[i])
return gms
[docs]def ptins(fts, equiv=None, tau=2):
"""Calculate potential tile attachments to input neighborhoods"""
ptins = []
if equiv is None:
equiv = fts.gluelist.blankequiv()
for ta in [fts.tilelist.stiles, fts.tilelist.htiles, fts.tilelist.vtiles]:
ptin = []
for ti in np.arange(0, len(ta.used))[ta.used]:
# Only iterate through used tiles
isel = ta.use[ti] == 1 # used edges
gsel = equiv[ta.glues[ti, isel]] == equiv[ta.glues[:, isel]]
# matching glues
strs = np.sum(
fts.gluelist.strength[ta.glues[ti, isel]] * gsel, axis=1
) # strengths of matching
matches = np.nonzero((strs >= tau) & (ta.dfake == 0))
# indices of matching
# (excludes fake doubles, which can't actually attach
# (because they are
# actually two singles) to their own local neighborhoods)
ptin.append(matches)
ptins.append(ptin)
return ptins
[docs]def is_2go_single_nn(
fts, tn, un, equiv, tau=2, in2go=None, retall=False, also22go=False
):
# Before starting, check to see if t and u are actually the same:
if np.all(
equiv[fts.tilelist.stiles.glues[fts.tilelist.stiles.used][tn]]
== equiv[fts.tilelist.stiles.glues[un]]
):
if not also22go:
if not retall:
return False
else:
return False, None
else:
if not retall:
return False, False
else:
return False, False, None, None
if in2go is None:
in2go = _ffakedouble_n(
np.flatnonzero(fts.tilelist.stiles.used)[tn],
fts.tilelist.stiles,
fts.gluelist,
outputonly=True,
dir4=True,
)
incorrect = _ffakedouble_n(
un, fts.tilelist.stiles, fts.gluelist, outputonly=False, dir4=True, equiv=equiv
)
is2go = None
for tvs, uws in zip(in2go, incorrect):
for tv in tvs:
for uw in uws:
if (
np.sum(
fts.gluelist.strength[
tv.glues[
(equiv[tv.glues] == equiv[uw.glues]) & (tv.use == 1)
]
]
)
>= tau
):
if also22go and uw.sfake:
if not retall:
return True, True
else:
return True, True, (ts, uw), None
elif also22go:
is2go = (tv, uw)
for dirr, (tvs2, uws2) in enumerate(zip(in2go, incorrect)):
if dirr + 1 == tv.dfake:
continue
for tv2 in tvs2:
for uw2 in uws2:
if (
np.sum(
fts.gluelist.strength[
tv2.glues[
(
equiv[tv2.glues]
== equiv[uw2.glues]
)
& (tv2.use == 1)
]
]
)
>= tau
):
if not retall:
return True, True
else:
return True, True, (tv, uw), (tv2, uw2)
elif not retall:
return True
else:
return True, (tv, uw)
if also22go:
if retall:
return (not (is2go is None)), False, is2go, None
else:
return (not (is2go is None)), False
elif not retall:
return False
else:
return False, None
[docs]def gen_2go_single_ins(fts, tau=2):
ssel = np.flatnonzero(fts.tilelist.stiles.used)
ins = []
for tn in ssel:
ins.append(
_ffakedouble_n(
tn, fts.tilelist.stiles, fts.gluelist, outputonly=True, dir4=True
)
)
return ins
[docs]def gen_2go_profile(fts, equiv=None, ins2go=None, also22go=False):
if ins2go is None:
ins2go = gen_2go_single_ins(fts, tau=2)
if equiv is None:
equiv = fts.gluelist.blankequiv()
sens1s = ptins(fts, tau=1, equiv=equiv)[0]
sens2s = []
sens22s = []
for tn, (uns, in2go) in enumerate(zip(sens1s, ins2go)):
x2 = []
x22 = []
for un in uns[0]:
if also22go:
s2, s22 = is_2go_single_nn(
fts, tn, un, equiv, tau=2, in2go=in2go, also22go=True
)
else:
s2 = is_2go_single_nn(
fts, tn, un, equiv, tau=2, in2go=in2go, also22go=False
)
if s2:
x2.append(un)
if also22go and s22:
x22.append(un)
sens2s.append(x2)
if also22go:
sens22s.append(x22)
if also22go:
return sens2s, sens22s
else:
return sens2s
[docs]def is_2go_equiv(fts, equiv=None, ins2go=None, origsens=None):
if ins2go is None:
ins2go = gen_2go_single_ins(fts, tau=2)
if equiv is None:
equiv = fts.gluelist.blankequiv()
if origsens is None:
origsens = gen_2go_profile(fts)
sens1s = ptins(fts, tau=1, equiv=equiv)[0]
for tn, (uns, in2go, os) in enumerate(zip(sens1s, ins2go, origsens)):
for un in uns[0]:
r, p = is_2go_single_nn(fts, tn, un, equiv, tau=2, in2go=in2go, retall=True)
if r and (un not in os):
# Next if checks to see if sensitive tile is actually identical
# to an already sensitive tile:
# FIXME: this would not be necessary if we stored whether a
# tile was a duplicate. However, that would break the
# glue-equiv-only method...
if not np.any(
np.all(
equiv[fts.tilelist.stiles.glues[os]]
== equiv[fts.tilelist.stiles.glues[un]],
axis=1,
)
):
return (
False,
(
fts.tilelist.stiles.name[fts.tilelist.stiles.used][tn],
fts.tilelist.stiles.name[un],
),
p,
)
return True, None, None
[docs]def is_22go_equiv(fts, equiv=None, ins2go=None, orig22go=None):
if ins2go is None:
ins2go = gen_2go_single_ins(fts, tau=2)
if equiv is None:
equiv = fts.gluelist.blankequiv()
if orig22go is None:
_, orig22go = gen_2go_profile(fts, also22go=True)
sens1s = ptins(fts, tau=1, equiv=equiv)[0]
for tn, (uns, in2go, os22) in enumerate(zip(sens1s, ins2go, orig22go)):
for un in uns[0]:
r2, r22, p2, p22 = is_2go_single_nn(
fts, tn, un, equiv, tau=2, in2go=in2go, also22go=True, retall=True
)
if r22 and (un not in os22):
# Next if checks to see if sensitive tile is actually identical
# to an already sensitive tile:
# FIXME: this would not be necessary if we stored whether a
# tile was a duplicate. However, that would break the
# glue-equiv-only method...
if not np.any(
np.all(
equiv[fts.tilelist.stiles.glues[os22]]
== equiv[fts.tilelist.stiles.glues[un]],
axis=1,
)
):
return (
False,
(
fts.tilelist.stiles.name[fts.tilelist.stiles.used][tn],
fts.tilelist.stiles.name[un],
),
p22,
)
return True, None, None
[docs]def fta_to_ft(stiles, un, sel=None):
if sel is None:
sel = np.ones_like(stiles.used, dtype=bool)
return FTile(
color=stiles.color[sel][un],
use=stiles.use[sel, :][un, :],
glues=stiles.glues[sel, :][un, :],
name=stiles.name[sel][un],
used=stiles.used[sel][un],
structure=stiles.structure[sel][un],
dfake=stiles.dfake[sel][un],
sfake=stiles.sfake[sel][un],
)
[docs]def isatamequiv(fts, equiv, initptins=None):
if initptins is None:
initptins = ptins(fts, fts.gluelist.blankequiv())
npt = ptins(fts, equiv)
for x, y, tl in zip(
initptins, npt, [fts.tilelist.stiles, fts.tilelist.htiles, fts.tilelist.vtiles]
):
for xx, yy in zip(x, y):
if len(xx[0]) == len(yy[0]) and (np.all(xx[0] == yy[0])): # No change
continue
elif len(xx[0]) == 1: # Deterministic start
mm = ~np.all((equiv[tl.glues[xx]] == equiv[tl.glues[yy]]), axis=1) | ~(
tl.color[xx] == tl.color[yy]
)
if np.any(mm):
return False, (tl.name[xx[0][0]], tl.name[yy[0][mm][0]])
elif len(xx[0]) == 0:
return False, None
else:
raise NotImplementedError(xx)
return True, None
[docs]def tilemerge(fts, equiv, t1, t2, preserveuse=False):
if t1.structure.name != t2.structure.name:
raise ValueError
if t1.color != t2.color:
raise ValueError
for g1, g2 in zip(t1.glues, t2.glues):
equiv = fts.gluelist.domerge(equiv, g1, g2, preserveuse=preserveuse)
return equiv
[docs]def _findpotentialtilemerges(fts, equiv):
ppairs = []
for ti in range(0, len(fts.tilelist.tiles)):
t1 = fts.tilelist.tiles[ti]
if not t1.used:
continue
ppairs.extend(
(t1, t)
for t in fts.tilelist.tiles[ti:]
if (t1.color == t.color)
and (t1.structure.name == t.structure.name)
and (t1.name != t.name)
)
shuffle(ppairs)
return ppairs
[docs]def _findpotentialgluemerges(fts, equiv):
ppairs = []
for g1 in np.arange(0, len(fts.gluelist.strength)):
g2s = (
g1
+ 1
+ np.nonzero(
(fts.gluelist.strength[g1 + 1 :] == fts.gluelist.strength[g1])
& (fts.gluelist.structure[g1 + 1 :] == fts.gluelist.structure[g1])
)[0]
)
ppairs.extend((g1, g2) for g2 in g2s)
shuffle(ppairs)
return ppairs
[docs]def _recfix(
fts,
equiv,
tp,
initptins,
check2go=False,
ins2go=None,
orig2go=None,
orig22go=None,
check22go=False,
checkld=False,
chain=None,
preserveuse=False,
):
log = logging.getLogger(__name__)
if chain is None:
chain = []
equiv = tilemerge(
fts,
equiv,
fts.tilelist.totile[tp[0]],
fts.tilelist.totile[tp[1]],
preserveuse=preserveuse,
)
ae, badpair = isatamequiv(fts, equiv, initptins=initptins)
if check2go and ae:
ae, badpair, _ = is_2go_equiv(fts, equiv, ins2go=ins2go, origsens=orig2go)
if ae and check22go:
ae, badpair, _ = is_22go_equiv(fts, equiv, ins2go=ins2go, orig22go=orig22go)
if ae and checkld:
ld_e = fld.latticedefects(fts, "e", equiv=equiv)
ld_w = fld.latticedefects(fts, "w", equiv=equiv)
if (len(ld_e) > 0) or (len(ld_w) > 0):
raise ValueError
if ae:
log.debug("Recfix succeeds with {}, {}, {}".format(tp[0], tp[1], chain))
return equiv
elif badpair is None:
raise ValueError
else:
chain.append(tp)
return _recfix(
fts,
equiv,
badpair,
initptins,
check2go,
ins2go,
orig2go,
orig22go=orig22go,
check22go=check22go,
checkld=checkld,
chain=chain,
preserveuse=preserveuse,
)
[docs]def _tilereduce(
fts,
equiv=None,
check2go=False,
initptins=None,
check22go=False,
checkld=False,
preserveuse=False,
):
log = logging.getLogger(__name__)
if equiv is None:
equiv = fts.gluelist.blankequiv()
todo = _findpotentialtilemerges(fts, equiv)
if initptins is None:
initptins = ptins(fts)
if check2go or check22go:
ins2go = gen_2go_single_ins(fts)
origsens, orig22go = gen_2go_profile(fts, ins2go=ins2go, also22go=True)
else:
ins2go = None
orig22go = None
origsens = None
for todoi, (t1, t2) in enumerate(todo):
try:
nequiv = tilemerge(fts, equiv, t1, t2, preserveuse=preserveuse)
except ValueError:
continue
ae, badpair = isatamequiv(fts, nequiv, initptins=initptins)
if ae and check2go:
ae, badpair, _ = is_2go_equiv(fts, nequiv, ins2go=ins2go, origsens=origsens)
if ae and check22go:
ae, badpair, _ = is_22go_equiv(
fts, nequiv, ins2go=ins2go, orig22go=orig22go
)
if ae and checkld:
ld_e = fld.latticedefects(fts, "e", equiv=nequiv)
ld_w = fld.latticedefects(fts, "w", equiv=nequiv)
if (len(ld_e) > 0) or (len(ld_w) > 0):
continue
if ae:
equiv = nequiv
log.debug(
"Reduced {}, {} ({} of {} pairs done)".format(
t1.name, t2.name, todoi, len(todo)
)
)
elif badpair is None:
continue
else:
try:
equiv = _recfix(
fts,
equiv,
badpair,
initptins,
check2go,
ins2go,
origsens,
check22go=check22go,
orig22go=orig22go,
checkld=checkld,
chain=[(t1.name, t2.name)],
preserveuse=preserveuse,
)
except ValueError:
continue
except KeyError:
continue
return equiv
[docs]def _gluereduce(
fts,
equiv=None,
check2go=False,
check22go=False,
checkld=False,
initptins=None,
preserveuse=False,
):
log = logging.getLogger(__name__)
if equiv is None:
equiv = fts.gluelist.blankequiv()
todo = _findpotentialgluemerges(fts, equiv)
if initptins is None:
initptins = ptins(fts)
if check2go or check22go:
ins2go = gen_2go_single_ins(fts)
origsens, orig22go = gen_2go_profile(fts, ins2go=ins2go, also22go=True)
else:
ins2go = None
origsens = None
orig22go = None
for todoi, (g1, g2) in enumerate(todo):
try:
nequiv = fts.gluelist.domerge(equiv, g1, g2, preserveuse=preserveuse)
except ValueError:
continue
ae, badpair = isatamequiv(fts, nequiv, initptins=initptins)
if ae and check2go:
ae, badpair, _ = is_2go_equiv(fts, nequiv, ins2go=ins2go, origsens=origsens)
if ae and check22go:
ae, badpair, _ = is_22go_equiv(
fts, nequiv, ins2go=ins2go, orig22go=orig22go
)
if ae and checkld:
ld_e = fld.latticedefects(fts, "e", equiv=nequiv)
ld_w = fld.latticedefects(fts, "w", equiv=nequiv)
if (len(ld_e) > 0) or (len(ld_w) > 0):
continue
if ae:
equiv = nequiv
log.debug(
"Glue reduction: {}, {} ({}/{} done)".format(
fts.gluelist.name[g1], fts.gluelist.name[g2], todoi, len(todo)
)
)
elif badpair is None:
continue
else:
try:
equiv = _recfix(
fts,
equiv,
badpair,
initptins,
check2go,
ins2go,
origsens,
check22go=check22go,
checkld=checkld,
chain=[(fts.gluelist.name[g1], fts.gluelist.name[g2])],
preserveuse=preserveuse,
)
except ValueError:
continue
except KeyError:
continue
return equiv
[docs]def _single_reduce_tiles(p):
feq, ts, fts, params = p
# starttime = time.time()
initptins = ptins(fts)
e = _tilereduce(fts, equiv=feq, initptins=initptins, **params)
# endtime = time.time()
# te = fts.applyequiv(ts, e)
# nt = len([y for y in te.tiles if 'fake' not in y.keys()])
# ng = len(te.allends)
# a = isatamequiv(fts, e)
# g2 = is_2go_equiv(fts, e)[0]
# g22 = is_22go_equiv(fts, e)[0]
# lde = len(fld.latticedefects(fts, equiv=e, direction='e')) == 0
# ldw = len(fld.latticedefects(fts, equiv=e, direction='w')) == 0
# print('TR: Found {}t, {}g: aTAM {} 2GO {} 22GO {} LD {}/{} in {}s'.format(
# nt, ng, a, g2, g22, lde, ldw, endtime - starttime))
# te.to_file(nb + 'step1-{}t{}g.yaml'.format(nt, ng))
return e
[docs]def _single_reduce_ends(p):
equiv, ts, fts, params = p
# starttime = time.time()
initptins = ptins(fts)
e = _gluereduce(fts, equiv=equiv, initptins=initptins, **params)
# FIXME: all this is code for checking things and printing logs.
# endtime = time.time()
# te = fts.applyequiv(ts, e)
# nt = len([y for y in te.tiles if 'fake' not in y.keys()])
# ng = len(te.allends)
# a = isatamequiv(fts, e)
# g2 = is_2go_equiv(fts, e)[0]
# g22 = is_22go_equiv(fts, e)[0]
# lde = len(fld.latticedefects(fts, equiv=e, direction='e')) == 0
# ldw = len(fld.latticedefects(fts, equiv=e, direction='w')) == 0
# print('ER: Found {}t, {}g: aTAM {} 2GO {} 22GO {} LD {}/{} in {}s'.format(
# nt, ng, a, g2, g22, lde, ldw, endtime - starttime))
# te.to_file(nb + '-step2-{}t{}g.yaml'.format(nt, ng))
return e
[docs]def reduce_tiles(
tileset,
preserve=("s22", "ld"),
tries=10,
threads=1,
returntype="equiv",
best=1,
key=None,
initequiv=None,
):
"""
Apply tile reduction algorithm, preserving some set of properties, and using a multiprocessing pool.
Parameters
----------
tileset: TileSet
The system to reduce.
preserve: a tuple or list of strings, optional
The properties to preserve. Currently supported are 's1' for first order
sensitivity, 's2' for second order sensitivity, 's22' for two-by-two sensitivity,
'ld' for small lattice defects, and 'gs' for glue sense (to avoid spurious
hierarchical attachment). Default is currently ('s22', 'ld').
tries: int, optional
The number of times to run the algorithm.
threads: int, optional
The number of threads to use (using multiprocessing).
returntype: 'TileSet' or 'equiv' (default 'equiv')
The type of object to return. If 'equiv', returns an array of glue equivalences
(or list, if best != 1) that can be applied to the tileset with apply_equiv, or used
for further reduction. If 'TileSet', return a TileSet with the equiv already applied
(or a list, if best != 1).
best: int or None, optional
The number of systems to return. If 1, the result will be returned
directly; if k > 1, a list will be returned of the best k results (per cmp);
if k = None, a list of *all* results will be returned, sorted by cmp. (default 1)
key: function (ts, equiv1, equiv2) -> some number/comparable
A comparison function for equivs, to sort the results. FIXME: documentation needed.
Default (if None) here is to sort by number of glues in the system, regardless of number
of tiles.
initequiv: equiv
If provided, the equivalence array to start from. If None, start from the tileset without
any merged glues.
Returns
-------
reduced: single TileSet
The reduced system/systems
"""
fts = _FastTileSet(tileset)
if key is None:
key = lambda x: len(
np.unique(x)
) # number of unique numbers in equiv, equivalent
# to number of glues in reduced system. FIXME?
# FIXME: could do a better job here
params = {
"check2go": "s2" in preserve,
"check22go": "s22" in preserve,
"checkld": "ld" in preserve,
"preserveuse": "gs" in preserve,
}
if initequiv is None:
initequiv = fts.gluelist.blankequiv()
if threads > 1:
from multiprocessing import Pool
with Pool(threads) as pool:
equivs = pool.map(
_single_reduce_tiles, [[initequiv, tileset, fts, params]] * tries
)
else:
equivs = [
_single_reduce_tiles(x)
for x in ([[initequiv, tileset, fts, params]] * tries)
]
equivs.sort(key=key)
if returntype == "TileSet":
equivs = [tileset.apply_equiv(equiv) for equiv in equivs]
if best == 1:
return equivs[0]
else:
return equivs[0:best]
[docs]def reduce_ends(
tileset,
preserve=("s22", "ld"),
tries=10,
threads=1,
returntype="equiv",
best=1,
key=None,
initequiv=None,
):
"""
Apply end reduction algorithm, preserving some set of properties, and using a multiprocessing pool.
Parameters
----------
tileset: TileSet
The system to reduce.
preserve: a tuple or list of strings, optional
The properties to preserve. Currently supported are 's1' for first order
sensitivity, 's2' for second order sensitivity, 's22' for two-by-two sensitivity,
'ld' for small lattice defects, and 'gs' for glue sense (to avoid spurious
hierarchical attachment). Default is currently ('s22', 'ld').
tries: int, optional
The number of times to run the algorithm.
threads: int, optional
The number of threads to use (using multiprocessing).
returntype: 'TileSet' or 'equiv' (default 'equiv')
The type of object to return. If 'equiv', returns an array of glue equivalences
(or list, if best != 1) that can be applied to the tileset with apply_equiv, or used
for further reduction. If 'TileSet', return a TileSet with the equiv already applied
(or a list, if best != 1).
best: int or None, optional
The number of systems to return. If 1, the result will be returned
directly; if k > 1, a list will be returned of the best k results (per cmp);
if k = None, a list of *all* results will be returned, sorted by cmp. (default 1)
key: function (ts, equiv1, equiv2) -> some number/comparable
A comparison function for equivs, to sort the results. FIXME: documentation needed.
Default (if None) here is to sort by number of glues in the system, regardless of number
of tiles.
initequiv: equiv
If provided, the equivalence array to start from. If None, start from the tileset without
any merged glues.
Returns
-------
reduced: single TileSet
The reduced system/systems
"""
fts = _FastTileSet(tileset)
if key is None:
key = lambda x: len(
np.unique(x)
) # number of unique numbers in equiv, equivalent
# to number of glues in reduced system. FIXME?
# FIXME: could do a better job here
params = {
"check2go": "s2" in preserve,
"check22go": "s22" in preserve,
"checkld": "ld" in preserve,
"preserveuse": "gs" in preserve,
}
if initequiv is None:
initequiv = fts.gluelist.blankequiv()
if threads > 1:
from multiprocessing import Pool
with Pool(threads) as pool:
equivs = pool.map(
_single_reduce_ends, [[initequiv, tileset, fts, params]] * tries
)
else:
equivs = [
_single_reduce_ends(x)
for x in ([[initequiv, tileset, fts, params]] * tries)
]
equivs.sort(key=key)
if returntype == "TileSet":
equivs = [tileset.apply_equiv(equiv) for equiv in equivs]
if best == 1:
return equivs[0]
else:
return equivs[0:best]