Source code for alhambra.sensitivity

import logging as log

import numpy as np

[docs]secondclasses = { (False, True): "O", (False, False): "U", (True, True): "U", (True, False): "U", }
[docs]firstsortorder = ["F", "M", "C", "D"]
[docs]secondsortorder = ["f", "b", "r", "d"]
[docs]flipdir = [2, 3, 0, 1]
[docs]def senspairs(tileset, **params): # Begin by changing the tileset into a form useful for our calculations. tiles = [] for tile in tileset["tiles"]: # Ignore non-single tiles. if len(tile["ends"]) != 4: log.warning("Ignoring non-single tile {0}".format(tile["name"])) continue # Make the actual tile list. ends = np.array(tile["ends"]) inputs = np.array(tile["input"]) tiles.append((tile["name"], ends, inputs)) senslist = [] # Now consider each tile: for An, Ae, Ai in tiles: senslist += _find_pairs_from_tile(An, Ae, Ai, tiles, **params) # Now do something with the senslist. return senslist
[docs]def _find_pairs_from_tile(An, Ae, Ai, tiles, **params): """ Given a tile of name `An`, ends `Ae`, and IO `Ai`, and a list of tiles `tiles`, find all sensitive end type pairs. """ senslist = [] # We need to iterate over tiles (again!): for Bn, Be, Bi in tiles: # Skip if the tiles are identical: if (Be == Ae).all(): if An == Bn: continue else: log.warning("{0} and {1} have *identical* ends.".format(An, Bn)) # Now we need to iterate over each non-inert common side: for side in (np.nonzero(np.ravel((Ae == Be) & (Ai != -1) & (Bi != -1))))[0]: senslist += _pairs_commonside(An, Ae, Ai, Bn, Be, Bi, side, tiles, **params) return senslist
[docs]def _pairs_commonside(An, Ae, Ai, Bn, Be, Bi, common, tiles, order=2, **params): """ Given two tiles and a common side, find all sensitive pairs. """ senslist = [] # We need to loop over every side except the common one. for mismatch in range(0, 4): # Skip the common mismatch if mismatch == common: continue # Skip any inert mismatch if Ai[mismatch] == -1 or Bi[mismatch] == -1: continue # Skip, but warn, about any second common mismatch if Ae[mismatch] == Be[mismatch]: log.debug("Tiles {} and {} have two common sides.".format(An, Bn)) continue # Now we know we have a sensitive pair, and we just need to determine type. # First order type at this point is entirely IO dependent. # Second order will require more loops... # This is the first-order sensitivity type. if (Ai[common]) >= 1 and (Ai[mismatch]) >= 1: senstype = "O1" else: senstype = "U1" # Now we need to find second-order if order == 2: st1, st2 = ( senstype, _second_order_type( An, Ae, Ai, Bn, Be, Bi, common, mismatch, tiles, senstype ), ) if st1 == "O1" and st2 == "O2": sens = ("2GO", "2NGO") elif st1 == "O1" and st2 == "U2": sens = ("1GO", "2NGO") elif st1 == "O1" and st2 == "": sens = ("1GO", "1NGO") elif st1 == "U1" and st2 == "": sens = ("0GO", "1NGO") elif st1 == "U1" and st2 == "U2": sens = ("0GO", "2NGO") else: raise "Impossible situation {},{}".format(st1, st2) senslist.append((Ae[mismatch], Be[mismatch], sens)) return senslist
[docs]def _second_order_type(An, Ae, Ai, Bn, Be, Bi, common, mismatch, tiles, firsttype): types = set() # We need to consider every non-mismatch, non-common side of A: for internal in range(0, 4): if internal == common: continue if internal == mismatch: continue # Now we need to consider every tile that fits there: for A2n, A2e, A2i in tiles: if not (A2e[flipdir[internal]] == comp(Ae[internal])): continue # And then consider every tile that corresponds... for B2n, B2e, B2i in tiles: if B2e[flipdir[internal]] != comp(Be[internal]): continue # And now we consider every common side of that except the bond it has with the # first tile... for secondedge in ( np.nonzero(np.ravel((A2e == B2e) & (A2i != -1) & (B2i != -1))) )[0]: if secondedge == flipdir[internal]: continue types.add(secondclasses[(Ai[internal] >= 1, A2i[secondedge] >= 1)]) if len(types) == 0: return "" elif "O" in types and firsttype == "O1": return "O2" else: return "U2"
[docs]def consolidate_pairs(senslist, onlytop=False, **params): # We start out with a [ (e1, e2, type) ] list of sensitivity types. # We need to consolidate it. # To begin, we'll make a dict... sensdict = {"1GO": set(), "2GO": set(), "1NGO": set(), "2NGO": set()} for end1, end2, senstype in senslist: # First we need to sort the ends: if end1 > end2: end2, end1 = end1, end2 if params["comcomp"] >= 1: if end1[-1] == "/" and end2[-1] == "/": end1 = end1[:-1] end2 = end2[:-1] if params["comcomp"] == 2: if end1[-1] == "/" and end2[-1] != "/": end1 = end1[:-1] end2 = end2 + "/" for s in senstype: if s == "0GO": continue sensdict[s].add((end1, end2)) if s == "2GO": sensdict["1GO"].add((end1, end2)) if s == "2NGO": sensdict["1NGO"].add((end1, end2)) if onlytop: sensdict["1NGO"].difference_update(sensdict["1GO"]) sensdict["1NGO"].difference_update(sensdict["2NGO"]) if not sensdict["1GO"].issubset(sensdict["2NGO"]): log.warning( "1GO is not a subset of 2NGO. Contains {}.".format( sensdict["1GO"].difference(sensdict["2NGO"]) ) ) sensdict["2NGO"].difference_update(sensdict["1GO"]) sensdict["1GO"].difference_update(sensdict["2GO"]) return sensdict
[docs]def comp(edge): if not (type(edge) is np.ndarray): if edge[-1] == "/": return edge[:-1] else: return edge + "/" if type(edge) is np.ndarray: return np.array([comp(x) for x in edge])