Commit e6251be1 authored by Adam P. Goucher's avatar Adam P. Goucher

Python API for connected components

parent 3091c1de
Pipeline #54484812 passed with stages
in 7 minutes and 53 seconds
#ifndef LIFELIB_VERSION /*
__version__=[x.replace('"', '') for x in '''
*/
#define LIFELIB_VERSION "ll2.2.3"
#define LIFELIB_VERSION "ll2.2.4"
// '''.split() if ('ll' in x)][0][2:]
#endif
......@@ -174,6 +174,31 @@ extern "C"
return new(std::nothrow) apg::pattern(ppat->stream(gstream));
}
void* FindConnectedComponent(void* v_seed, void* v_backdrop, void* v_corona) {
auto p_seed = reinterpret_cast<apg::pattern*>(v_seed);
auto p_backdrop = reinterpret_cast<apg::pattern*>(v_backdrop);
auto p_corona = reinterpret_cast<apg::pattern*>(v_corona);
apg::pattern agglom = (*p_seed) & (*p_backdrop);
apg::pattern cluster = agglom;
while (cluster.nonempty()) {
cluster = cluster.convolve(*p_corona);
cluster &= (*p_backdrop);
cluster -= agglom;
agglom += cluster;
}
return new(std::nothrow) apg::pattern(agglom);
}
void* GetOneCell(void* pat) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
return new(std::nothrow) apg::pattern(ppat->onecell());
}
void* MatchLive(void* pat, void* pat1) {
auto ppat = reinterpret_cast<apg::pattern*>(pat);
auto ppat1 = reinterpret_cast<apg::pattern*>(pat1);
......
......@@ -872,6 +872,54 @@ namespace apg {
}
}
hypernode<I> onecell_recurse(hypernode<I> hnode) {
if (hnode.index == 0) {
return hnode;
} else if (hnode.depth == 0) {
kiventry<nicearray<uint64_t, 4*N>, I, J >* pptr = ind2ptr_leaf(hnode.index);
nicearray<uint64_t, 4*N> outleaf = {0ull};
for (int j = 0; j < 4; j++) {
uint64_t l = pptr->key.x[j];
for (int i = 1; i < N; i++) {
l |= pptr->key.x[4*i+j];
}
if (l == 0) { continue; }
l &= (-l); // extract one bit
for (int i = 0; i < N; i++) {
outleaf.x[4*i+j] = l;
}
break;
}
I res = make_leaf(outleaf);
return hypernode<I>(res, 0);
} else {
kiventry<nicearray<I, 4>, I, J >* pptr = ind2ptr_nonleaf(hnode.depth, hnode.index);
nicearray<I, 4> cc = {(I) 0, (I) 0, (I) 0, (I) 0};
if (pptr->key.x[0]) {
cc.x[0] = onecell_recurse(hypernode<I>(pptr->key.x[0], hnode.depth - 1)).index;
} else if (pptr->key.x[1]) {
cc.x[1] = onecell_recurse(hypernode<I>(pptr->key.x[1], hnode.depth - 1)).index;
} else if (pptr->key.x[2]) {
cc.x[2] = onecell_recurse(hypernode<I>(pptr->key.x[2], hnode.depth - 1)).index;
} else if (pptr->key.x[3]) {
cc.x[3] = onecell_recurse(hypernode<I>(pptr->key.x[3], hnode.depth - 1)).index;
}
hypernode<I> xcc = make_nonleaf_hn(hnode.depth, cc);
return xcc;
}
}
hypernode<I> brand_recurse(hypernode<I> hnode, std::map<std::pair<I, uint32_t>, I> *memmap, bool disjunctive) {
if (N == 1) { return hnode; }
......
......@@ -103,6 +103,7 @@ namespace apg {
virtual hypernode<I> pyramid_up(hypernode<I> hnode) = 0;
virtual hypernode<I> subnode(hypernode<I> hnode, uint64_t x, uint64_t y, uint64_t n) = 0;
virtual hypernode<I> onecell_recurse(hypernode<I> hnode) = 0;
virtual hypernode<I> demorton_recurse(std::map<uint64_t, uint64_t>::iterator &it,
std::map<uint64_t, uint64_t>::iterator &pe,
......
......@@ -367,6 +367,10 @@ namespace apg {
return basepattern<I>(lab, lab->brand_recurse(hnode), rulestring);
}
basepattern<I> onecell() {
return basepattern<I>(lab, lab->onecell_recurse(hnode), rulestring);
}
// Periodicity detection:
uint64_t digest() {
......
......@@ -75,7 +75,8 @@ restypes = {'DeletePattern': None, 'GetBeszelIndex': c_uint64, 'GetUlqomaIndex':
'PatternNonempty': c_bool, 'MatchLive': c_void_p, 'MatchLiveAndDead': c_void_p, 'GetRuleOfPattern': None,
'GetSolidForPattern': c_void_p, 'GetOriginState': c_uint64, 'GetCells': None, 'SetCells': None,
'CreatePatternFromFile': c_void_p, 'MakeSpaceshipStream': c_void_p, 'GetCompiledVersion': None,
'GetCoords': None, 'GetPatternBound': c_uint64, 'GetSubpops': None, 'Hashsoup': c_void_p}
'GetCoords': None, 'GetPatternBound': c_uint64, 'GetSubpops': None, 'Hashsoup': c_void_p,
'FindConnectedComponent': c_void_p, 'GetOneCell': c_void_p}
def call_underlying(lifelib, fname_and_args):
......
......@@ -262,6 +262,38 @@ class Pattern(object):
return b + x._getbound(direction, pixelsize - 30, offset)
def component_containing(self, seed=None, halo='ooo$ooo$ooo!'):
if seed is None:
seed = self.onecell()
if not isinstance(halo, Pattern):
halo = self.owner.pattern(halo, self.getrule()).centre()
if isinstance(seed, tuple):
seed = self.owner.pattern('o!', self.getrule())(seed[0], seed[1])
newptr = self.lifelib('FindConnectedComponent', seed.ptr, self.ptr, halo.ptr)
return Pattern(self.session, newptr, self.owner)
def onecell(self):
newptr = self.lifelib('GetOneCell', self.ptr)
return Pattern(self.session, newptr, self.owner)
def components(self, halo='ooo$ooo$ooo!'):
ccs = []
x = self
while (x.nonempty()):
cc = x.component_containing(halo=halo)
x = x - cc
ccs.append(cc)
return ccs
def match(self, live, dead=None, halo=None):
if halo is not None:
......
......@@ -14,6 +14,22 @@ class TestGoL(unittest.TestCase):
self.sess = lifelib.load_rules('b3s23')
self.lt = self.sess.lifetree(memory=1000, n_layers=4)
def test_components(self):
pat = self.lt.pattern('''7b3o5b3o16b3o5b3o$6bo2bo5bo2bo15bo2bo3bo2bo$6bo3bo3bo3bo15bo9bo$7b4o3b
4o14b2o2bobobobo2b2o$8bobo3bobo15b2o2bobobobo2b2o$31bo5bo3bo5bo$10bo3b
o17bo2bobo3bobo2bo$5bo4b2ob2o4bo13b2o9b2o$5b2o3b2ob2o3b2o14bo9bo$10b2o
b2o$9b2o3b2o16b2o11b2o$9bo5bo16b2o11b2o2$36b2o3b2o$36b2o3b2o$19b3o8b3o
$3b3o3b2o3b2o3bo2bo7bo2bo12b3o$3bo2bo2b2o3b2o2bo3bo7bob2o12bo2bo$3bo
14bo3bo23bo2bo$4bobo11bo2bo$bo3bo13b2o12b3o5bob2o3bo2bo$bo25b2o4b2o7b
3o6bo$o26b3o2b4o10b2o2bo$29bo19bo$3bobo41b2o$3b3o$2bo2b3ob3o35bo$3b5o
3bo29b2o3bobo$2bobo2b2o2bo2bo26b3o2bobo$bobo5bo5bo$2bo7b2o4bo24b2obo$
3b2o6b3obo19bo6bo4b2o2bo$35bobo6bo6bo$35bo3b2o3bo$36bo4bo5b2o$36bo4bo$
37bo2bo!''', 'b3s23')
ncomps = len(pat.components())
self.assertEqual(ncomps, 41)
def test_samples(self):
pat = self.lt.pattern('xp40_33zxs48g69gvzx3021zy833')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment