Q&A for ptbsearch

For scripts to aid with computation or simulation in cellular automata.
User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 4th, 2015, 9:17 pm

Kazyan wrote:Sorry to bug you two
Not at all. Actually I was worried that I was causing uneasiness to this thread. Welcome.
chris_c wrote:No problem! Yeah, my Makefile knowledge is close to zero. It looks like I should add the line "all: ptb2 survive" before the "ptb2: ..." line. Either that or you will need to type "make survive" to (hopefully) get survive to compile.
You would probably want to add "clean: rm *.o ptb2 survive" too. Otherwise make may reuse existing object files, so it won't reflect the changes after you tweak the source, if I recall correctly.

I would say that the change in ptbsearch is very successful.
Here's one of the scripts that I am re-running:
viewtopic.php?p=23900#p23900
Before the update ptblist was working with about 2000 solutions (when adding the fourth catalyst)
Now it's about 1000 solutions, so it is about twice as fast. AND including all those rocks.
Actually it's about (2^(N/3))=1.26^N times as fast, N being the number of catalysts, as the number of reactions progresses kinda exponentially.
I'm using the double snapshot filter thingy, so using your version would have a little more solutions. But I don't think that would be much, as the test run last night gave 4 junk results out of about 40~50. That's +10% for each catalyst added. It would probably search for 1.3 times more results in a 4 catalyst search. (1300 in my example) Not too much.

Forgot to advertise that survive supports 'min-generation-to-survive-after-the-catalyst-is-restored' So this:

Code: Select all

survive ab 1 < ... > ...
Would output every catalyst surviving, and this:

Code: Select all

survive a 30 < ... > ...
Would output catalysts that only survive for >= 30 gens after restoration and so on. (The default was 10, by the way.)

And a small thing. You can just replace strings from an old script to make a new script (a script in the ptbsearch intro would do)
You can do it easily by:

Code: Select all

sed s/pitoblock/some_new_rxn/g pitoblock.sh > some_new_rxn.sh
And use a text editor (I just use nano in the command line directly.) to edit generation params.
Last edited by Scorbie on November 5th, 2015, 12:59 pm, edited 4 times in total.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 4th, 2015, 11:50 pm

Summary: I ran the same search with the newer ptbsearch that found the sparky HWSS eater, to see if there are more solutions. Unfortunately, the search could not find even the original HWSS eater.

I added four catalysts incrementally.

It did find the precursor when it added 3 catalysts:

Code: Select all

x = 34, y = 21, rule = B3/S23
29bo$27b3o$26bo$26b2o4b2o$32bo$30bobo$30b2o2$2b2o5bobobobobobo$o4bo3bo
bobobobobo$6bo17b2o$o5bo2bobobobobobo4b2o$b6o$9bobobobobobo$9bobobobob
obo3$15b2o$16bo$13b3o$13bo!
But couldn't find the following when added one more catalyst.

Code: Select all

x = 34, y = 21, rule = B3/S23
29bo$27b3o$26bo$26b2o4b2o$32bo$30bobo$30b2o2$2b2o5bobobobobobo$o4bo3bo
bobobobobo12b2o$6bo17b2o5bobo$o5bo2bobobobobobo4b2o6bo$b6o$9bobobobobo
bo$9bobobobobobo3$15b2o$16bo$13b3o$13bo!
But if ypu start from that three catalyst intermediate instead of from scratch (as chris did) it finds the eater with no problem.

I think the problem is somewhere in ptblist.pl, I guess.
Last edited by Scorbie on November 5th, 2015, 8:26 pm, edited 4 times in total.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 5th, 2015, 10:34 am

Here's one issue with survive from today.
viewtopic.php?f=2&t=1878&p=24657#p24657
The catalyst takes 14 :o gens to recover, so survive couldn't filter that from an identical reaction:

Code: Select all

x = 74, y = 35, rule = B3/S23
2o2b6o40b2o2b6o$2bobo2bo44bobo2bo$bob4o44bob4o$2o2b2obo42b2o2b2obo$obo
bo2b3o40bobobo2b3o$o2bo2bo2bo40bo2bo2bo2bo$2bo5b2o42bo5b2o$2ob3obo42b
2ob3obo$5b3o47b3o$bo4bo2bo41bo4bo2bo12$17b2o$16bobo$16bo$14b3o47b2o$
13bo3b2o44bo2bo2b2o$13b4o2bo43bobo4bo$16bob2o44bo5bob2o$13b2obo50b2obo
bo$14bobo50bo2bo2bo$12bobob2o46bo4bo2b2o$12b2o50b5o2$66b2obo$66bob2o!
What happened is this (may be wrong by 1 gen, but the general point is the same):

Code: Select all

        eater3 drifter
reaction 79     79   -> startgen = 79, maxgen = 79 + 10(restorefilter) + 10 = 99
restored 87     93
passed   97     103  -> passed restorefilter at 10(restorefilter) gens after the restoration
snapshot 99     103 -> eater3 taken at maxgen, drifter taken when passing restorefilter
So the eater3's snapshot was taken at maxgen, but the drifter's snapshot is taken right when it passed the restorefilter. This is because drifter passed the restorefilter after maxgen. i.e.

Code: Select all

startgen+recoverytime+restorefilter > maxgen = startgen + restorefilter + 10
<=> recoverytime > 10
So this happens to every catalyst with recoverytime > 10.
As a workaround, just increase that 10 there to something >= than the catalyst's recovery time.

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 5th, 2015, 2:16 pm

Scorbie wrote:ptb2 couldn't add the boat here. Here's an excerpt from ptbsearch after adding 4 catalysts, before running survive.
Let's get concrete. With scorbie.rle as:

Code: Select all

x = 34, y = 21, rule = B3/S23
29bo$27b3o$26bo$26b2o4b2o$32bo$30bobo$30b2o2$2b2o5bobobobobobo$o4bo3bo
bobobobobo$6bo17b2o$o5bo2bobobobobobo4b2o$b6o$9bobobobobobo$9bobobobob
obo3$15b2o$16bo$13b3o$13bo!
and with boat.list as:

Code: Select all

.**!z.*!.*!
I ran the command:

Code: Select all

./ptb2 scorbie.rle boat.list 130 0 100 0
and I got the one expected result sent back to me. Can you reproduce the problem in more detail, otherwise it's difficult to know what to say.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 5th, 2015, 7:41 pm

I also did reproduce that, but running ptb2 and ptblist to add catalysts from scratch one by one doesn't find that pattern.

I get your point. Probably the problem is in ptblist.pl or else I really don't know. I'll try to rerun the script when I have time.

Chris, could you think of any bad points if one increases that 10 at my previous post?

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 5th, 2015, 9:42 pm

Scorbie wrote: Chris, could you think of any bad points if one increases that 10 at my previous post?
I wanted to keep that number as small as possible because I was worried about a different catalysis starting before "maxgen" of the the previous catalysis. I can't work out if I am just being paranoid or if this is a real issue. If not then increasing the number from 10 should be no problem at all.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 6th, 2015, 1:18 am

chris_c wrote:
Scorbie wrote:Chris, could you think of any bad points if one increases that 10 at my previous post?
I wanted to keep that number as small as possible because I was worried about a different catalysis starting before "maxgen" of the the previous catalysis. I can't work out if I am just being paranoid or if this is a real issue. If not then increasing the number from 10 should be no problem at all.
I would also like to keep that number small, just in case. I just wanted the end user (and myself) to know about its side effects, when increasing the number is inavoidable.
Still, I'm curious at your explanation. If a different catalysis started, doesn't that update firstgen and maxgen to those of the new catalysis? (And the snapshot is updated too.) Still, on further thoughts, there might be situations where the catalysts are destroyed before "maxgen". (If then, catalysts with trivial stator variants would all be destroyed at different gens, causing the filter to fail.)
Last edited by Scorbie on November 6th, 2015, 6:33 am, edited 1 time in total.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 6th, 2015, 5:20 am

Chris, I think we do have a problem with the current survive. (Hopefully you didn't update your main repo, right?)
Compile survive with your rock source and run a small search with these files:

test

Code: Select all

aa...........
bb...........
.............
.a...........
aa...........
a.a..........
....a........
.............
...a..a......
.............
.....a..a....
.............
.......a..a..
.............
.........a..a
.............
...........a.
take any catalyst file that has eaters and run the following:

Code: Select all

./ptb2 test catalysts 50 0 1 0 > test1
Hopefully it won't take too long.
Then run the following:

Code: Select all

./survive a < test1
survive from the main repo outputs some solutions (despite not being exciting) but survive from the rock repo does not report any solutions. Is there some kind of problem when survive has something to restore?

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 6th, 2015, 6:39 am

Scorbie wrote: Compile survive with your rock source and run a small search with these files:

test

Code: Select all

aa...........
bb...........
.............
.a...........
aa...........
a.a..........
....a........
.............
...a..a......
.............
.....a..a....
.............
.......a..a..
.............
.........a..a
.............
...........a.
I can see why that input is going to cause problems straight away. Remember that the definition of "restored" in the rock branch is "cells are alive and all neighbouring cells are dead". If you run with "./survive a" then the catalyst is just the pair of b-cells and they never have a clean boundary (without dying in the next generation).

It looks like with the rock branch, the trick of labelling SLs with two different letters is impossible.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 6th, 2015, 7:05 am

Ah, yes, you're right. The following outputs solutions. Thanks again! I'll rerun the problematic HWSS search to see if the problem is reproducible.

Code: Select all

abba
.bb
.
.
..aaa
..a
...a

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 6th, 2015, 12:26 pm

Reran the problematic script. Didn't find the HWSS eater. Yes, the problem is consistent, and I finally found out what the problem is.
It turns out that the original pattern was found by amazing sheer luck, not that the new ptbsearch had a problem.
The new ptblist.pl only adds catalysts till the original one was destroyed. When I ran ptbsearch incrementally, the relevant result was this:
(Whoops, I must have mad a mistake when editing the file, but you can ignore it as it is irrelevant to what I am describing now.)

Code: Select all

.............................*!...........................***!..........................*!..........................z*....**!................................*!..............................*.*!..............................z*!!..aa.....a.a.a.a.a.a!a....a...a.a.a.a.a.a!......a.................bb!a.....a..a.a.a.a.a.a....bb!.aaaaaa!.........a.a.a.a.a.a!.........a.a.a.a.a.a!!!...............*z!................*!.............***!.............*! 72 4 151 115 ...............**!....**........*..*!*....**.......*.*!...............*!*....**!.....**! ......*.....***!.....*.*....***!.......**......*!.*....**..*...**!*.......*...*.*!*....*..******!.****.***...*!......*!.......**!
It is describing this pattern.

Code: Select all

x = 34, y = 21, rule = B3/S23
29bo$27b3o$26bo$26b2o4b2o$32bo$30bobo$30b2o2$2b2o5bobobobobobo$o4bo3bo
bobobobobo$6bo17b2o$o5bo2bobobobobobo4b2o$b6o$9bobobobobobo$9bobobobob
obo3$15b2o$16bo$13b3o$13bo!
The fifth param indicates when the catalyst is destroyed. It says gen 115.
When we run the pattern, though, we can see that the eater is destroyed at gen 120 or so. I'm curious why it announces failure so early.
Anyway, the boat reacts at gen 117, so that number should be >= 117 in order to find the boat catalyst.

Then how did the original search add the boat?
If you run that pattern carefully, you could see that the eater is slightly restored (see gen 117-119 to see what I mean) before being permanently wrecked. The new survive doesn't detect that as restoration (which is a good thing) as one of the boundary cells are on. The old survive, on the other hand, detects this as restoration, because it doesn't care about boundary cells. This (wrong) restoration detection saves 2 generations. If I run the same search with the old survive, the 115 would probably be 117. And I'm pretty sure that's how the old ptb2 added the boat at the last moment.

In summary, I'd say it's an edge case of an edge case of an edge case, so no problems with the new ptbsearch, I think. Still I'm really curious why on earth would survive underestimate the wreck gen to be 115 not 120 (and make me spend quite a lot of time and effort) When I do the math with the source code I think it should report as 117.

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 8th, 2015, 10:31 am

Scorbie wrote: In summary, I'd say it's an edge case of an edge case of an edge case, so no problems with the new ptbsearch, I think. Still I'm really curious why on earth would survive underestimate the wreck gen to be 115 not 120 (and make me spend quite a lot of time and effort) When I do the math with the source code I think it should report as 117.
Thanks for taking the time to investigate. That does sound very frustrating!

You are right that the generation output by survive is a bit wrong and I think there are two reasons why. Starting from this pattern:

Code: Select all

x = 34, y = 21, rule = B3/S23
29bo$27b3o$26bo$26b2o4b2o$32bo$30bobo$30b2o2$2b2o5bobobobobobo$o4bo3bo
bobobobobo$6bo17b2o$o5bo2bobobobobobo4b2o$b6o$9bobobobobobo$9bobobobob
obo3$15b2o$16bo$13b3o$13bo!
The last generation the catalysts are completely clean is generation 116 and the last generation they are alive (but not clean) is generation 119. The new version of survive reports 115 and the old version reports 117. How do we explain these differences?

I think the reason for the off-by-one error (115 vs 116) is because "generate" is being called at the top of the loop instead of the bottom. When we first enter the loop "generate" moves us into generation 1 but the loop variable "i" is still equal to 0.

So how to explain the 117 in the old version of survive? It's because of the fact that fail only gets reset to zero when restore is big enough so the behaviour is a bit odd when you get a "near restoration" such as T=119. To get rid of this I just keep track of "last survival generation" the obvious way instead of using a formula. Suggested patch is here.

With this version the survival time is 116 and this should be sufficient for ptb2 to place the boat. (That is an untested statement ---- ptb2 should consider that the boat interacts with the patten at generation 116).

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 8th, 2015, 7:10 pm

chris_c wrote:
Scorbie wrote:Still I'm really curious why on earth would survive underestimate the wreck gen to be 115 not 120 (and make me spend quite a lot of time and effort) When I do the math with the source code I think it should report as 117.
Thanks for taking the time to investigate. That does sound very frustrating!
Thanks... I was 1/4 joking and 3/4 serious :?
chris_c wrote:I think the reason for the off-by-one error (115 vs 116) is because "generate" is being called at the top of the loop instead of the bottom. When we first enter the loop "generate" moves us into generation 1 but the loop variable "i" is still equal to 0.
Whoa. That seems pretty brain-rolling to find. Thanks again for your explanation. Now I get what's happening here.
chris_c wrote:To get rid of this I just keep track of "last survival generation" the obvious way instead of using a formula. Suggested patch is here.
Just asking, how about setting it to when one of the * cells is damaged? Still, thanks for the fix, and I'll try that in my next ptbsearch run.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 9th, 2015, 11:27 am

Running the same HWSS test with your new version, found something really annoying. The problematic pull request I mentioned earlier.
around ptbsearch2.c:50, this line:

Code: Select all

     if(depth == 0)fprintf(stderr, "Working with perturber #%d\n", iperturb+1);
Should be like this in order for ptbsearch to be quiet in ptblist runs.

Code: Select all

     if(depth == 0 && maxDepth > 0)
        fprintf(stderr, "Working with perturber #%d\n", iperturb+1);
Also, here's the ptblist.pl that I currently use. I wanted to call ptblist from subdirectories, but the original one doesn't work, so I tweaked it a little bit. It's your decision to add them or not. It's a minor change anyway. I'm totally okay with both.

Code: Select all

#!/usr/bin/env perl
use File::Basename;

# Sometimes I call ptblist in other directories...
my $ptbdir = dirname(__FILE__);
my $temp = "temp_$ARGV[0]";

# Get number of lines
open TEMP, $ARGV[0];
1 while(<TEMP>);
my $all = $.;
close TEMP;

# Main loop of ptb2
my $cur = 0;
open PATLIST, $ARGV[0];
while (<PATLIST>)
{
    $cur++;
    @flds = split(" ");

    if(scalar(@flds) < 2){
        $gen0 = 18;
    }else{
        $gen0 = $flds[1];
    }
   
    if(scalar(@flds)< 4){
        $gen1 = $ARGV[2];
    }elsif($ARGV[2] < $flds[4]){
        $gen1 = $ARGV[2];
    }else{
        $gen1 = $flds[4];
    }
   
    $pat = $flds[0];
    $pat =~ s/!/.\n/g;

    open OUTPAT, "> $temp";
    print OUTPAT $pat;
    close OUTPAT;

    print STDERR "==========$cur/$all==========\n";
    system("$ptbdir/ptb2 $temp $ARGV[1] $gen1 0 $gen0 1");

}
close PATLIST;
unlink "$temp";
EDIT: Still running the exact same search. Let me tell you the number of reactions ptblist has to cope with when adding the fourth catalyst. (Which is the bottleneck.)
old ptbsearch: 2000
new ptbsearch: 1400 --- EDIT2: No results; the new ptb2 must have thought the boat is added at gen 117.
new ptbsearch, with double filter is 1000, but I'm kinda afraid that it might lose some unique reactions. I hope not :roll:

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 22nd, 2015, 9:01 pm

EDIT: @chris Do you think the following can be done trivially or is it kind of a hassle?
Allowing "all-state-3" catalysts in a non-transparent run. I came out with these idea cause:
i) Sometimes when you union all the catalytic sites, every cell has to be state 3. So one has to make two catalysts, which looks kinda redundant.
ii) On censusing one might want to run a search without prior information on catalyst recation mechanism. And one's pretty sure it won't be transparent, so would prefer a way that is as fast as an opaque search.

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 23rd, 2015, 6:28 am

Scorbie wrote:EDIT: @chris Do you think the following can be done trivially or is it kind of a hassle?
Allowing "all-state-3" catalysts in a non-transparent run. I came out with these idea cause:
i) Sometimes when you union all the catalytic sites, every cell has to be state 3. So one has to make two catalysts, which looks kinda redundant.
ii) On censusing one might want to run a search without prior information on catalyst recation mechanism. And one's pretty sure it won't be transparent, so would prefer a way that is as fast as an opaque search.
If all the cells are state 3 then how can ptb2 tell when the catalyst is "broken" and shortcut the search? If it is going to do this then ptb2 needs some more information. One possibility is that when the catalyst is placed we count the number of cells that die within the next 10 generations and only accept it as a valid catalyst placement if the number of dead cells is within certain bounds.

This could help in two ways. In your example it could filter reactions where too much of the catalyst dies.

Also it could help in the other direction. For example, we could teach ptb2 to only place transparent blocks if more than 2 cells die shortly after the catalyst placement. It would hopefully prevent those annoying duplicates from appearing.

It might be an idea worth thinking about but I'm probably not going to do it this week.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 23rd, 2015, 6:38 am

Thanks for the advice and the suggestion.
It might be an idea worth thinking about but I'm probably not going to do it this week.
Nah. I didn't want to burden you. I myself am trying to focus on utilizing the search rather than developing it right now.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 24th, 2015, 8:20 pm

I think there's a slight glitch on ptb2.

Test input:

Code: Select all

aa
.aa
.a
test.list

Code: Select all

##Eater 1
A$3A$3.A$2.AC!
3.A$.3A$A$CA!

##Long Hooks
3.2C$C3.A$4A2$4A$C3.A$3.2C!
The catalysts are the following. I'm verifying the RLE string with a script so I think I'm super sure it has no typos.

Code: Select all

x = 25, y = 24, rule = LifeHistory
A22.2C$3A17.C3.A$3.A16.4A$2.AC$20.4A$20.C3.A$23.2C14$3.A$.3A$A$CA!
When I run

Code: Select all

ptb2 test test.list 55 1 50 0 > test2; makematrix.pl < test2 > test2.life
There's only the following:

Code: Select all

x = 12, y = 19, rule = B3/S23
3b2o$o3bo$4o$11bo$4o5b3o$o3bo3bo$3b2o3b2o10$7b2o$8b2o$8bo!
Perhaps this is filtered:

Code: Select all

x = 11, y = 16, rule = B3/S23
o9bo$3o5b3o$3bo3bo$2b2o3b2o10$6b2o$7b2o$7bo!
Because ptb2 only puts the left eater first, then the right eater, and not the other way around?

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » November 24th, 2015, 9:14 pm

Scorbie wrote:I think there's a slight glitch on ptb2.
Yep, that looks like pretty solid evidence of a bug. Note that if I switch the order of the eaters in the catalyst file then I do get the other result. I've got no idea what is causing this. I'll try to investigate some time.

EDIT: No, hang on. It's not a bug it's a feature. If the left eater is placed first (which would happen at generation 51) then on it's own it is destroyed in less than 15 generations. Therefore the search is pruned because of this line.

If the right hand eater gets placed first then it survives for long enough on it's own and the search can carry on.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » November 24th, 2015, 10:21 pm

No, hang on. It's not a bug it's a feature.
Yep. I basically tried to say the same, rather cryptically, at the last sentence of my post. Good to know it didn't bug you for long.

Still, it's kinda unnatural to see that the search results are subjective to the order of catalysts... Well lowering that 15 to about 10 will solve in this case.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » December 26th, 2015, 1:50 am

Here are some enhancements to ptblist. This ptblist.py replaces both ptblist.pl and ptblist2.pl.

Code: Select all

#!/usr/bin/env python3

# Usage: Supports both ptblist.pl syntax and ptb2 syntax.
# ptblist.py patfile catfile lastgen
# ptblist.py patfile catfile lastgen #_of_cats-1 firstgen #_of_transp_cats


import os
import sys
import subprocess
from collections import OrderedDict
from datetime import datetime, timedelta
from collections import deque


def prettytimedelta(td):
    """Just pretty format of the timedelta object"""
    days = td.days
    hours, less = divmod(td.seconds, 3600)
    mins, secs = divmod(less, 60)
    secs = round(secs + td.microseconds/100000, 2)
    unitrepr = [(days, 'd'), (hours, 'h'), (mins, 'm'), (secs, 's')]
    words = ["{}{}".format(unit, reprstr)
             for unit, reprstr in unitrepr if unit or reprstr == 's']
    return ''.join(words)

def runptbs(patsfile, catsfile, lastgenlimit='18',
            ncats='0', firstgenlimit='0', ntranspcats='1'):
    """Run ptb2 with the given params."""

    # Get number of patterns
    with open(patsfile) as patlist:
        npats = sum(1 for pat in patlist)

    # Print the types and number of catalysts
    catcount = OrderedDict([('Not specified', 0)])
    curcat = 'Not specified'
    with open(catsfile) as catalist:
        for line in catalist:
            if line.startswith('##'):
                curcat = line.rstrip()
                catcount[curcat] = 0
            if not line.startswith('#') and line.rstrip():
                catcount[curcat] += 1
            #if not line.rstrip(): curcat = 'Not specified'
    print("==========Catalysts==========", file=sys.stderr)
    for catinfo in catcount.items():
        if catinfo[0] == 'Not specified':
            print("{}: {}".format(*catinfo), file=sys.stderr)
        else:
            print("{} x {}".format(*catinfo), file=sys.stderr)
    print("Total Catalysts: {}".format(sum(catcount.values())), file=sys.stderr)

    tempfilename = 'temp_{}'.format(patsfile)

    # Main loop of ptb2
    print("==========Status==========", file=sys.stderr)
    starttime = datetime.now()
    recenttimes = deque([starttime], 10) # Save 10 recent times.
    with open(patsfile) as patlist:
        for curr, pat in enumerate(patlist):
            # Get the firstgen, lastgen, and pattern
            # with the limit given by the user and the reaction.
            fields = pat.split()
            try:
                pattern, initrxngen, _, _, lastsurvivegen = fields[:5]
                firstgen = max(firstgenlimit, initrxngen, key=int)
                lastgen = min(lastgenlimit, lastsurvivegen, key=int)
            except ValueError:
                try:
                    pattern, initrxngen = fields[:2]
                    firstgen = max(firstgenlimit, initrxngen, key=int)
                    lastgen = lastgenlimit
                except ValueError:
                    pattern = fields[0]
                    firstgen = firstgenlimit
                    lastgen = lastgenlimit
            pattern = pattern.replace('!', '.\n')
            with open(tempfilename, 'w') as tempfile:
                tempfile.write(pattern)
            # ptb2 spews out the same catalyst information everytime to stderr.
            # Redirect it to /dev/null (or its friends)
            subprocess.call(['./ptb2', tempfilename, catsfile, lastgen, ncats,
                             firstgen, ntranspcats], stderr=subprocess.DEVNULL)
            # Print time info (remaining time etc.) to stderr.
            currtime = datetime.now()
            elapsed = currtime - starttime
            recenttimes.append(currtime)
            # Average time taken per pattern
            recentavgtime = (currtime - recenttimes[0])/len(recenttimes)
            totalavgtime = elapsed/(curr+1)
            remainingtime = totalavgtime * (npats-curr-1)
            print("Pat: {}/{}, Tot: {}, Avg: rec {}/tot {} per pat, Est: {}"
                  .format(curr+1, npats, *(prettytimedelta(td) for td in
                          [elapsed, recentavgtime, totalavgtime, remainingtime])),
                          file=sys.stderr)

    os.remove(tempfilename)

# Main
runptbs(*sys.argv[1:])
Usage:
  • You can use it like the original ptblist.pl: ptblist.py pattern cats.list lastgen
  • You can use it like ptb2: ptblist.py pattern cats.list lastgen #_of_cats-1 firstgen #_of_transparent_cats
Features:
  • It doesn't report catalyst information (generated by ptb2) every time to stderr. Prints the catalyst information just once in the beginning.
  • Gives timing information to stderr similar to CatForce. That includes: Total elapsed time, average time for recent 10 patterns and all patterns, an estimate of the remaining search time.
Unexpected Features:
  • Can be stopped with Ctrl+c (The perl script cannot.)
  • Raises errors (with helpful info) if one made a typo or sth. The original perl script silently continues without reporting anything, which was a little annoying.
I tested it for one case and I think I got almost all bugs, but there might be some bugs remaining, especially the timing ones. (I can't figure out why the total time decreases for the second pattern, for instance.)
Except for the possible quirks, I strongly recommend using this instead of ptblist.pl or ptblist2.pl as it gives you less rubbish and more information.
Screenshots:
ptblist.py first picture
ptblist.py first picture
ptbpy1.png (53.98 KiB) Viewed 16082 times
ptblist.py second picture
ptblist.py second picture
ptbpy2.png (64.7 KiB) Viewed 16082 times
pt blist.pl second picture
pt blist.pl second picture
ptbpl2.png (51.99 KiB) Viewed 16082 times
EDIT: Alternative, concise version:

Code: Select all

#!/usr/bin/env python3

# Usage: Supports both ptblist.pl syntax and ptb2 syntax.
# ptblist.py patfile catfile lastgen
# ptblist.py patfile catfile lastgen #_of_cats-1 firstgen #_of_transp_cats


import os
import sys
import subprocess
from collections import OrderedDict
from itertools import dropwhile
from datetime import datetime, timedelta


def strftimedelta(td):
    """Just pretty format of the timedelta object"""
    days = td.days
    hours, less = divmod(td.seconds, 3600)
    mins, secs = divmod(less, 60)
    units = [(days, "d"), (hours, "h"), (mins, "m"), (secs, "s")]
    # Discard larger units than necessary
    words = ["{}{}".format(t, u)
             for t, u in dropwhile(lambda x: not x[0], units)]
    if not words:
        words = ["0s"]
    return ''.join(words)

def runptbs(patsfile, catsfile, lastgenlimit='18',
            ncats='0', firstgenlimit='0', ntranspcats='1'):
    """Run ptb2 with the given params."""

    # Get number of patterns
    with open(patsfile) as patlist:
        npats = sum(1 for pat in patlist)

    # Print the types and number of catalysts
    catcount = OrderedDict([('Not specified', 0)])
    curcat = 'Not specified'
    with open(catsfile) as catalist:
        for line in catalist:
            if line.startswith('##'):
                curcat = line.rstrip()
                catcount[curcat] = 0
            if not line.startswith('#') and line.rstrip():
                catcount[curcat] += 1
            #if not line.rstrip(): curcat = 'Not specified'
    print("==========Catalysts==========", file=sys.stderr)
    for catinfo in catcount.items():
        if catinfo[0] == 'Not specified':
            print("{}: {}".format(*catinfo), file=sys.stderr)
        else:
            print("{} x {}".format(*catinfo), file=sys.stderr)
    print("Total Catalysts: {}".format(sum(catcount.values())), file=sys.stderr)

    tempfilename = 'temp_{}'.format(patsfile)

    # Main loop of ptb2
    print("==========Status==========", file=sys.stderr)
    starttime = datetime.now()
    with open(patsfile) as patlist:
        for curr, pat in enumerate(patlist):
            # Get the firstgen, lastgen, and pattern
            # with the limit given by the user and the reaction.
            fields = pat.split()
            try:
                pattern, initrxngen, _, _, lastsurvivegen = fields[:5]
                firstgen = max(firstgenlimit, initrxngen, key=int)
                lastgen = min(lastgenlimit, lastsurvivegen, key=int)
            except ValueError:
                try:
                    pattern, initrxngen = fields[:2]
                    firstgen = max(firstgenlimit, initrxngen, key=int)
                    lastgen = lastgenlimit
                except ValueError:
                    pattern = fields[0]
                    firstgen = firstgenlimit
                    lastgen = lastgenlimit
            pattern = pattern.replace('!', '.\n')
            with open(tempfilename, 'w') as tempfile:
                tempfile.write(pattern)
            # ptb2 spews out the same catalyst information everytime to stderr.
            # Redirect it to /dev/null (or its friends)
            subprocess.call(['./ptb2', tempfilename, catsfile, lastgen, ncats,
                             firstgen, ntranspcats], stderr=subprocess.DEVNULL)
            # Print time info (remaining time etc.) to stderr.
            currtime = datetime.now()
            elapsed = currtime - starttime
            remainingtime = elapsed / (curr+1) * (npats-curr-1)
            print("Pat: {}/{}, Tot: {}, Est: {}".format(
                    curr+1, npats, strftimedelta(elapsed),
                    strftimedelta(remainingtime)),
                  file=sys.stderr)

    os.remove(tempfilename)

# Main
runptbs(*sys.argv[1:])

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » December 30th, 2015, 9:57 pm

Here is a really primitive target matcher that can be run after survive.
I guess the speed is tolerable if you turn off profiling.
I tried to optimize it a bit, but not much. A really strange thing is that using frozensets as target patterns are faster to iterate than using lists. Not sure why.

I really wonder how Guam's program searched for target patterns... (His main program seems to be something like Catforce, doesn't it?)

Code: Select all

# survive.py v0.95 by Dongook Lee
# Searches user-specified target patterns from ptbsearch dump files.
# The first layer should be empty.

import golly as g
import time
from collections import deque
from itertools import dropwhile
from datetime import datetime


def strftimedelta(td):
    """Just pretty format of the timedelta object"""
    days = td.days
    hours, less = divmod(td.seconds, 3600)
    mins, secs = divmod(less, 60)
    units = [(days, "d"), (hours, "h"), (mins, "m"), (secs, "s")]
    # Discard larger units than necessary
    words = ["{:02d}{}".format(t, u)
             for t, u in dropwhile(lambda x: not x[0], units)]
    if not words:
        words = ["0s"]
    return ''.join(words)


def symmetrize(onrle, offrle):
    """Return a list of the given target pattern of all symmetries.
    Each output target pattern is a list of coordinates(2-tuples).
    The target pattern input is given by its onfilter and offfilter as
    an RLE string.

    IMPORTANT NOTE!!!
    The onrle and offrle should be on the same bounding box!!!
    Like the following:
    onrle    offrle
    .....    *****
    ..*..    **.**
    ...*.    ***.*
    .***.    *...*
    .....    *****
    Also, the target would be pretty useless if a cell is both in onrle
    and offrle...
    """
    symmetries = [(1, 0, 0, 1), (-1, 0, 0, 1), (1, 0, 0, -1), (-1, 0, 0, -1),
                  (0, 1, 1, 0), (0, -1, 1, 0), (0, 1, -1, 0), (0, -1, -1, 0)]
    oncells, offcells = g.parse(onrle), g.parse(offrle)
    allcells = g.join(oncells, offcells)
    uniqtargets = set()
    for symm in symmetries:
        shadow = g.transform(allcells, 0, 0, *symm)
        offx, offy = min(shadow[::2]), min(shadow[1::2])
        newon = g.transform(oncells, -offx, -offy, *symm)
        newoff = g.transform(offcells, -offx, -offy, *symm)
        # Just checking...
        newall = g.join(newon, newoff)
        assert min(newall[::2]) == min(newall[1::2]) == 0
        ontarget = frozenset(zip(newon[::2], newon[1::2]))
        offtarget = frozenset(zip(newoff[::2], newoff[1::2]))
        uniqtargets.add((ontarget, offtarget))
    assert not (ontarget & offtarget),\
        "The ontarget and the offtarget overlap. You probably made a mistake."
    return uniqtargets


def assertsymm(x, n):
    assert len(x) == n, ''.join('On:{}\nOff:{}\n\n'.format(*a) for a in x)
# Just assertions
blocks = symmetrize('$b2o$b2o!', '4o$o2bo$o2bo$4o!')
hives = symmetrize('$2bo$bobo$bobo$2bo!', '5o$2ob2o$obobo$obobo$2ob2o$5o!')
# Gliders are used later, actually :)
gliders = (symmetrize('$2bo$3bo$b3o!', '5o$2ob2o$3obo$o3bo$5o!') |
           symmetrize('$bo$2b2o$b2o!', '5o$ob3o$2o2bo$o2b2o$5o!'))
assertsymm(blocks, 1)
assertsymm(hives, 2)
assertsymm(gliders, 16)
# Common targets:
'''cf) All of them in one place:
x = 14, y = 34, rule = B3/S23
7b7o$2bo4b3ob3o$2ob2o2bo2bo2bo$bo2bo2b2ob2obo$2b2o3b3o2b2o$8b6o4$8b4o$
b2o5bo2b2o$bobo4bobob2o$bob2o3bobo2bo$2b2o4b2o2b2o$2bo5b2ob3o$9b4o3$8b
5o$bo6bob3o$bobo4bobobo$b3o4bo3bo$3bo4b3obo$8b5o4$8b5o$b2o5bo2b2o$bobo
4bobob2o$3bo4b3ob2o$bobo4bobob2o$b2o5bo2b2o$8b5o!
'''
rs = symmetrize('$3bo$b2ob2o$2bo2bo$3b2o!',
                '7o$3ob3o$o2bo2bo$2ob2obo$3o2b2o$b6o!')
bs = symmetrize('$b2o$bobo$bob2o$2b2o$2bo!',
                '4o$o2b2o$obob2o$obo2bo$2o2b2o$2ob3o$b4o!')
hs = symmetrize('$bo$bobo$b3o$3bo!',
                '5o$ob3o$obobo$o3bo$3obo$5o!')
pis = symmetrize('$b2o$bobo$3bo$bobo$b2o!',
                 '5o$o2b2o$obob2o$3ob2o$obob2o$o2b2o$5o!')

# Personal additions:
cs = symmetrize('$b3o$bobo$bo2bo$3b2o!', '5o$o3bo$obob2o$ob2obo$3o2bo$2b4o!')
hfs = symmetrize(
        '2$4b3o$3bo3bo$2bo5bo$3bo3bo$4b3o!',
        '3b5o$2b7o$b3o3b3o$3ob3ob3o$2ob5ob2o$3ob3ob3o$b3o3b3o$2b7o$3b5o!')


def highlight(target, dx=0, dy=0):
    """For debugging.
    Highlights a pattern as a collection of coordinates.
    target: the catalyst target like the bs, hs, pis etc. above.
    dx, dy: the translation to match the target in the whole pattern.
    """
    mintx = min(tx for tx, ty in target)+dx
    minty = min(ty for tx, ty in target)+dy
    maxtx = max(tx for tx, ty in target)+dx
    maxty = max(ty for tx, ty in target)+dy
    g.select([mintx, minty, maxtx-mintx+1, maxty-minty+1])

""""""""""""""""""""""""""""SEARCH SETTINGS"""""""""""""""""""""""""""""
# The targets
# You can gather targets like this :)
targets = rs | bs | hs | pis | cs
# targets = hfs

# Filter settings
maxjunkpop = 20
# Gens to ignore targets from the beginning.
# e.g. If you're starting with a Herschel,
#      you wouldn't want the H to be identified at gen 0, would you?
ignoregens = 20
# Number of gens for the catalysts to survive after target detection
survivegens = 30

# Stablization settings
recentgens = 10
"""""""""""""""""""""""""""""OTHER SETTINGS"""""""""""""""""""""""""""""
# Directories
ptbdir = '/home/scorbie/Apps/ptbsearch'
ptbdumppath = g.opendialog("Choose ptbsearch dump file", "*", ptbdir)

# Alignment of filtered patterns
space = 80
pats_in_row = 10

# Enable profiling?
# cProfile profiling
CPROFILE = False
# Line profiling: needs to have line_profiler installed.
# Can be installed with: sudo pip install line_profilter
LINEPROFILE = False
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""


def matchtarget(oncells):
    """Get the pattern as coordinates of ON cells and see if
    any of the targets are in the pattern.
    Return (glidercnt, matchpop) where
    glidercnt is the number of gliders matched, and
    matchpop is the population of the largest target matched.
    """
    objects = gliders | targets
    # Remove gliders and try to match patterns
    glidercnt = 0
    matchpops = []  # Pops of matched patterns
    for ox, oy in oncells:
        for t in objects:
            # t[0] is a frozenset of target ON coordinates.
            # Get an arbitrary target ON cell as pivot.
            for px, py in t[0]:
                break  # Hacky...
            for tx, ty in t[0]:
                if (tx+ox-px, ty+oy-py) not in oncells:
                    break
            else:
                # All target ON cells in oncells!
                for tx, ty in t[1]:
                    if (tx+ox-px, ty+oy-py) in oncells:
                        break
                else:
                    # All target OFF cells not in oncells!
                    if t in targets:
                        matchpops.append(len(t[0]))
                    else:  # glider
                        glidercnt += 1
                        # highlight(t[0], ox-px, oy-py); time.sleep(0.5)
    return max(matchpops) if matchpops else 0, glidercnt


def groupblobs(cells):
    """Get the pattern as coordinates. Group cells into "blobs".
    Two cells are in the same blob iff they have a common neighbor,
    either dead or alive.
    Return the list of blobs including dead cells. Each blob is a set.
    """
    blobs = []
    for x, y in cells:
        newblob = {(nx, ny) for nx in (x-1, x, x+1)
                   for ny in (y-1, y, y+1)}
        for blob in blobs[:]:
            if blob & newblob:
                blobs.remove(blob)
                newblob |= blob
        blobs.append(newblob)
    return blobs

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""


def main():
    g.setrule('B3/S23')
    g.setalgo('QuickLife')
    g.autoupdate(True)
    # Position of the current filtered pattern:
    patx, paty = 0, 0
    cur_pat_col = 0
    g.setlayer(0)
    try:
        g.addlayer()
        g.setlayer(0)
        with open(ptbdumppath) as ptbdumpfile:
            npats = sum(1 for line in ptbdumpfile)
        starttime = datetime.now()
        with open(ptbdumppath) as ptbdumpfile:
            for curr, line in enumerate(ptbdumpfile):
                # Initialize
                g.new('')
                g.setmag(2)
                currtime = datetime.now()
                elapsed = currtime - starttime
                remainingtime = elapsed / (curr+1) * (npats-curr-1)
                g.show("Pat: {}/{}, Tot: {}, Est: {}".format(
                    curr+1, npats, strftimedelta(elapsed),
                    strftimedelta(remainingtime)))
                pattern = line.split()[0]
                rows = pattern.split('!')
                # Rock cells: Catalyst cells that should never die.
                rockcells = set()
                catcells = set()
                # Parse ptbdump; get the whole pattern and rock cells.
                for y, row in enumerate(rows):
                    for x, char in enumerate(row):
                        g.setcell(x, y, 0 if char == '.' else 1)
                        if char == '*':
                            rockcells.add((x, y))
                            catcells.add((x, y))
                        if char == 'z':
                            catcells.add((x, y))
                catpop = len(catcells)
                initpat = g.getcells(g.getrect())
                recentpops = deque([], recentgens)
                envelope = set()
                # Run the pattern and check for matches
                while int(g.getgen()) < 500:
                    g.run(1)
                    curpop = int(g.getpop())
                    recentpops.append(curpop)
                    # Just let it run for "ignoregens"
                    if int(g.getgen()) < ignoregens:
                        continue
                    curpat = g.getcells(g.getrect())
                    oncells = {(x, y) for x, y in
                               zip(curpat[::2], curpat[1::2])}
                    envelope |= oncells
                    # Abort if any catalyst is damaged.
                    if not rockcells <= oncells:
                        break
                    oncells -= catcells
                    # Abort if the pattern "stabilized."
                    if (len(recentpops) == recentgens and
                            all(pop == recentpops[0] for pop in recentpops)):
                        break
                    # Check if the pattern contains the target
                    matchpop, glidercnt = matchtarget(oncells)
                    if not matchpop:
                        continue
                    # Check the junk population is small enough.
                    junkpop = curpop - catpop - 5 * glidercnt - matchpop
                    # g.show("cur:{} cat:{} gl:{} match:{} junk:{}".format(
                    #          curpop, catpop, 5*glidercnt, matchpop, junkpop))
                    if junkpop > maxjunkpop:
                        continue
                    # Check if all catalysts reacted:
                    #   1. Group the catayst cells into catalysts.
                    catalysts = groupblobs(catcells)
                    #   2. Check if every blob reacted:
                    envelope -= catcells  # Fallen into this!
                    hyperenvelope = {(nx, ny) for x, y in envelope
                                     for nx in (x-1, x, x+1)
                                     for ny in (y-1, y, y+1)}
                    # highlight(hyperenvelope)
                    # g.show("Envelope (+ 1 cell padding)")
                    # time.sleep(1)
                    # for cat in catalysts:
                    #     highlight(cat)
                    #     if not (hyperenvelope & cat):
                    #         g.show("Catalyst does not meet Envelope!!!")
                    #         time.sleep(1)
                    #     else:
                    #         g.show("Catalyst meets Envelope!")
                    #         time.sleep(1)
                    #         highlight(cat & hyperenvelope)
                    #         g.show("Here's the overlapping region.")
                    #         time.sleep(1)
                    if any(not(hyperenvelope&cat) for cat in catalysts):
                        continue
                    # Check if the catalyst survives long enough:
                    g.run(survivegens)
                    curpat = g.getcells(g.getrect())
                    oncells = {(x, y)
                               for x, y in zip(curpat[::2], curpat[1::2])}
                    if not rockcells <= oncells:
                        break
                    # Passed all filters!!!
                    # Save the pattern to the next layer.
                    g.setlayer(1)
                    g.putcells(initpat, patx, paty)
                    cur_pat_col += 1
                    if cur_pat_col < pats_in_row:
                        patx += space
                    else:
                        patx, paty, cur_pat_col = 0, paty+space, 0
                    g.setlayer(0)
                    break
    except:
        # g.dellayer()
        if not ptbdumppath:
            g.exit("No file specified; aborting.")
        else:
            raise

if CPROFILE:
    import cProfile
    import pstats
    cProfile.runctx('main()', globals(), locals(), filename='profdump_survive')
    with open('profile-survive.txt', 'w') as profileresults:
        st = pstats.Stats('profdump_survive', stream=profileresults)
        st.sort_stats('tottime')
        st.print_stats()
elif LINEPROFILE:
    from line_profiler import LineProfiler
    profile = LineProfiler(main)
    profile.runctx('main()', globals(), locals())
    profile.dump_stats('profdump_survive')
    with open('profile-survive.txt', 'w') as profileresults:
        profile.print_stats(profileresults)
else:
    main()
For those who want to test this:
Sample ptbsearch dump file here:

Code: Select all

....bb!....bb!!*z...a!zz..aa!....a.a!........a!.*!*.z....a..a!.z!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a! 5 3 0 247 0 !
......bb.............*z!......bb.............zz!!.......a!......aa!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 45 4 151 114 53 .........***!.........***!........*...**!.......**.*.**!........*.***!.........**!!............*!.*.....*...*.*.......***!*.*...*.*...**.....**.*!*.*..*...*.........***!.*....*.*...........*!.......*!!!**!**!
......bb!......bb!!.......a!......aa!*z....a.a!zz........a......zz!.................z*!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 25 4 0 274 10 .*!*.*!*.*!.*!!!!**!**!
......bb!......bb!!.......a!......aa!*z....a.a!zz........a.......zz!..................z*!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 28 4 0 283 28 ......*!.....*.*!....*.....*!....*....*.*!....*!....**.....*!.*..**...**!*.*...*.*!*.*!.*!!!!**!**!
......bb!......bb!!.......a!......aa!*z....a.a..............zz!zz........a............z*!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 38 4 151 103 22 ..........*!.........*.*!!.........***!!!!.*!*.*!*.*...***!.*!.......*..*!.........*!!**!**!
............z*!...........z.z!............z!!......bb!......bb!!.......a!......aa!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 35 4 0 499 48 ....**!...*!...*...*!...**.**!.....*.**.**..*..**!.*........*.******.**!*.*...**.......*....*!*.*....*...*.....*..*!.*.....*.........*.*!........*!!!**!**!
......bb!......bb!!.......a!......aa!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!........z*!........*......a..a!.........***!...........*.....a..a!!...................a! 18 6 0 499 28 ....*!...*.*!..*..*!..*....*!..*******!.*.......*!*...*****!*.....**!*..*!.*!
......bb!......bb!!.......a!......aa!*z....a.a!zz........a!!.........a..a!.....................z*!...........a..a......*!......................***!.............a..a.......*!!...............a..a!!.................a..a!!...................a! 37 4 151 57 40 .........***!..........**!..........*!.......*.*!!...............**!.*............*..*!*.*............**!*.*...*.***!.*.....***.**!.......*!.........*!.........*..*!**.......***!**!
.......bb!.......bb!!........a!.......aa!.*z....a.a!.zz........a!!..........a..a!!............a..a!!..*z..........a..a!.*.*!.*..............a..a!**!..................a..a!!....................a! 23 4 151 80 40 .......**!......**.**!.......*!.......*..*!.....**...*!.....**.....***!.**....*..**..*!*..*....*.....*!*..*.....**..*!.**.......**!!!**!**!
*!***......bb!...*.....bb!..*z!..........a!.........aa!...*z....a.a!...zz........a!!............a..a!!..............a..a!!................a..a!!..................a..a!!....................a..a!!......................a! 15 4 151 245 31 ......*!.....*.*!....*..*!....*....*!....*******!...*.......*!..*...*****!.*......**!*...*!!!*.*!.**!
......bb!......bb......**!..............*!.......a....*.*!......aa....z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 16 5 0 284 19 .....**!.....**!!!!.......**!***...**!...*...*!**.***!.....*!
......bb!......bb.......**!...............*!.......a.....*.*!......aa.....z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 18 6 0 265 10 ..*..*!.*...*!*...*!!!*.*!.**!
......bb!......bb.............**!.....................*!.......a...........*.*!......aa...........z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 31 4 151 82 34 .....*****!....*..***!.....***!!!.*!*.*!*.*!.*....*...**!......****.*!.......***.*!!**!**!
......bb!......bb..............**!......................*!.......a............*.*!......aa............z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 33 4 151 63 37 .....**!....*..**!....**.*..*!.....*.**.*!......*!.*!*.*........**!*.*........**!.*......*....*!.............*!........*!.........****!**.........*!**!
......bb!......bb................**!........................*!.......a..............*.*!......aa..............z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 37 4 151 74 31 .........***!..........**!..........*!.......*.*!!!.*!*.*!*.*...**......**!.*.....**.*...**!.......***!.........*!!**!**!
......bb!......bb.................**!.........................*!.......a...............*.*!......aa...............z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 39 4 151 52 27 ..........*!..........*!.........*.*!..........*!..........*!!!.*!*.*....*......***!*.*....*........**!.*....*.*........*!................*!!!**!**!
......bb!......bb...................**!...........................*!.......a.................*.*!......aa.................z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 43 4 0 358 32 .........***!........*...*!........*...*!........*...*!.........***!!.............**!.*.....*.....**!*.*....*!*.*...*.*!.*.....*!.......*!!!**!**!
......bb!......bb....................**!............................*!.......a..................*.*!......aa..................z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 45 4 151 66 47 .........***!!.......*.....*!.......*.....*!.......*.....*!!.........***..**!.............*.*!.*.....*....**..**!*.*...*.*....**...*!*.*..*...*....***.*!.*....*.*.......**!.......*!!!**!**!
......bb!......bb.....................**!.............................*!.......a...................*.*!......aa...................z*!*z....a.a!zz........a!!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 47 4 151 103 52 .........***!!.......*!.......*.....***!.......*....*..*!...........**!.........**....*!...........***.*!.*....***.....**!*.*..*...*...****!*.*..*...*....***!.*...*...*....*!......***!!!**!**!
......bb!......bb!!.......a!......aa......z*!*z....a.a.....*.*!zz........a.....*!................**!.........a..a!!...........a..a!!.............a..a!!...............a..a!!.................a..a!!...................a! 19 4 151 42 14 ...*!..****!.*...*!..**.*!!!!!**!**!
Save this as whatever.out and Select whatever.out inside survive.py. Make sure you tweak the hardcoded directories in the settings. (I grouped the settings and I'm sure they're not hard to find...)
Last edited by Scorbie on January 4th, 2016, 3:45 am, edited 1 time in total.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » January 3rd, 2016, 9:18 pm

Oh, No! There seems to be a bug in ptbsearch-sym_hack! (Or maybe I'm just doing it all wrong)
Here are the input files:
test.in (Size 22x18)

Code: Select all

*
***
...*
..zz

........a
.......a.a
......aa.aa
.......a.a...a
........a...a.a
...........aa.aa
............a.a
.............a

..................zz
..................*
...................***
.....................*
halfptb.list

Code: Select all

...*!.***!*!zz!
*!***!...*!..zz!
**!.*!.*.z!..*z!
..**!..*!z.*!z*!
z*!z*!
zz!**!
With the files above I ran:

Code: Select all

./ptb2 test.in halfptb.list 20 0 1 21 17 | ./makematrix.pl
Which prints out patterns to stdout. If you analyze them, you can clearly see that something's wrong...

Code: Select all

**.
**.
.
.
.
.
.
*.
***.
*..*.
****.
...*....*.
..**...*.*.
......*****.
.......*.*...*.
......*****.*.*.
.......*.*.*****.
........*...*.*.
...........*****.
............*.*...**.
.............*....*.
..................****.
..................*..*.
...................***.
.....................*.
.
.
.
.
.
....................**.
....................**.
Interestingly, if you overlap the eaters of the input and output patterns, the hf of the input pattern goes right in the middle of the two hfs of the output pattern.

If one runs ptbsearch (symmetry) with intermediate results like these, some are affected and some are not. Here are some intermediate results run with ptbsearch once more:

Code: Select all

x = 888, y = 639, rule = B3/S23
225bo79bo79b2o78b2o98bo75bo81bo66b2o$22bo202b3o77b3o78bo16bo62bo96b3o
73b3o63b2o14b3o66b2o$20b3o205bo79bo77bobo12b3o62bobo86bo6bo68bo6bo66b
2o6bo6bo$12bo6bo207b2o11bo66b2o78b2o4bo6bo66b2o15bo69bobo5b2o5b2o54b2o
3bobo5b2o72bobo5b2o78bo$11bobo5b2o217b3o79bo71bobo5b2o80b3o68b2ob2o11b
o55b2o2b2ob2o77b2ob2o82b3o$10b2ob2o215bo6bo80b3o70b2ob2o78bo6bo65b2o5b
obo3bo6bobo60bobo3bo75bobo3bo71bo6bo$11bobo3bo211bobo5b2o71bo6bo74bobo
3bo74bobo5b2o63bobo6bo3bobo5b2o62bo3bobo75bo3bobo69bobo5b2o$12bo3bobo
209b2ob2o76bobo5b2o74bo3bobo72b2ob2o69bo11b2ob2o71b2ob2o2b2o73b2ob2o
67b2ob2o$15b2ob2o209bobo3bo72b2ob2o83b2ob2o72bobo3bo65b2o5b2o5bobo66b
2o5bobo3b2o67b2o5bobo69bobo3bo$9b2o5bobo211bo3bobo72bobo3bo74b2o5bobo
74bo3bobo72bo6bo68bo6bo74bo6bo6b2o63bo3bobo$10bo6bo215b2ob2o72bo3bobo
74bo6bo4b2o72b2ob2o68b3o73b3o79b3o14b2o66b2ob2o$7b3o217b2o5bobo76b2ob
2o70b3o12bobo65b2o5bobo69bo75bo81bo78b2o5bobo$7bo220bo6bo71b2o5bobo71b
o16bo66bo6bo308bo6bo$225b3o80bo6bo89b2o62b3o313b3o$225bo11b2o66b3o161b
o15b2o298bo$237bo67bo179bobo$238b3o76b2o168bo306b2o$240bo76bo169b2o
305b2o$318b3o$320bo75$7bo$7b3o$10bo$9b2o3$12bo$11bobo294bo80bo79bo73b
2obo$10b2ob2o293b3o78b3o77b3o71b2ob3o$11bobo3bo293bo80bo79bo76bo$12bo
3bobo291b2o79b2o78b2o75b2o$15b2ob2o205bo82bo4bo75bo4bo74bo4bo71bo4bo$
16bobo206b3o80b3obobo74b3obobo73b3obobo70b3obobo$17bo210bo82b2ob2o76b
2ob2o75b2ob2o72b2ob2o$227b2o76b2o3b3obo3bo66b2o4b3obo3bo71b3obo3bo68b
3obo3bo$225bo4bo73bobo6bo3bobo64bobo7bo3bobo64b2o7bo3bobo8b2o60bo3bobo
$19b2o204b3obobo72bo7bobob2ob2o6b2o55bo8bobob2ob2o7b2o53bobo6bobob2ob
2o7bo60bobob2ob2o$19bo208b2ob2o70b2o6b2ob2obobo7bo55b2o7b2ob2obobo8bo
54bo7b2ob2obobo6bobo59b2ob2obobo$20b3o204b3obo3bo76bobo3bo6bobo65bobo
3bo7bobo53b2o8bobo3bo7b2o61bobo3bo$22bo199b2o6bo3bobo8bo67bo3bob3o3b2o
67bo3bob3o4b2o65bo3bob3o68bo3bob3o$223bo5bobob2ob2o5b3o70b2ob2o76b2ob
2o75b2ob2o72b2ob2o$220b3o5b2ob2obobo5bo74bobob3o74bobob3o73bobob3o70bo
bob3o$220bo8bobo3bo6b2o74bo4bo75bo4bo74bo4bo71bo4bo$230bo3bob3o81b2o
79b2o78b2o75b2o$233b2ob2o82bo80bo79bo76bo$234bobob3o80b3o78b3o77b3o74b
3ob2o$235bo4bo82bo80bo79bo76bob2o$237b2o$237bo$238b3o$240bo54$8bo$8b3o
$11bo$10b2o214bo79bo79bo77bo80bo$226b3o77b3o77b3o75b3o78b3o$229bo79bo
79bo77bo80bo$228b2o78b2o70b2o6b2o76b2o79b2o$13bo217bo79bo69bo9bo77bo
80bo$12bobo215bobo77bobo68bobo6bobo75bobo78bobo$11b2ob2o210bo2b2ob2o
72bo2b2ob2o68b2o2bo2b2ob2o70bo2b2ob2o73bo2b2ob2o$12bobo3bo207b3obobo3b
o63b2o4b3obobo3bo69b3obobo3bo67b3obobo3bo65b2o3b3obobo3bo$13bo3bobo
209bobo3bobo63bo7bobo3bobo71bobo3bobo62b2o5bobo3bobo64b2o6bobo3bobo$
16b2ob2o202bo4b2o4b2ob2o7b2o53bobo4b2o4b2ob2o69b2o4b2ob2o61b2o4b2o4b2o
b2o70b2o4b2ob2o$17bobo201b3o7bo3bobo9bo54b2o7bo3bobo73bo3bobo71bo3bobo
74bo3bobo$18bo201bo9bobo3bo7b3o63bobo3bo7b2o64bobo3bo71bobo3bo74bobo3b
o$220b2o7b2ob2o4b2o4bo64b2ob2o4b2o4bobo62b2ob2o4b2o67b2ob2o4b2o4b2o64b
2ob2o4b2o$230bobo3bobo71bobo3bobo7bo63bobo3bobo69bobo3bobo5b2o65bobo3b
obo6b2o$231bo3bobob3o69bo3bobob3o4b2o63bo3bobob3o67bo3bobob3o70bo3bobo
b3o3b2o$20b2o212b2ob2o2bo72b2ob2o2bo72b2ob2o2bo2b2o66b2ob2o2bo73b2ob2o
2bo$20bo214bobo77bobo77bobo6bobo66bobo78bobo$21b3o212bo79bo79bo9bo67bo
80bo$23bo214b2o78b2o78b2o6b2o68b2o79b2o$238bo79bo79bo77bo80bo$239b3o
77b3o77b3o75b3o78b3o$241bo79bo79bo77bo80bo67$315bo78b2o80bo73bo79bo86b
o72b2o$313b3o79bo80b3o71b3o77b3o84b3o70b2o$312bo79b3o81bo2bo73bo79bo
83bo2bo$229bo82b2o78bo83b4o72b2o78b2o83b4o$227b3o249bo4bo235bo4bo$226b
o251b2o3bobo233b2o3bobo$4bo221b2o254b5o225b2o9b5o$4b3o465b2o9bobo3bo
13bo51bo155bobo10bobo3bo59bo$7bo302bo79bo82bo8b5obobo10b3o51b3o77bo75b
o11b5obobo12b2o44b3o$6b2o302b3o77b3o77b3o10bobob5o8bo54bo2bo76b3o72b2o
12bobob5o11bo45bo2bo$224bo85bo2bo76bo2bo76bo13bo3bobo9b2o53b4o76bo2bo
86bo3bobo10bobo45b4o$12bo211b3o83b4o76b4o93b5o66bo4bo71b4o89b5o9b2o49b
o4bo$11bobo210bo2bo85bo4bo74bo4bo89bobo3b2o61b2o3bobo73bo4bo85bobo3b2o
55b2o3bobo$10b2ob2o209b4o84b2o3bobo72b2o3bobo89bo4bo66b5o71b2o3bobo85b
o4bo60b5o$11bobo3bo209bo4bo83b5o75b5o93b4o64bobo3bo72b5o89b4o58bobo3bo
$12bo3bobo207b2o3bobo83bobo3bo73bobo3bo90bo2bo63b5obobo72bobo3bo86bo2b
o57b5obobo$15b2ob2o210b5o81b5obobo71b5obobo90b3o64bobob5o70b5obobo86b
3o58bobob5o$16bobo212bobo3bo79bobob5o71bobob5o91bo65bo3bobo72bobob5o
87bo59bo3bobo$17bo212b5obobo79bo3bobo73bo3bobo161b5o72bo3bobo151b5o$
231bobob5o81b5o75b5o161bobo3b2o71b5o151bobo3b2o$22b2o208bo3bobo83bobo
3b2o72bobo3b2o158bo4bo73bobo3b2o148bo4bo$22bo212b5o83bo4bo74bo4bo164b
4o71bo4bo154b4o$23b3o210bobo3b2o84b4o76b4o161bo2bo76b4o151bo2bo$25bo
211bo4bo85bo2bo76bo2bo162b3o76bo2bo152b3o$242b4o83b3o77b3o164bo77b3o
154bo$242bo2bo85bo79bo244bo$243b3o$245bo$578b2o$578bo$328b2o79bo169b3o
76b2o150b2o$242b2o85bo77b3o171bo76bo151b2o$243bo82b3o77bo252b3o$240b3o
83bo79b2o253bo$240bo44$5b2o$6bo$6bobo$7b2o4bo$12bobo$11b2ob2o$12bobo3b
o$13bo3bobo212bo$16b2ob2o209b3o242b2o$17bobo209bo246bo$18bo4b2o194b2o
8b2o85bo80b2o77bobo$23bobo194bo95b3o79bo78b2o4bo$25bo194bobo96bo78bobo
81bobo$25b2o194b2o4bo90b2o79b2o4bo11b2o62b2ob2o9b2o$226bobo84b2o89bobo
10bo64bobo3bo6b2o$225b2ob2o84bo88b2ob2o7bobo57b2o6bo3bobo$226bobo3bo
81bobo87bobo3bo4b2o58b2o9b2ob2o$227bo3bobo81b2o4bo77b2o4bo3bobo75bobo$
230b2ob2o85bobo75bobo7b2ob2o75bo4b2o$231bobo85b2ob2o74bo10bobo81bobo$
232bo4b2o81bobo3bo70b2o11bo4b2o78bo$237bobo81bo3bobo87bobo77b2o$239bo
84b2ob2o88bo$229b2o8b2o84bobo89b2o$230bo95bo4b2o$227b3o101bobo$227bo
105bo$333b2o$328b2o$328bo$329b3o$331bo47$2o$bo$bobo$2b2o5bo225bo90bo
68bo90b2o$8bobo222b3o88b3o68b3o88bo$7b2ob2o220bo90bo74bo85bobo$8bobo3b
o217b2o89b2o72b2o85b2o$9bo3bobo375b2o$12b2ob2o203b2o170bo78b2o$13bobo
205bo89b2o79bobo77bo$14bo5b2o199bobo88bo80b2o5bo71bobo$20bobo199b2o5bo
82bobo84bobo71b2o5bo$22bo205bobo82b2o5bo77b2ob2o76bobo$22b2o203b2ob2o
87bobo77bobo3bo72b2ob2o$228bobo3bo83b2ob2o77bo3bobo72bobo3bo$229bo3bob
o83bobo3bo77b2ob2o72bo3bobo$232b2ob2o83bo3bobo77bobo76b2ob2o$233bobo
87b2ob2o77bo5b2o71bobo$234bo5b2o82bobo84bobo71bo5b2o$240bobo82bo5b2o
80bo77bobo$242bo88bobo79b2o78bo$242b2o89bo73b2o84b2o$333b2o72bo$230b2o
176b3o69b2o$231bo178bo68bobo$228b3o90b2o156bo$228bo93bo155b2o$319b3o$
319bo41$2b2o$3bo$3bobo$4b2o6bo$11bobo$10b2ob2o$11bobo3bo$12bo3bobo209b
2o21bo56b2o$15b2ob2o209bo19b3o57bo$16bobo210bobo16bo60bobo$17bo6b2o
204b2o6bo9b2o60b2o6bo13b2o$24bobo210bobo77bobo12bo$26bo209b2ob2o75b2ob
2o9bobo$26b2o209bobo3bo73bobo3bo6b2o$238bo3bobo65b2o6bo3bobo$241b2ob2o
63bobo9b2ob2o$242bobo64bo12bobo$232b2o9bo6b2o56b2o13bo6b2o$233bo16bobo
77bobo$230b3o19bo79bo$230bo21b2o78b2o46$241bo79bo83b2o64bo79bo78b2o84b
o9b2o68b2o78b2o$239b3o77b3o84bo64b3o77b3o76b2o4bo78bobo8b2o68b2o78b2o$
238bo79bo84b3o68bo79bo80bobo72b2o2b2ob2o$238b2o78b2o83bo69b2o78b2o75b
2o2b2ob2o71b2o3bobo3bo$11bo384bo233b2o3bobo3bo74bo3bobo3b2o$10bobo382b
obo238bo3bobo3b2o71b2ob2o2b2o68bo$5b2o2b2ob2o222bo153b2o2b2ob2o77bo
162b2ob2o2b2o62b2o8bobo72bobo$5b2o3bobo3bo218bobo152b2o3bobo3bo73bobo
162bobo67b2o9bo68b2o2b2ob2o77bo$11bo3bobo3b2o207b2o2b2ob2o157bo3bobo3b
2o62b2o2b2ob2o162bo4b2o142b2o3bobo3bo73bobo$14b2ob2o2b2o207b2o3bobo3bo
74bo82b2ob2o2b2o62b2o3bobo3bo74bo89b2o148bo3bobo3b2o62b2o2b2ob2o$15bob
o218bo3bobo3b2o67bobo82bobo73bo3bobo3b2o67bobo241b2ob2o2b2o62b2o3bobo
3bo$16bo222b2ob2o2b2o62b2o2b2ob2o82bo77b2ob2o2b2o62b2o2b2ob2o241bobo
73bo3bobo3b2o$240bobo67b2o3bobo3bo72bo85bobo67b2o3bobo3bo239bo77b2ob2o
2b2o$241bo74bo3bobo3b2o64b3o86bo74bo3bobo3b2o312bobo$319b2ob2o2b2o63bo
167b2ob2o2b2o313bo$320bobo68b2o167bobo$238b2o81bo161b2o76bo238b2o$239b
o243bo316b2o$236b3o245b3o$236bo249bo$880b2o$880b2o$318b2o243b2o$319bo
243bo$316b3o245b3o$316bo249bo33$11bo$10bobo$2b2o5b2ob2o$2b2o6bobo3bo$
11bo3bobo6b2o$14b2ob2o5b2o221bo60b2o$15bobo227b3o60b2o$16bo227bo$234bo
9b2o$233bobo78bo$225b2o5b2ob2o76bobo$225b2o6bobo3bo65b2o5b2ob2o$234bo
3bobo6b2o56b2o6bobo3bo$237b2ob2o5b2o65bo3bobo6b2o$238bobo76b2ob2o5b2o$
228b2o9bo78bobo$229bo89bo$226b3o$226bo$324b2o$324b2o!
(I parsed this from the intermediate results from my search)

chris_c
Posts: 966
Joined: June 28th, 2014, 7:15 am

Re: Q&A for ptbsearch

Post by chris_c » January 4th, 2016, 6:38 am

Scorbie wrote:Oh, No! There seems to be a bug in ptbsearch-sym_hack! (Or maybe I'm just doing it all wrong)
Here are the input files:
test.in (Size 22x18)
I don't know. If the input pattern has size 22x18 then 21 and 17 should be the correct values to use for x and y in the command line arguments. But from the output pattern it looks the y-value is out by 1. What happens if you run with 21 and 16 or 21 and 18? I don't have time to investigate in detail at the moment.

User avatar
Scorbie
Posts: 1693
Joined: December 7th, 2013, 1:05 am

Re: Q&A for ptbsearch

Post by Scorbie » January 4th, 2016, 7:58 am

chris_c wrote:What happens if you run with 21 and 16 or 21 and 18? I don't have time to investigate in detail at the moment.
I am pretty sure I got the alignments correct... The resultes look as if I put the params off by 1 BUT no, the output is not the input two copies of the input pattern misplaced. If you try to overlap the input you'll see that they don't match perfectly.

With all that said, I'll double check if something is different with others...


Ahh!!!! I think I made a mistake! A blank line is ignored (without incrementing y pos)by ptbsearch, right? Uhh... I must have parsed the pattern witout this in mind....

Sorry about the inconvenience...

Post Reply