Catalyst improvements WIP

For scripts to aid with computation or simulation in cellular automata.
User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » February 2nd, 2015, 5:38 am

I recently had an idea of different catalyst search approach:

1. Find all "single step" catalysts (catalyst that work on the initial interaction).
2. Try to combine 2-3 of "single step" catalyst to create new catalyst.
3. Recursively do the same to the results.

This approach can be simpler for parallel search, thus can be used by CUDA for example (or distributed search).
codeholic wrote:that if there's an escaping glider, it will enjoy to find all possible catalysts
This is the feature dvgrn introduced to catgl. Which brings us to the problem of ptbsearch - no one knows how it works and no one currently supports it. Otherwise it has no disadvantage over catgl (which is about 300 lines of code and much simpler to maintain).

---

On another topic - can anyone contact Guam just to post whatever he has?

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 2nd, 2015, 8:38 am

codeholic wrote:That's in fact the most annoying feature of ptbsearch, that if there's an escaping glider, it will enjoy to find all possible catalysts on each further glider's step, slowing down the search significantly and making it harder to sort out the search results. I wish there were a way to take the best of two programs.
Sadly, in spite of what I said, that's also the most annoying feature of catgl. For any glider generated by a catalysis you'll see it getting caught by an eater, eater2, tub-with-tail eater, and snake, every two ticks.

Currently the only way around this is to do single-catalyst searches and pick the best options manually for the next search. The original version of the 'catalyst' program had the additional problem that if a glider reached the fixed boundary of the search space, that branch of the search was considered to be a failure and was terminated, same as any other oversized exploding mess. Catgl at least monitors the boundaries and counts and removes gliders that reach it.

Now, computers are pretty fast these days, and we could fairly easily sort out all the results that are effectively identical, in a post-processing step. Catgl.py currently displays results in one long column, but with the addition of a small hash table we could see all the equivalent results (different glider eaters, catalysts that just interact with dying sparks, etc., etc.) in rows, making it much easier to scan down the left column to find the good stuff. It's just a matter of adding each final state to the hash table, with the catalysts subtracted out. Might have to change catgl's output format slightly to make this work, but it's not too difficult I think.

To me this seems like the only likely improvement for either catgl or ptbsearch, as far as glider-catching goes. The multiple glider eater locations are a pain, but it would slow things down even more to scan for gliders in each generation of each reaction. And removing gliders prematurely will give bad results fairly regularly, because not all gliders survive -- imagine searching for late-stage modifications of the F116 or Fx158 conduits, for example.

It does seem worth setting up a Golly-script GUI for ptbsearch, similar to catgl.py, and re-using the result-sorting code once someone gets the post-processing working for catgl...!

User avatar
Gustavo6046
Posts: 647
Joined: December 7th, 2013, 6:26 pm
Location: Brazil.

Re: Catalyst improvements WIP

Post by Gustavo6046 » February 3rd, 2015, 10:22 am

Eater 6s can be discovered this way!
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 8th, 2015, 7:14 pm

dvgrn wrote:
codeholic wrote:That's in fact the most annoying feature of ptbsearch, that if there's an escaping glider, it will enjoy to find all possible catalysts on each further glider's step, slowing down the search significantly and making it harder to sort out the search results. I wish there were a way to take the best of two programs.
Sadly, in spite of what I said, that's also the most annoying feature of catgl. For any glider generated by a catalysis you'll see it getting caught by an eater, eater2, tub-with-tail eater, and snake, every two ticks.
In terms of creating a best-of-both-programs search utility: there does seem to be one way in which catgl is significantly more efficient -- if I'm understanding and remembering correctly, anyway.

The catalyst list for ptbsearch doesn't seem to have a way to specify which direction an active reaction would be expected to come from. So doesn't it try a lot of very unlikely possibilities? For catgl, the catalysts show which cells have to be next to cells from the active reaction, to activate the catalyst -- so catgl doesn't waste any time trying to drop an eater2 in the wrong way around, where it would almost always fail. Does ptbsearch really not know which way is up, so to speak? or am I missing something?

So then ptbsearch can specify completely transparent catalysts, and catgl can't -- that's a really useful feature, especially if we get a distributed search running at some point and can hunt for less likely possibilities. Seems as if there's still a lot of potential in this stable-conduit search space -- the more options we test, in the long run, the more useful stuff we'll find. The trick is still to come up with an algorithm that can separate the really hopeful near-misses from the really wishful ones...!

User avatar
Gustavo6046
Posts: 647
Joined: December 7th, 2013, 6:26 pm
Location: Brazil.

Re: Catalyst improvements WIP

Post by Gustavo6046 » February 8th, 2015, 8:22 pm

Maybe with some catalyst(s) "ship tie boat with tail", and some tubs, we can reproduce an Eater (or maybe even, who knows, an Rock!!!) I know an bun-based pattern that, if put right in the middle of a traffic light, it can cleanly destroy it and act as a catalyst all at the same time!
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 8th, 2015, 10:58 pm

Gustavo6046 wrote:Maybe with some catalyst(s) "ship tie boat with tail", and some tubs, we can reproduce an Eater (or maybe even, who knows, an Rock!!!)
Eaters have occasionally been seen to appear out of a catalyzed active reaction -- there's even a periodic conduit that features a temporary eater that crystallizes out of the soup and then disappears cleanly again.

On the other hand, I don't think anyone will be looking for transparent-eater reactions, at least until after spending a decade or two running searches for transparent tubs and ponds and ships and half-blockades and everything else more common than eaters. There are eight different eater orientations, and for the purposes of this search they each count as different objects -- it doesn't do any good if a transparent eater reappears in the same place but a different orientation.
Gustavo6046 wrote:I know an bun-based pattern that, if put right in the middle of a traffic light, it can cleanly destroy it and act as a catalyst all at the same time!
In the context of catalyzed signal circuitry, it would be pretty much impossible for an active pattern to drop your "bun-based pattern" into the middle of a traffic light, or for a traffic light to form around your pattern -- so you'd have to add it in manually after the traffic light formed.

It doesn't seem as if there would be any point to doing that. The nice thing about Herschel conduits (and complex Life patterns in general) is that they take care of themselves, and can often keep functioning indefinitely without any interference -- not just once at T=0, or whenever you draw a structure by hand.

User avatar
biggiemac
Posts: 515
Joined: September 17th, 2014, 12:21 am
Location: California, USA

Re: Catalyst improvements WIP

Post by biggiemac » February 9th, 2015, 2:12 pm

dvgrn wrote:No doubt there are piles of transparent-blinker reactions, of course, but that would limit the circuit to P2 so nobody's been too interested in hunting for them.
A transparent-blinker reaction that had the blinker reappear out of phase could lead to an odd-period reflector. It's still not the most pertinent corner of search space but it could be promising. (Plus I would be quite amused if p34 was the final unobserved period..) There are messy traveling G + blinker reactions like the following that I would be interested in running through a good script, but I don't yet follow how to input them to catalyst/catgl/etc.

Code: Select all

x = 15, y = 5, rule = LifeHistory
2.D9.3C$.2D$2D8.2C$.2D6.C.C$11.C!
Any guidance would be appreciated!
Physics: sophistication from simplicity.

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 9th, 2015, 4:36 pm

biggiemac wrote:There are messy traveling G + blinker reactions like the following that I would be interested in running through a good script, but I don't yet follow how to input them to catalyst/catgl/etc.
Maybe a converter like this could be called a "P2-even" or "P2-odd" reflector/G-to-H, depending on the phase of the restored blinker.

Here's a sample catgl setup that maybe you can modify for your own purposes. If any of this doesn't work on your system, let me know what goes wrong.

If you have Catgl 1.0.3 installed and working -- same instructions as for the previous walkthrough -- then you should be able to paste a pattern like this into Golly, and then run catgl.py:

Code: Select all

x = 35, y = 22, rule = LifeHistory
30.AB$16.A2BA4.A2BA4BA$15.A2BA5.10B$14.4B.2BA.A7BA2B$4.A2.ABABABA17BA
2BA$3.A27BA2B$3.30B$.A25B2A2BA$.27B2A$BA13BA2.2A3.A.2BAB$ABA9BA11.4B$
ABA7BA12.A2BA$.A8B12.4B$.9BA10.A2BA$2.A8B9.4B$3.7BA8.A2BA$3.A5BA8.4B$
4.A4B8.A2BA$6.2A8.4B$15.A2BA$14.4B$13.A2BA!
Side note: I'd like to be able to use a pattern like this instead --

Code: Select all

x = 35, y = 22, rule = LifeHistory
30.2B$16.4B4.9B$15.4B5.10B$14.4B.3B.11B$4.B2.23B3A2B$3.31B$3.25B2A3B$
.26BABA2B$.28BA$BA14B2.2B3.B.4B$ABA10B11.4B$ABA8B12.4B$.A8B12.4B$.10B
10.4B$2.9B9.4B$3.8B8.4B$3.7B8.4B$4.5B8.4B$6.2B8.4B$15.4B$14.4B$13.4B!
-- but catgl doesn't support this kind of thing correctly yet. Cells have to be on for at least one tick in order to forbid catalysts from being placed there -- thus the ugly workaround with sparks lining the envelope.

With this reaction envelope, it starts to be possible to place catalysts around T=30. Really it could be earlier, but early catalysts will probably kill the B-heptomino so the interesting part of the search space is probably about T=35 to T=75. Beyond T=100 the explosion is getting so messy that probably no amount of catalysis will get it settled down.

That's just a rough estimate, and I could be totally wrong -- you can try any search settings that look promising. It's absolutely possible that there's a clean catalysis with a first interaction above T=150 or T=200. For a first round, though, let's start with an upper limit of T=80. What we're looking for is a small reflector, and catgl.exe as currently compiled only works up to T=250 (total, catalyst placement time plus survival time) and a 180x180 search area.

The above pattern has envelope cells marking off the path for a Herschel output for this conduit (blocked off by the beehive on the left). If you just want to find a G->G, you might want to remove the beehive and nearby envelope, allowing catgl to place catalysts in that area as well -- that would be a much larger search space.

Quick review: ideally we want some reasonable number of catalysts, such that a clean glider or a Herschel comes out, and the only junk left behind is a blinker in the original location. But we might be interested in any catalysis that drops a blinker back in the right place, even if there's some other junk: just maybe we'll be able to add more catalysts and remove the other junk.

The hard thing is finding the transparent blinker. That's just the luck of the draw, pretty much, so we'll have to look at a lot of possibilities, and apply some kind of filter to catgl output. There are some basic post-search filtering options built in to catgl.exe, but they mostly haven't found their way into catgl.py yet... work in progress, as the title says.

If you tell catgl it can use just two catalysts, that's a fairly short search -- I got eleven candidates back, of which the likeliest were these:

Code: Select all

x = 123, y = 27, rule = LifeHistory
118.C$117.C.C$117.C.C$116.2C.3C$30.A8.2C81.C$16.A2.A4.A2.A4.A6.C70.A
5.2A.3C$15.A2.A18.C.C56.A2.A4.A2.A4.A3.2A.C$21.A.A7.A5.AC56.A2.A$4.A
2.A.A.A.A17.A2.A66.A.A7.A$3.A27.A52.A2.A.A.A.A17.A2.A$83.A27.A$.A25.
2A2.A5.2A.C$28.2A7.2A.3C38.A25.2A2.A$.A13.A2.2A3.A3.A15.C64.2A$A.A9.A
24.2C.3C38.A13.A2.2A3.A3.A$A.A7.A12.A2.A11.C.C39.A.A9.A$.A36.C.C39.A.
A7.A12.A2.A$10.A10.A2.A14.C41.A$2.A87.A10.A2.A$10.A8.A2.A59.A$3.A5.A
80.A8.A2.A$4.A12.A2.A62.A5.A$6.2A76.A12.A2.A$15.A2.A67.2A$95.A2.A$13.
A2.A$93.A2.A!
That puts a block near the original location instead of a blinker, but the Herschel does escape, and there's lots of room for further catalysts. One of these could be used as the starting point for a new search.

If you tell catgl to place up to three catalysts, you'll get a much larger number of results, most of them duplicates. It doesn't really make sense to look through them all manually. So we need to either adjust catgl.py to sort out any reactions that drop a blinker in the right place, or write a separate script to find the promising candidates.

In the next few weeks I hope to find a little time to get the minimal filtering system in catgl.exe working properly with catgl.py, so that it becomes possible to do longer runs to look for less likely things (like this transparent blinker). And now it's my turn to say that any help would be appreciated -- anything from code assistance, to beta testing, to just occasional reminders that I'm supposed to try to tackle this annoying little problem...!

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » February 9th, 2015, 5:59 pm

Not to the topic: this is great example how LOM is part of edgy interaction and even how LOM get moved.

In this interaction see generation 95, 157 and 179.

Code: Select all

x = 44, y = 22, rule = LifeHistory
30.A8.2C$16.A2.A4.A2.A4.A6.C$15.A2.A18.C.C$21.A.A7.A5.AC$4.A2.A.A.A.A
17.A2.A$3.A27.A2$.A25.2A2.A5.2A.C$28.2A7.2A.3C$.A13.A2.2A3.A3.A15.C$A
.A9.A24.2C.3C$A.A7.A12.A2.A11.C.C$.A36.C.C$10.A10.A2.A14.C$2.A$10.A8.
A2.A$3.A5.A$4.A12.A2.A$6.2A$15.A2.A2$13.A2.A!
LOM reappears three times on the edge - while each later is predecessor of the earlier.

And I know it's not even close to conduit - but you can see the potential and the frequency of LOM appearance in just random smoke.

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

Re: Catalyst improvements WIP

Post by Scorbie » February 9th, 2015, 7:20 pm

simsim314 wrote: this is great example how LOM is part of edgy interaction and even how LOM get moved.
Sorry to be off-topic, but another example that shows potential(I said "potential", not a conduit):

Code: Select all

x = 71, y = 41, rule = B3/S23
2o5b2obo4b2o3b2o28b2o3b2o$bo5bob2o4b2o3b2o27bo2bobo2bo$o10b2o37bobobob
o$2o9bo3b2o3b2o25b3obobobob3o$12bo2bobobobo24bo4bobobo4bo$2o9b2o4bobo
26bob3obobob3obo$bo5b2obo5b2obo30bo5bo$o6bob2o9b2o26bobobobobobo$2o3b
2o14bo25bo4b3o4bo$5bo14bo27b11o$2o4bo13b2o26b2o7b2o$bo3b2o14bo$o6b2obo
9bo43bo$2o5bob2o9b2o42bobo$63bobo$65bo$35bo$35b3o$38bo30b2o$37b2o30b2o
3$45b3o$43b2o2bo$32b2o9bo2b2o16b2o$32b2o9b3o18bo$49bo15b3o$48bobo16bo$
37bo12b2o$37bobo$36bobo$38bo$44bo11bo$43bobo3b3o3bobo$43bob4o3b4obo$
40b2obo13bob2o$40b2ob2o2b2o3b2o2b2ob2o$43bo13bo$43bobo4bo4bobo$44b2o3b
obo3b2o$50bo!

User avatar
biggiemac
Posts: 515
Joined: September 17th, 2014, 12:21 am
Location: California, USA

Re: Catalyst improvements WIP

Post by biggiemac » February 10th, 2015, 12:22 am

Thanks @dvgrn for the help! Of course it wasn't glitch-free, I had to repair my python installation because apparently the tkinter library was corrupted from 32-bit and 64-bit installations clashing; now I am able to use catgl and everything works as you and simsim have described. Looking for that magic blinker now.

One oddity, it reports blocks as surviving when they are converted to hives and ponds..
Physics: sophistication from simplicity.

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 10th, 2015, 8:25 am

biggiemac wrote:One oddity, it reports blocks as surviving when they are converted to hives and ponds..
Yes, there isn't really a good way around that at the moment, due to a limitation in the way-catgl defines success or failure in catalysts. Each catalyst is made up of always-ON cells and can-be-OFF cells. The catalyst is considered to fail as soon as an always-ON cell turns off, whereas it's assumed that a can-be-OFF cell will eventually turn back on when the catalyst returns to its rest state.

As you've discovered, that isn't quite always true. Blocks can get compromised in various ways while still retaining their two always-ON cells. Eaters and tubs-with-tail can get converted into boats-with-tail and various other things, eater2 blocks can be converted to boats, etc.

These mutations happen rarely enough that they're usually easy to weed out manually with the rest of the junk. Here again it probably makes sense to solve this problem with an automated post-search filtering system -- make sure that all added catalysts are still the right shape in the final configuration, and then sort to the top of the list any reactions that produce final ON cells at such-and-such locations, and OFF cells at such-and-such other locations (e.g., the magic blinker).

User avatar
Gustavo6046
Posts: 647
Joined: December 7th, 2013, 6:26 pm
Location: Brazil.

Re: Catalyst improvements WIP

Post by Gustavo6046 » February 12th, 2015, 2:54 pm

Thanks to catalyst research improvement I did discovered one unstable pattern! 'lol' I meant one pattern that after a bunch of generations turn into that "ship-tie-boat with tail" eater, and even sends a glider in the meantime that after another bunch of generations is eaten by the eater (more like a rock) which confirm IT WORKS!!!
lol
unfortunately it needs one unnatural agar to work, which at least dissapear cleanly after the generations.

also try to do squaredance in B3/S23:T50,50 infinite dance! *sic
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

User avatar
Kazyan
Posts: 1247
Joined: February 6th, 2014, 11:02 pm

Re: Catalyst improvements WIP

Post by Kazyan » February 25th, 2015, 12:29 am

catgl.py almost works, but the dialog box doesn't close. When exiting out of it, there's an obvious problem with tkinker: it's trying to open a path using double slashes instead of single slashes. Quickfix?
Tanner Jacobi
Coldlander, a novel, available in paperback and as an ebook. Now on Amazon.

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 25th, 2015, 12:58 am

Kazyan wrote:catgl.py almost works, but the dialog box doesn't close. When exiting out of it, there's an obvious problem with tkinker: it's trying to open a path using double slashes instead of single slashes. Quickfix?
This sounds familiar, but I can't quite think what the problem would be. You put catgl.exe and catgl.py in Scripts/Catgl as described here? And your script folder is set to the default location? If so, what's the exact path quoted by the error message? (and what's the rest of the message)? And is it really possible to create a file on your system with that exact path and filename, disregarding the double backslashes?

Try the same file creation with a separate minimal script, maybe, and make sure it works there. Sometimes double backslashes are just Python's way of representing a backslash in a string -- backslash is an escape character, so backslash+backslash = just one backslash.

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

Re: Catalyst improvements WIP

Post by Scorbie » February 25th, 2015, 10:10 am

I modified catgl.py a little bit so that it uses os.getcwd() instead of absolute path. I'll upload it when I come back home, just in case it helps.

Here:

Code: Select all

#python GUI for Catgl. V0.2 released 5/13/2014 by Mciahel Simkin
#Runs catgl.exe with current golly pattern. Supports catgl V1.0.3
#saves/load the latest settings. 
#creates setup.txt and waits for catgl to finish running. 
#opens rle report from catgl
# - multiproccess is not implemented yet
# os.getcwd() <- os.path.join(g.getdir("scripts"),"Scripts-custom\Catgl")

import golly as g 
import sys, os
from Tkinter import *
from tkFileDialog import askopenfilename
import tkMessageBox
import os.path
import time
import subprocess

class CatglForm(Frame):
   def __init__(self, master):
      Frame.__init__(self, master)
      
      self.master = master
      
      master.wm_attributes("-topmost", 1)
      master.geometry("400x400")
      master.title("Catalyst Search")

      #self.pathlabel = Label(master, height=1, width=100,text="Path to patgl.exe:")
      #self.pathlabel.place(x=-295, y=0)

      #self.fileChooserButton = Button(master, width=2, text ="...", command = self.ChooseCatglFile)
      #self.fileChooserButton.place(x=372, y=14)

      self.filePath = StringVar()
      self.filePath.set(os.path.join(os.getcwd(),"catgl.exe"))
      
      #self.fileEntry = Entry(master, width=58, textvariable=self.filePath)
      #self.fileEntry.place(x=10, y=20)
    
      self.catalystsLabels = ["Eater 1", "Eater 2", "Snake", "Block", "Block and tub with tail", "Tub", "Boat"]
      self.catalystUse = []
      self.lasty = 0
      
      for i in range(len(self.catalystsLabels)):
         self.catalystUse.append(IntVar())
         l = Checkbutton(master, text=self.catalystsLabels[i], variable=self.catalystUse[i])
         self.lasty = 20 * i 
         l.place(x=10, y=self.lasty)
      
      self.lasty += 30
      dx = 280
      
      self.firstgen = StringVar()
      
      temp = Label(master, height=1, width=100,text="First Generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.firstgen)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.gens = StringVar()
      
      temp = Label(master, height=1, width=100,text="Last generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.gens)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.survive = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of generation for catalyst to survive:")
      temp.place(x=-220, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.survive)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.cats = StringVar()
      
      temp = Label(master, height=1, width=100,text="Maximal number of catalyst to place:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.cats)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.threads = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of parallel processes to run:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.threads)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 40

      
      self.startButton = Button(master, width=20, text ="Start", command = self.Start)
      self.startButton.place(x=120, y=self.lasty)
      
      self.Load()
      
    
      self.filePath.set(os.path.join(os.getcwd(),"catgl.exe"))
      
   def ChooseCatglFile(self):
      filename = askopenfilename()
      self.filePath.set(filename)
   
   def Start(self):
   
      if self.filePath.get() == "": 
         tkMessageBox.showinfo("","Please choose path to catgl.exe")
         return
      
      catalystList = ""
      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"
      
      if catalystList == "": 
         tkMessageBox.showinfo("","Please choose at least one catalyst")
         return
      else: 
         catalystList += "0"
         
         
      if not self.firstgen.get().isdigit():
         tkMessageBox.showinfo("","First generation parameter must be integer")
         return
         
         
      if not self.gens.get().isdigit():
         tkMessageBox.showinfo("","Last generation parameter must be integer")
         return
         
      if not self.survive.get().isdigit():
         tkMessageBox.showinfo("","Generations to survive parameter must be integer")
         return
         
      if not self.cats.get().isdigit():
         tkMessageBox.showinfo("","Number of catalyst parameter must be integer")
         return
         
      if not self.threads.get().isdigit():
         tkMessageBox.showinfo("","Number of process to run parameter must be integer")
         return
      

     
      with open(os.path.join(os.getcwd(),"setup.txt"), "w") as text_file:
         text_file.write("reactions.txt\n")
         text_file.write("reactions.rle\n")
         
         rect = g.getrect(); 
         
         for j in xrange(rect[1], rect[1] + rect[3] + 1):
            for i in xrange(rect[0], rect[0] + rect[2] + 1):   
               if g.getcell(i, j) == 0: 
                  text_file.write(".")
               if g.getcell(i, j) == 1: 
                  text_file.write("A")
               if g.getcell(i, j) == 2: 
                  text_file.write("B")
               if g.getcell(i, j) == 3: 
                  text_file.write("C")
               if g.getcell(i, j) == 4: 
                  text_file.write("D")
               
            if j == rect[1] + rect[3]:
               text_file.write("!")
            else:
               text_file.write("\n")
         
         text_file.write("\n")
         text_file.write(catalystList)
         
         text_file.write("\nstartinggen " + self.firstgen.get())
         text_file.write("\ngens " + self.gens.get())
         text_file.write("\ncatsurvive " + self.survive.get())
         text_file.write("\ncatstoplace " + self.cats.get())
         text_file.write("\nmaxfirstgen " + self.gens.get())
         
      self.Save()


      p = subprocess.Popen([self.filePath.get()])
      '''
      while p.poll() is None:
         time.sleep(0.5)
      g.open(os.path.join(os.getcwd(),"reactions.rle"))
      '''
      self.master.quit()
      self.master.destroy()
      g.exit("")

   def Save(self):
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
   
      with open(fname, "w") as text_file:
         
         catalystList = ""
         
         for i in range(len(self.catalystUse)):
            if self.catalystUse[i].get() == 1:
               catalystList += str(i + 1) + "\n"
         
         catalystList += "0"
         text_file.write(catalystList)
         text_file.write("\n" + self.filePath.get())
         text_file.write("\n" + self.firstgen.get())
         text_file.write("\n" + self.gens.get())
         text_file.write("\n" + self.survive.get())
         text_file.write("\n" + self.cats.get())
         text_file.write("\n" + self.threads.get())
         
   def Load(self):
      
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
      
      if not os.path.isfile(fname):
         return
         
      with open(fname, "r") as text_file:
      
         while True: 
            line = text_file.readline()
            
            if line.strip() == "0":
               break
            
            self.catalystUse[int(line) - 1].set(1)
            
         self.filePath.set(text_file.readline().strip())
         self.firstgen.set(text_file.readline().strip())
         self.gens.set(text_file.readline().strip())
         self.survive.set(text_file.readline().strip())
         self.cats.set(text_file.readline().strip())
         self.threads.set(text_file.readline().strip())

if g.getrect() == []: 
	g.exit("Please setup an initial pattern. State 1 for live, state 2 for dead history, state 3 for always live, state 4 for always dead") 
	
root = Tk()
app = CatglForm(root)
root.mainloop()

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » February 25th, 2015, 3:18 pm

If you want me to place the script in the main thread, let dvgrn approve the fix (I honestly forgot how it works). If dvgrn approves - I'll place it in the first post (also probably we need repository for that one - codeholic?).

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 25th, 2015, 4:31 pm

simsim314 wrote:If you want me to place the script in the main thread, let dvgrn approve the fix (I honestly forgot how it works). If dvgrn approves - I'll place it in the first post (also probably we need repository for that one - codeholic?).
I've run a quick test, and the new lines of code seem to work fine. Now the only requirement is that catgl.exe should be in the same folder as catgl.py -- much better than tying it down to a subfolder in the Scripts directory. There are some cases where os.cwd() won't return the right information, but unless we end up calling catgl.py from some other script for some reason, I think this code should work just fine.

I had some significant trouble with indentation in the posted multi-CPU version 0.3 of catgl.py, though -- maybe some kind of conversion problem between tabs and spaces? Am wondering if I fixed the indentation incorrectly, since I'm getting a lockup now at the very end of the processing:

Code: Select all

#python GUI for Catgl. V0.4 released 25 Feb 2015 by Michael Simkin
#Runs catgl.exe with current golly pattern. Supports catgl V1.0.3
#saves/load the latest settings. 
#creates setup.txt and waits for catgl to finish running. 
#opens rle report from catgl
#Support for stated 1-2-3-4 was added. 1 - live, 2 - dead history, 3 - always live, 4 - always dead. 
#Multiprocessing is now implemented
# os.getcwd() <- os.path.join(g.getdir("scripts"),"Scripts\Catgl") (Scorbie)
# fixed indentation errors, possibly incorrectly (dvgrn)

import golly as g 
import sys, os
from Tkinter import *
from tkFileDialog import askopenfilename
import tkMessageBox
import os.path
import time
import subprocess

class CatglForm(Frame):
   def __init__(self, master):
      Frame.__init__(self, master)
      
      self.master = master
      
      master.wm_attributes("-topmost", 1)
      master.geometry("400x400")
      master.title("Catalyst Search")

      #self.pathlabel = Label(master, height=1, width=100,text="Path to patgl.exe:")
      #self.pathlabel.place(x=-295, y=0)

      #self.fileChooserButton = Button(master, width=2, text ="...", command = self.ChooseCatglFile)
      #self.fileChooserButton.place(x=372, y=14)

      self.filePath = StringVar()
      self.filePath.set(os.path.join(os.getcwd(),"catgl.exe"))
      
      #self.fileEntry = Entry(master, width=58, textvariable=self.filePath)
      #self.fileEntry.place(x=10, y=20)
    
      self.catalystsLabels = ["Eater 1", "Eater 2", "Snake", "Block", "Block and tub with tail", "Tub", "Boat"]
      self.catalystUse = []
      self.lasty = 0
      
      for i in range(len(self.catalystsLabels)):
         self.catalystUse.append(IntVar())
         l = Checkbutton(master, text=self.catalystsLabels[i], variable=self.catalystUse[i])
         self.lasty = 20 * i 
         l.place(x=10, y=self.lasty)
      
      self.lasty += 30
      dx = 280
      
      self.firstgen = StringVar()
      
      temp = Label(master, height=1, width=100,text="First Generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.firstgen)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.gens = StringVar()
      
      temp = Label(master, height=1, width=100,text="Last generation to place catalyst:")
      temp.place(x=-252, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.gens)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.survive = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of generation for catalyst to survive:")
      temp.place(x=-220, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.survive)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.cats = StringVar()
      
      temp = Label(master, height=1, width=100,text="Maximal number of catalyst to place:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.cats)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 25

      self.threads = StringVar()
      
      temp = Label(master, height=1, width=100,text="Number of parallel processes to run:")
      temp.place(x=-242, y=self.lasty)
      temp = Entry(master, width=3, textvariable=self.threads)
      temp.place(x=dx, y=self.lasty)
      
      self.lasty += 40

      
      self.startButton = Button(master, width=20, text ="Start", command = self.Start)
      self.startButton.place(x=120, y=self.lasty)
      
      self.Load()

      self.filePath.set(os.path.join(os.getcwd(),"catgl.exe"))
      
   def ChooseCatglFile(self):
      filename = askopenfilename()
      self.filePath.set(filename)
   
   def Start(self):
   
      if self.filePath.get() == "": 
         tkMessageBox.showinfo("","Please choose path to catgl.exe")
         return
      
      catalystList = ""
      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"
      
      if catalystList == "": 
         tkMessageBox.showinfo("","Please choose at least one catalyst")
         return
      else: 
         catalystList += "0"
         
         
      if not self.firstgen.get().isdigit():
         tkMessageBox.showinfo("","First generation parameter must be integer")
         return
         
         
      if not self.gens.get().isdigit():
         tkMessageBox.showinfo("","Last generation parameter must be integer")
         return
         
      if not self.survive.get().isdigit():
         tkMessageBox.showinfo("","Generations to survive parameter must be integer")
         return
         
      if not self.cats.get().isdigit():
         tkMessageBox.showinfo("","Number of catalyst parameter must be integer")
         return
         
      if not self.threads.get().isdigit():
         tkMessageBox.showinfo("","Number of process to run parameter must be integer")
         return
      
      self.SaveForm()
      idx = self.RunProcs()
      self.CombineReactionFiles(idx)
     
      g.open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"))
      
      self.master.quit()
      self.master.destroy()
      g.exit("")
     
   def RunProcs(self):
   
      idx = 0 
      procs = [] 
     
      while int(self.firstgen.get()) < int(self.gens.get()): 
         if len(procs) < int(self.threads.get()): 
            self.SaveSetup(idx, 5)
            procs.append(subprocess.Popen([self.filePath.get()]))
            time.sleep(0.2)
            idx += 1
      
      for p in procs: 
         if not (p.poll() is None):
            procs.remove(p)
            break
            
      while len(procs) > 0: 
     
         for p in procs: 
            if not (p.poll() is None):
               procs.remove(p)
               break
            
      return idx
      
   def CombineReactionFiles(self, idx):
      text = ""
      firstSet = False
      for i in xrange(0, idx):
         first = True
         fname = os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(i) + ".rle")
      
         if os.path.isfile(fname):
            with open(fname, "r") as text_file:
               for line in text_file:
                  if not(first and firstSet): 
                     text += line
                     firstSet = True
                  
                  first = False   
      with open(os.path.join(os.path.dirname(self.filePath.get()),"reactions.rle"), "w") as text_file:
         text_file.write(text)
         

   def SaveSetup(self, idx, genStep):
      
      with open(os.path.join(os.path.dirname(self.filePath.get()),"setup.txt"), "w") as text_file:
     
         if os.path.isfile(os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(idx) + ".rle")):
            os.remove(os.path.join(os.path.dirname(self.filePath.get()),"reactions" + str(idx) + ".rle"))
         
         text_file.write("reactions" + str(idx) + ".txt\n")
         text_file.write("reactions" + str(idx) + ".rle\n")
         
         rect = g.getrect(); 
         
         for j in xrange(rect[1], rect[1] + rect[3] + 1):
            for i in xrange(rect[0], rect[0] + rect[2] + 1):   
               if g.getcell(i, j) == 0: 
                  text_file.write(".")
               if g.getcell(i, j) == 1: 
                  text_file.write("A")
               if g.getcell(i, j) == 2: 
                  text_file.write("B")
               if g.getcell(i, j) == 3: 
                  text_file.write("C")
               if g.getcell(i, j) == 4: 
                  text_file.write("D")
               
            if j == rect[1] + rect[3]:
               text_file.write("!")
            else:
               text_file.write("\n")
         
         text_file.write("\n")
         catalystList = self.CatalystList()
         text_file.write(catalystList)
         
         nextF = int(self.firstgen.get()) + genStep
       
         text_file.write("\nstartinggen " + self.firstgen.get())
         text_file.write("\ngens " + self.gens.get())
         text_file.write("\ncatsurvive " + self.survive.get())
         text_file.write("\ncatstoplace " + self.cats.get())
         text_file.write("\nmaxfirstgen " + str(nextF - 1))
         
       
         self.firstgen.set(str(nextF))
      
         
   def CatalystList(self):
      catalystList = ""

      for i in range(len(self.catalystUse)):
         if self.catalystUse[i].get() == 1:
            catalystList += str(i + 1) + "\n"

      catalystList += "0"
   
      return catalystList
   
   def SaveForm(self):
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
   
      with open(fname, "w") as text_file:
         
         catalystList = self.CatalystList()
         text_file.write(catalystList)
         text_file.write("\n" + self.filePath.get())
         text_file.write("\n" + self.firstgen.get())
         text_file.write("\n" + self.gens.get())
         text_file.write("\n" + self.survive.get())
         text_file.write("\n" + self.cats.get())
         text_file.write("\n" + self.threads.get())
         
   def Load(self):
      
      fname = os.path.join(g.getdir("data"),"catglsetup.txt")
      
      if not os.path.isfile(fname):
         return
         
      with open(fname, "r") as text_file:
      
         while True: 
            line = text_file.readline()
            
            if line.strip() == "0":
               break
            
            self.catalystUse[int(line) - 1].set(1)
            
         self.filePath.set(text_file.readline().strip())
         self.firstgen.set(text_file.readline().strip())
         self.gens.set(text_file.readline().strip())
         self.survive.set(text_file.readline().strip())
         self.cats.set(text_file.readline().strip())
         self.threads.set(text_file.readline().strip())

if g.getrect() == []: 
   g.exit("Please set up an initial pattern:  state 1 for live, state 2 for dead history, state 3 for always live, state 4 for always dead") 
   
root = Tk()
app = CatglForm(root)
root.mainloop()
Also, while I'm thinking of it, toward the end of the actual catgl 1.0.3 C code, the RLE is currently getting printed with reversed colors:

Code: Select all

            //Fix this:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "A");
                else fprintf(f, "C");
            else if (fixed[x][y]) fprintf(f, ".");
            else fprintf(f, ".");
It would be more useful if the output pattern states matched the input pattern states:

Code: Select all

           //Fixed this (I think) -- DMG 25 Feb 2015:
            if (boardgen0[x][y])
                if (fixed[x][y]) fprintf(f, "C");
                else fprintf(f, "A");
            else if (fixed[x][y]) fprintf(f, "D");
            else fprintf(f, ".");
Probably ought to add in the "B" states, too, but I don't have time to look into this today -- and anyway including "B" isn't quite a perfect solution: a few catalysts will fit in a subsequent search in places where they should be forbidden.

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

Re: Catalyst improvements WIP

Post by Scorbie » February 25th, 2015, 11:33 pm

Just two things:
1) In my computer, Golly freezes every time catgl.py waits for catgl.exe (and even if catgl.exe is finished) can it be solved?
2) I use this script and saw that this is handy in several situations.
convert-catglout-to-catglin.py

Code: Select all

import golly as g

if g.getrule() == "B3/S23":
    g.setrule("LifeHistory")

r=g.getselrect()
if len(r)==0:
  r=g.getrect()
  if len(r)==0: g.exit("No pattern, nothing to do.")
sel = g.getcells(r)
if len(sel)==0: g.exit("Nothing in selection.")

for y in xrange(r[1],r[1]+r[3]):
	for x in xrange(r[0],r[0]+r[2]):
		if g.getcell(x,y) ==0: continue;
		elif g.getcell(x,y)==2: g.setcell(x,y,0)
		elif g.getcell(x,y) in [3,5]: g.setcell(x,y,1)
		elif g.getcell(x,y)==1:g.setcell(x,y,3)
EDIT: Just one more thing. I wish if the catalyst survive constraint changed
from
survives for 30 gens
to
survives till 30 gens after interaction with last catalyst.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » February 26th, 2015, 12:49 pm

I just added a link in the first message to dvgrn message with V0.4

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » February 27th, 2015, 10:14 am

simsim314 wrote:I just added a link in the first message to dvgrn message with V0.4
Can you also have a quick look at your posted code for catgl.py version 0.3, which I'm guessing ran fine on your system? Quite a few lines have illegal indentations; see for example the while statement on line 166 -- the following block isn't indented correctly. If this is a tab conversion problem, it seems to have happened before the code was posted.

In most cases the fix is very obvious, but occasionally it takes a close reading of the code to figure out how long the indented block needs to be -- and I didn't do a close reading! Very likely the lockup problem that Scorbie and I are seeing is caused by an incorrect patch to the v0.3 code.

-- And very likely I could find my mistake with enough re-reading. But I'm hoping there's a clean version of the multi-CPU v0.3 code out there that I can start from instead...?

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » March 1st, 2015, 4:44 pm

dvgrn wrote:But I'm hoping there's a clean version of the multi-CPU v0.3 code out there that I can start from instead...?
Unfortunately there is not :(

I can post here some comments of what I remember about this piece of code you mentioned but I really need to start working on it in full power to make sure it works again:

Code: Select all



      idx = 0 
      procs = [] 
	  
	  #The main idea is to go in steps of 5 gens - and search in separate processes
	  #from start gen=idx to end gen = idx + 5. Continue doing so until there will remain 
	  #no generations left. 
      while int(self.firstgen.get()) < int(self.gens.get()):
	  
      if len(procs) < int(self.threads.get()): 
		 
		 #this save setup file and increses the current firstgen by 5 
         self.SaveSetup(idx, 5)
         procs.append(subprocess.Popen([self.filePath.get()]))
		 #The sleep is needed to make sure the process has started and 
		 #The file can be modified for the next process. 
         time.sleep(0.2)
		 #if the first process works on gens 1-5 the next process will work on 6-10. 
         idx += 1
      
	  for p in procs: 
         if not (p.poll() is None):
            procs.remove(p)
            break
      
	 
	  #The procs list is needed to make sure every process has finished before we exit the script
	  #Not sure why this loop is here... 
      while len(procs) > 0: 
     
      for p in procs: 
         if not (p.poll() is None):
            procs.remove(p)
            break
            
      return idx
      

User avatar
dvgrn
Moderator
Posts: 10669
Joined: May 17th, 2009, 11:00 pm
Location: Madison, WI
Contact:

Re: Catalyst improvements WIP

Post by dvgrn » March 1st, 2015, 5:32 pm

simsim314 wrote:I can post here some comments of what I remember about this piece of code you mentioned ...
Interesting! Looking at it all again, it seems to me that the multi-CPU option doesn't quite divide things up properly -- you end up with only groups of catalysts that react within a few ticks of each other -- so effectively you're throwing away solutions where one catalyst reacts at T=1 and the other at T=25. (?)

I'm not getting the lockup on the single-CPU version, so it makes sense to me to go back to that for now, and concentrate on simplifying the pipeline so that the output of one search can trivially become the input of the next search. It's certainly still possible to use multiple CPUs to explore multiple branches of a tree simultaneously -- it just takes some manual inspection to decide which branches of the tree to try.

So far I've found it most effective to add just one or two catalysts at a time, but of course that also tends to cut off branches of the search. The results end up being biased toward patterns that settle into stable junk instead of exploding and endangering the catalysts. In the early stages of the search, a more explosive reaction might be better, as long as it travels reasonably well -- more places to try placing catalysts to settle it down.

From some trial runs recently, it does look as if a good filtering system would make it worthwhile to run a lot of speculative three-catalyst searches, anyway. Probably still wouldn't be effective to search with four catalysts, though.

User avatar
simsim314
Posts: 1823
Joined: February 10th, 2014, 1:27 pm

Re: Catalyst improvements WIP

Post by simsim314 » March 2nd, 2015, 1:41 am

dvgrn wrote: so effectively you're throwing away solutions where one catalyst reacts at T=1 and the other at T=25. (?)
I don't think so. I think I only increase the start gen then. I think I've added special flag to the c code, that says the first catalyst should be activated in range (x, y). This allowed me to divide the search in ranges of the first activation gen.

---

Yes there is definitely an issue to make second iteration. I really hope to get back to it some day, and see what can be improved (having second thoughts of writing some super catalyst search engine combining all the known approaches into single search utility - anyway I'm currently want to fix bellman bug, which is annoyingly not as simple as it looks).

User avatar
Gustavo6046
Posts: 647
Joined: December 7th, 2013, 6:26 pm
Location: Brazil.

Re: Catalyst improvements WIP

Post by Gustavo6046 » March 4th, 2015, 5:07 pm

Can a mango be a catalyst? When with two tubs in the sides?
PS: Which is Scorbie's oscillator?
*yawn* What a nothing-to-do day! Let's be the only person in the world to do CGOL during boring times. :)

Post Reply