Geneascopy
Posted: March 17th, 2019, 5:32 pm
[UPDATED SCRIPT AT THE BOTTOM OF THIS POST]
geneasco.py
I had been toying around with the idea, inspired by NMR, to probe "stuff" (I will expand on this) in CA in a systematic manner that can yield useful results. I was surprised there has not been much study in the dynamics of rules in general and CGoL in particular starting from random configurations, and I have devised a script that can allow both this and, in the future, probing specific pattern families.
What is geneasco.py?
It is a Python script that runs ns 16x16 soups in any rule for kmax generations, recording generation by generation their population AND their population change (the average is given by the results divided by ns, obviously).
The idea behind this is that random soups are not random in their evolution and, on top of significant background noise, can show surprisingly important trends in population and population change when averaged across enough examples.
What information this gives us: Population
I greatly suggest you run the script for CGoL with ns=2000 and kmax=2000 (should be fairly quick, less than half a minute) and plot the resulting cumulative population against generation in Excel. The resulting plot is what seems to be a simple Morse potential-like function. The parameters for that Morse potential-like function will be very interesting to understand, as it will give insight into the dynamics of the rule/soup size/soup density/etc. You will see surprisingly well-defined humps within that general function though, and I think we can safely call this fine-structure, which I think has vital information about the rule's dynamics (think X-ray crystallography in chemistry - the fine detail within the diffraction points gives info on the atom positions in the unit cell).
Unexpectedly, if you run it for 5000 generations especially for loads of scans (40000), you see the population falling again after a peak at about 2100. Interestingly, if you zoom in enough with these many scans, you see a VERY well-defined fine structure!* Which brings us to this. That "fine-structure" I have found to differ extremely significantly between relatively closely-related rules. I think further studying this will be extremely interesting.
What information this gives us: Population Change
Population change converges apparently oscillating around 0 (actually, around the expected end heat of a soup).
Plotting ln(change^2)/2 vs generation shows something unique: after a given generation threshold, the behaviour shows a slow decrease with a lot of noise mostly under a maximum y=mx+c, related to the chaotic heat of the rule. However, before this generation threshold, in the case of CGoL being around 160 generations, the curve followed is not linear and very precise, behaving in an entirely different manner. The complexity of the system seems to be defined by generation 160-ish for CGoL, before which the evolution of most soups has strikingly similar features (in fact, the dip at about 28 ticks is actually where you are most likely to find B-heptominos, r-pentominos, pis etc!). *NOTE: I upload the raw results from the 5000-generation 40k scans as a text file. Future work:
Discrete Fourier transforms will be HUGELY useful for the fine structure (not the Morse potential-like function which is already easily analysed). For some reason, the frequency domain always shows more processable data. I will try to find out how to do this.
geneasco.py
I had been toying around with the idea, inspired by NMR, to probe "stuff" (I will expand on this) in CA in a systematic manner that can yield useful results. I was surprised there has not been much study in the dynamics of rules in general and CGoL in particular starting from random configurations, and I have devised a script that can allow both this and, in the future, probing specific pattern families.
What is geneasco.py?
It is a Python script that runs ns 16x16 soups in any rule for kmax generations, recording generation by generation their population AND their population change (the average is given by the results divided by ns, obviously).
The idea behind this is that random soups are not random in their evolution and, on top of significant background noise, can show surprisingly important trends in population and population change when averaged across enough examples.
What information this gives us: Population
I greatly suggest you run the script for CGoL with ns=2000 and kmax=2000 (should be fairly quick, less than half a minute) and plot the resulting cumulative population against generation in Excel. The resulting plot is what seems to be a simple Morse potential-like function. The parameters for that Morse potential-like function will be very interesting to understand, as it will give insight into the dynamics of the rule/soup size/soup density/etc. You will see surprisingly well-defined humps within that general function though, and I think we can safely call this fine-structure, which I think has vital information about the rule's dynamics (think X-ray crystallography in chemistry - the fine detail within the diffraction points gives info on the atom positions in the unit cell).
Unexpectedly, if you run it for 5000 generations especially for loads of scans (40000), you see the population falling again after a peak at about 2100. Interestingly, if you zoom in enough with these many scans, you see a VERY well-defined fine structure!* Which brings us to this. That "fine-structure" I have found to differ extremely significantly between relatively closely-related rules. I think further studying this will be extremely interesting.
What information this gives us: Population Change
Population change converges apparently oscillating around 0 (actually, around the expected end heat of a soup).
Plotting ln(change^2)/2 vs generation shows something unique: after a given generation threshold, the behaviour shows a slow decrease with a lot of noise mostly under a maximum y=mx+c, related to the chaotic heat of the rule. However, before this generation threshold, in the case of CGoL being around 160 generations, the curve followed is not linear and very precise, behaving in an entirely different manner. The complexity of the system seems to be defined by generation 160-ish for CGoL, before which the evolution of most soups has strikingly similar features (in fact, the dip at about 28 ticks is actually where you are most likely to find B-heptominos, r-pentominos, pis etc!). *NOTE: I upload the raw results from the 5000-generation 40k scans as a text file. Future work:
Discrete Fourier transforms will be HUGELY useful for the fine structure (not the Morse potential-like function which is already easily analysed). For some reason, the frequency domain always shows more processable data. I will try to find out how to do this.
Code: Select all
import golly as g
class soup:
def __init__(self,kmax):
self.pop=[0]*kmax #Population of pattern
self.dpop=[0]*kmax #Change of population wrt previous generation
kmax=int(g.getstring("Maximum number of generations:","2000"))
ns=int(g.getstring("Number of scans:","20000"))
alpha=soup(kmax)
k=0
g.show("Press q to stop at any point.")
for j in xrange(ns):
g.new("Spectrum_scan_"+str(j))
g.select([0,0,16,16]) # These two lines can be substituted to analyse different patterns
g.randfill(30)
temp_pop=int(g.getpop())
alpha.pop[0]+=temp_pop
alpha.dpop[0]=0 # IMPORTANT: Ignore first data point for FT
for i in xrange(kmax-1):
g.run(1)
alpha.pop[i+1]+=int(g.getpop())
alpha.dpop[i+1]+=int(g.getpop())-temp_pop
temp_pop=int(g.getpop())
try:
g.select(g.getselrect())
g.clear(0)
except: pass
event=g.getevent()
if event.startswith("key q none"):
ns=j
break
File=open("geneascopy_ns_%d.txt"%ns,"w+")
File.write("Gen\tPop\tdPop\tln(dPop^2)/2\tln((Pop-minpop)+1)\tabs(dPop)\t\tNS=%d\n"%ns)
minPop=min(alpha.pop)
for m in xrange(kmax):
File.write(str(m)+"\t"+str(alpha.pop[m])+"\t"+str(alpha.dpop[m])+"\t=ln(C%d^2)/2\t"%(m+2)+"=ln(B%d-"%(m+2)+"%d+1)\t"%minPop+"=abs(C%d)\n"%(m+2))
#
File.write("\nPop{")
for n in xrange(kmax):
File.write("%f,"%(float(alpha.pop[n])/float(ns)))
File.write("}\n\ndPop{")
for op in xrange(kmax):
File.write("%f,"%(float(alpha.dpop[op])/float(ns)))
File.write("}")
#
File.close()
g.exit()