A (one dimensional) cellular automaton is a function1 F : Σ → Σ with the property that there is a K > 0 such that F (x)i depends only on the 2K + 1 coordinates xi−K , xi−K+1, . . . , xi−1, xi, xi+1, . . . , xi+K . A periodic point of σ is any x such that σ^p (x) = x for some p ∈ N, and a periodic point of F is any x such that F^q (x) = x for some q ∈ N. Given a cellular automaton F, a point x ∈ Σ is jointly periodic if there are p, q ∈ N such that σ^p (x) = F^q (x) = x, that is, it is a periodic point under both functions.
This project aims to explore the nature of one-dimensional Cellular Automata, in the hope of finding the structure of cellular automata through its periodic points.
License: MIT
ubuntu2004
/*1* Copyright (C) 2004 Bryant Lee2*3* This file is part of FProbPeriod.4*5* FProbPeriod is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* FProbPeriod is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with FProbPeriod; if not, write to the Free Software17* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA18*/1920/* This is the main function of the FProbPeriod program21* FProbPeriod program samples points over N-shift that are shift periodic22* with period k and finds period and preperiod. The result gives some23* indication of what is the average period of all points of shift period k24* over the N-shift.25*26* Written by: Bryant Lee27* Date: 9/01/0528*/2930#define USEARRAYS 031#define DEBUG 03233//Standard C++34#include <string.h>35#include <iostream>36#include <iomanip>37#include <fstream> //for file ops3839//C40#include <stdlib.h> //for atoi41#include <math.h> //for pow, and ceil42#include <time.h> //to get seed for random numbers4344//STL45#include <vector>46#include <list>47#include <map>4849//local50#include "StringOps.h"51#include "Comp.h"52#include "StorageKey.h"53#include "StorageVal.h"54#include "RecNode.h"55#include "StorageKeyUInt.h"5657//mersenne twister, pseudo-random number generator58#include "MTRand.h"5960//function definitions61void convertPeriodList(const string &strInput, vector<int> &periodList);62void probExp(unsigned int shiftSize, const vector<int> &periodList,63Comp *comp, int numPoints, list<RecNode*> &recList,64string &prefix, vector<string> &commandbuffer);65void probExpTwoShift(const vector<int> &periodList,66Comp *comp, int numPoints, list<RecNode*> &recList,67string &prefix, vector<string> &commandbuffer);68void clearStorageNodes(vector<StorageVal*> &table);69void clearRecList(list<RecNode*> &recList);70void output(unsigned int shiftSize, int numPoints, string &prefix,71const vector<int> &periodList, Comp * comp,72list<RecNode*> &recList, vector<string> &commandbuffer);73void findDivisors(unsigned int x, vector<int> &divisorsList);74bool leastPeriod(byte *word, unsigned int currPeriod,75const vector<int> &divisorsList);76bool leastPeriod(unsigned int word, unsigned int currPeriod,77const vector<int> &divisorsList);7879//GLOBAL8081//this debug variable allows testing the "special case 2-shift" with82//arrays of unsigned ints in which each unsigned int carries UINTSIZE83//bits of info (each bit representing 1 coordinate). Values up to 32 allowed.84int UINTSIZE = 32;8586//boolean that determines whether output is given after each completed experiment (after each K)87//or only at end88bool OUTPUT_AFTER_EACH = false;8990//main91int main() {92unsigned int shiftSize; //# symbols in shift93string prefix; //prefix for output files94string strInput, strInput2, strInput3; //temp variables95vector<int> periodList; //list of periods96unsigned int i = 0, temp; //counter97list<RecNode*> recList; //list of records98list<RecNode*> leastRecList; //list of records for points of least period k99Comp *comp = NULL; //composition to apply100101int numPoints;102string outoption;103104//hold the command strings105vector<string> commandbuffer;106int commandbufferit;107108//grab all input from cin and put in an array of strings109while(!cin.eof()) {110std::getline(cin, strInput);111commandbuffer.push_back(strInput);112}113114commandbufferit = 0;115116//get "output after each" or "output at end"117outoption = commandbuffer[commandbufferit++];118trim(outoption);119transform(outoption.begin(), outoption.end(), outoption.begin(), tolower);120121if(outoption == "output after each")122OUTPUT_AFTER_EACH = true;123else if(outoption == "output at end")124OUTPUT_AFTER_EACH = false;125else {126cout << "Error on line 1: invalid value for output mode\n";127goto cleanup;128}129130//get filename prefix131prefix = commandbuffer[commandbufferit++];132trim(prefix);133134//get N, the number of symbols in the full shift135strInput = commandbuffer[commandbufferit++];136trim(strInput);137shiftSize = atoi(strInput.c_str());138139//get set of K, the periodicities to be tested140strInput = commandbuffer[commandbufferit++];141convertPeriodList(strInput, periodList);142143//get m, the number of sample points144strInput = commandbuffer[commandbufferit++];145trim(strInput);146numPoints = atoi(strInput.c_str());147148//Allocate composition object149comp = new Comp(shiftSize);150151//get "Begin Definitions"152strInput = commandbuffer[commandbufferit++];153154//make it lowercase155trim(strInput);156transform(strInput.begin(), strInput.end(), strInput.begin(), tolower);157158if(strInput.compare("begin definitions") != 0) {159cout << "ERROR: Expecting \"BEGIN DEFINITIONS\"\n";160goto cleanup;161}162163//read definitions164while(1) {165strInput = commandbuffer[commandbufferit++];166167//strInput2 is lowercased and trimmed168strInput2.resize(strInput.length());169transform(strInput.begin(), strInput.end(), strInput2.begin(), tolower);170trim(strInput2);171172//strInput3 is lowercased and NOT TRIMMED173strInput3.resize(strInput.length());174transform(strInput.begin(), strInput.end(), strInput3.begin(), tolower);175176if(strInput2 == "" || (strInput2[0] == '/' && strInput2[1] == '/')) {177//ignore blank lines and allow comment out178}179else if(strInput2 == "begin commands") {180break; //BREAK181}182else {183i = 0;184185while(i < strInput.length()) {186if(strInput[i] == '=') {187break;188}189190i++;191}192193if(strInput2[0] == 'o' && strInput2[1] == 'p' && strInput2[2] == 't') {194temp = strInput3.find("opt");195temp += 3;196197comp->addFunc(strInput.substr(i+1, strInput.length() - (i+1)),198strInput.substr(temp, i-temp), true);199}200else {201comp->addFunc(strInput.substr(i+1, strInput.length() - (i+1)),202strInput.substr(0,i), false);203}204}205}206207//read commands208while(commandbufferit < (int)commandbuffer.size()) { //check for EOF209strInput = commandbuffer[commandbufferit++];210trim(strInput);211212//ignore blank lines and allow comment out with "//"213if(strInput != "" && !(strInput[0] == '/' && strInput[1] == '/')) {214215//set composition216if(!comp->setComposition(strInput)) {217cout << "ERROR: Command \"" << strInput218<< "\" references undefined function\n";219goto cleanup;220}221222//do experiment223if(shiftSize == 2 && sizeof(unsigned int) == 4 && !USEARRAYS)224probExpTwoShift(periodList, comp, numPoints, recList, prefix,225commandbuffer);226else227probExp(shiftSize, periodList, comp, numPoints, recList, prefix,228commandbuffer);229230if(!OUTPUT_AFTER_EACH)231output(shiftSize, numPoints, prefix, periodList, comp, recList,232commandbuffer);233234//clear records (delete the nodes with ptrs in lists)235clearRecList(recList);236clearRecList(leastRecList);237recList.clear();238leastRecList.clear();239}240}241242//cleanup and exit243cleanup:244//delete the nodes with ptrs in lists245clearRecList(recList);246clearRecList(leastRecList);247248//delete composition (and the functions in the Comp object's storage)249delete comp;250251return 0;252}253254255/*256* Full output257*/258259void output(unsigned int shiftSize, int numPoints, string &prefix,260const vector<int> &periodList, Comp * comp, list<RecNode*> &recList,261vector<string> &commandbuffer) {262list<RecNode*>::const_iterator it;263264unsigned int commandbufferit = 0;265266if(DEBUG)267cout << "Output\n";268269//output270ofstream fout;271string fname = prefix + comp->returnName() + ".txt";272fout.open(fname.c_str(), ios::trunc | ios::out);273274if(!fout.is_open()) {275cout << "ERROR: could not create file " << fname << "\n";276return;277}278279//HEADER280fout << "FProbPeriod 3.67\n";281282/*283fout << "N = " << shiftSize << "\n";284fout << "K = ";285for(i = 0; i < periodList.size(); i++) {286fout << periodList[i];287if(i != periodList.size() - 1)288fout << ",";289}290fout << "\n";291fout << "m = " << numPoints << "\n";292*/293294fout << "\n";295296//copy of the input297fout << "INPUT\n";298fout << "--------------------\n";299for(commandbufferit = 0; commandbufferit < commandbuffer.size(); commandbufferit++) {300string test;301test = commandbuffer[commandbufferit];302trim(test);303if(test != "") {304fout << commandbuffer[commandbufferit] << "\n";305}306}307fout << "--------------------\n";308fout << "\n";309310//file for preperiods311ofstream fout_preperiod;312string fname_preperiod = prefix + comp->returnName() +313"_preperiods.txt";314fout_preperiod.open(fname_preperiod.c_str(), ios::trunc | ios::out);315316if(!fout_preperiod.is_open()) {317cout << "ERROR: could not create file " << fname_preperiod << "\n";318return;319}320321//HEADER322fout_preperiod << "FProbPeriod 3.67\n";323/*324fout_preperiod << "N = " << shiftSize << "\n";325fout_preperiod << "K = ";326for(i = 0; i < periodList.size(); i++) {327fout_preperiod << periodList[i];328if(i != periodList.size() - 1)329fout_preperiod << ",";330}331fout_preperiod << "\n";332fout_preperiod << "m = " << numPoints << "\n";333*/334335fout_preperiod << "\n";336337//copy of the input338fout_preperiod << "INPUT\n";339fout_preperiod << "--------------------\n";340for(commandbufferit = 0; commandbufferit < commandbuffer.size(); commandbufferit++) {341string test;342test = commandbuffer[commandbufferit];343trim(test);344if(test != "") {345fout_preperiod << commandbuffer[commandbufferit] << "\n";346}347}348fout_preperiod << "--------------------\n";349fout_preperiod << "\n";350351/* Output file */352353//fill on right side, show in fixed notation354fout << setiosflags(ios::left | ios::fixed);355356fout << "COMMAND:\n";357fout << comp->returnName() << "\n";358fout << "WITH DEFINITIONS:\n";359comp->printDefinitions(fout);360fout << "\n";361362/* Preperiod file */363364//fill on right side, show in fixed notation365fout_preperiod << setiosflags(ios::left | ios::fixed);366367fout_preperiod << "Preperiod output\n\n";368369fout_preperiod << "COMMAND:\n";370fout_preperiod << comp->returnName() << "\n";371fout_preperiod << "WITH DEFINITIONS:\n";372comp->printDefinitions(fout_preperiod);373fout_preperiod << "\n";374375/* output file */376fout << "PERIOD K (NOT LEAST)\n\n";377378/* preperiod file */379fout_preperiod << "PERIOD K (NOT LEAST)\n\n";380381/* output file */382//Summary info383384fout << "K (AvgPd)^(1/K) (MaxPd)^(1/K) (AvgPrepd)^(1/K) (MaxPrepd)^(1/K)\n";385386for(it = recList.begin(); it != recList.end(); it++) {387fout << setw(3) << (*it)->currPeriod << " ";388389if((*it)->pending) {390fout << "PENDING" << "\n";391}392else {393fout << setw(13) << setprecision(4) <<394pow((*it)->avgPeriod(),((double)1/((double)(*it)->currPeriod))) << " ";395fout << setw(13) << setprecision(4) <<396pow((*it)->maxPeriod(),((double)1/(double)(*it)->currPeriod)) << " ";397fout << setw(16) << setprecision(4) <<398pow((*it)->avgPreperiod(),((double)1/((double)(*it)->currPeriod))) << " ";399fout << setw(16) << setprecision(4) <<400pow((*it)->maxPreperiod(), ((double)1/((double)(*it)->currPeriod))) <<"\n";401}402}403fout <<"\n";404405fout << "K AvgPd MaxPd AvgPrepd MaxPrepd\n";406407for(it = recList.begin(); it != recList.end(); it++) {408fout << setw(3) << (*it)->currPeriod << " ";409410if((*it)->pending) {411fout << "PENDING" << "\n";412}413else {414fout << setw(13) << setprecision(2) << (*it)->avgPeriod() << " ";415fout << setw(13) << setprecision(2) << (*it)->maxPeriod() << " ";416fout << setw(16) << setprecision(2) << (*it)->avgPreperiod() << " ";417fout << setw(16) << setprecision(2) << (*it)->maxPreperiod() <<"\n";418}419}420421//orbit and period multiplicity422fout << "\n";423fout << "K Period Mult(#points)\n";424for(it = recList.begin(); it != recList.end(); it++) {425fout << setw(3) << (*it)->currPeriod << " ";426427if((*it)->pending) {428fout << "PENDING" << "\n";429}430else {431map<unsigned long long, unsigned long long>::const_iterator pitpoint;432bool pfirst = true;433434//note that the period and orbit maps have the same periods in the same orders435// so we can iterate through both at once436for(pitpoint = (*it)->periodMap.begin();437pitpoint != (*it)->periodMap.end(); pitpoint++) {438if(!pfirst) {439fout << " ";440}441else {442pfirst = false;443}444445fout << setw(11) << pitpoint->first << " ";446fout << setw(13) << pitpoint->second << "\n"; //points447}448}449}450451/* Preperiod file */452453//Preperiod multiplicity454fout_preperiod << "K Preperiod Mult\n";455for(it = recList.begin(); it != recList.end(); it++) {456fout_preperiod << setw(3) << (*it)->currPeriod << " ";457458if((*it)->pending) {459fout_preperiod << "PENDING" << "\n";460}461else {462map<unsigned long long, unsigned long long>::const_iterator pit;463bool pfirst = true;464465for(pit = (*it)->preperiodMap.begin(); pit != (*it)->preperiodMap.end();466pit++) {467if(!pfirst) {468fout_preperiod << " ";469}470else {471pfirst = false;472}473474fout_preperiod << setw(11) << pit->first << " ";475fout_preperiod << setw(13) << pit->second << "\n";476}477}478}479480//DONE481fout_preperiod.close();482fout.close();483}484485void probExp(unsigned int shiftSize, const vector<int> &periodList,486Comp *comp, int numPoints, list<RecNode*> &recList,487string &prefix, vector<string> &commandbuffer) {488unsigned int i = 0; //k = 0; //counters489unsigned long long j;490unsigned int currPeriod;491int point_it;492493map<StorageKey,unsigned long long> tree; //records points that we have seen494495list<RecNode*>::const_iterator recIterator;496497//initialize random number generator498MTRand random;499500if(DEBUG)501cout << "Arrays\n";502503if(DEBUG)504random.seed(2005); //hand seed505506//make a list of recNodes, one recNode per K value507for(i =0; i < periodList.size(); i++) {508recList.push_back(new RecNode(periodList[i], numPoints));509}510511//initialize recIterator512recIterator = recList.begin();513514//do for every K value515for(i = 0; i < periodList.size(); i++) {516currPeriod = periodList[i];517518//record keeping519RecNode *record = *recIterator;520521//start the sampled test522for(point_it = 0; point_it < numPoints; point_it++) {523byte cWord[currPeriod]; //curr word524byte output[currPeriod]; //helper word525unsigned long long period = 0, preperiod = 0;526map<StorageKey,unsigned long long>::iterator rt;527528tree.clear(); //clear the tree529530//Set current word to random point531for(j = 0; j < currPeriod; j++) {532cWord[j] = random.randInt(shiftSize - 1);533}534535//insert the first word into tree536StorageKey sk1 = StorageKey(cWord, currPeriod);537tree.insert(pair<StorageKey,unsigned long long>(sk1,0));538539//DEBUG540if(DEBUG >= 5) {541cout << "\n\n-- Start orbit --\n";542printArray(cWord, currPeriod);543}544545//Perform experiment546j = 1;547while(true) {548comp->image(cWord, currPeriod, output);549copyWord(cWord, output, currPeriod);550551//DEBUG552if(DEBUG >= 5)553printArray(cWord, currPeriod);554555StorageKey sk2 = StorageKey(cWord, currPeriod);556557rt = tree.find(sk2);558if(rt != tree.end()) {559break;560}561else {562tree.insert(pair<StorageKey,unsigned long long>(sk2,j));563}564565j++;566}567568period = j - rt->second;569preperiod = rt->second;570571record->recordPeriod(period, preperiod);572}573574record->pending = false;575576//advance recIterator to next in list577recIterator++;578579if(OUTPUT_AFTER_EACH) {580output(shiftSize, numPoints, prefix, periodList, comp, recList,581commandbuffer);582}583}584}585586void probExpTwoShift(const vector<int> &periodList,587Comp *comp, int numPoints, list<RecNode*> &recList,588string &prefix, vector<string> &commandbuffer) {589unsigned int i = 0;590unsigned long long j = 0; //k = 0; //counters591unsigned int currPeriod;592int point_it;593594map<StorageKeyUInt, unsigned long long> tree;595596list<RecNode*>::const_iterator recIterator;597598MTRand random;599600if(DEBUG)601random.seed(2005); //hand seed602603if(DEBUG)604cout << "Special case 2-shift\n";605606for(i = 0; i < periodList.size(); i++) {607recList.push_back(new RecNode(periodList[i], numPoints));608}609610recIterator = recList.begin();611612for(i = 0; i < periodList.size(); i++) {613unsigned int blocks;614currPeriod = periodList[i];615616blocks = (int) ceil( ((double)currPeriod) / ((double)UINTSIZE) );617618if(DEBUG) {619cout << "K = " << currPeriod << " blocks = " << blocks << "\n";620}621622RecNode *record = *recIterator;623624for(point_it = 0; point_it < numPoints; point_it++) {625626unsigned int cWord[blocks]; //curr word627unsigned int output[blocks]; //helper word628unsigned long long period = 0, preperiod = 0;629map<StorageKeyUInt,unsigned long long>::iterator rt;630631//clear the tree632tree.clear();633634//set cWord to a random word635//NOTE: it is very important to clear the array first or636//else you may start out with junk637for(j = 0; j < blocks; j++) {638cWord[j] = 0;639}640641int blocki = 0, offseti = 0;642for(j = 0; j < currPeriod; j++) {643cWord[blocki] |= (random.randInt(1) << (offseti));644645offseti++;646if(offseti >= UINTSIZE) {647blocki++;648offseti -= UINTSIZE;649}650}651652tree.insert(pair<StorageKeyUInt, unsigned long long>(StorageKeyUInt(cWord, blocks), 0));653654//DEBUG655if(DEBUG >= 5) {656cout << "\n-- Start Orbit --\n";657printUIntArray(cWord, currPeriod);658cout << "\n";659}660661//Perform experiment662j = 1;663while(true) {664//NOTE: no need to clear output[]. Comp::image will automatically665//initialize output[] to 0's and then fill it in.666667comp->image(cWord, currPeriod, output, blocks);668669copyUIntArray(cWord, output, blocks);670671//DEBUG672if(DEBUG >= 5) {673printUIntArray(cWord, currPeriod);674cout << "\n";675}676677StorageKeyUInt sk(cWord,blocks);678679rt = tree.find(sk);680if(rt != tree.end()) {681break;682}683else {684tree.insert(pair<StorageKeyUInt, unsigned long long>(sk, j));685}686687j++;688}689690period = j - rt->second;691preperiod = rt->second;692693record->recordPeriod(period, preperiod);694}695696record->pending = false;697698//advance recIterator to next in list699recIterator++;700701if(OUTPUT_AFTER_EACH) {702output(2, numPoints, prefix, periodList, comp, recList, commandbuffer);703}704}705}706707void convertPeriodList(const string &strInput,708vector<int> &periodList) {709vector<string> strList; //all terms710vector<string> subList; //vector representation of an "[a,b]" term711unsigned int strListLength = 0, i = 0;712713split(strInput,';', strList);714strListLength = strList.size();715716for(i = 0; i < strListLength; i++) {717string *temp = &(strList[i]);718719trim(*temp);720721if((*temp)[0] == '[') {722int j, low, high;723724temp->erase(0,1); //remove '['725temp->erase(temp->length() - 1, 1); //remove ']'726727split(*temp, ',', subList);728729trim(subList[0]);730trim(subList[1]);731732low = atoi(subList[0].c_str());733high = atoi(subList[1].c_str());734735for(j = low; j <= high; j++) {736periodList.push_back(j);737}738739subList.clear();740}741else {742periodList.push_back(atoi(temp->c_str()));743}744}745}746747/*748* Delete the nodes pointed to by ptrs in recList749*/750void clearRecList(list<RecNode*> &recList) {751list<RecNode*>::iterator it;752753for(it = recList.begin(); it != recList.end(); it++) {754delete (*it);755}756}757758/*759* Delete the nodes pointed to by the values in the table760*/761void clearStorageNodes(vector<StorageVal*> &table) {762vector<StorageVal*>::iterator it;763764for(it = table.begin(); it != table.end(); it++) {765delete (*it);766}767}768769/*770* Find divisors of X, excluding X itself771*/772void findDivisors(unsigned int x, vector<int> &divisorsList) {773unsigned int i = 2;774unsigned int halfX = x/2;775776divisorsList.clear();777778if(x > 1) {779divisorsList.push_back(1);780}781782for(i = 2; i <= halfX; i++) {783if(x % i == 0) {784divisorsList.push_back(i);785}786}787}788789/*790* returns true if this word has least period p791*/792bool leastPeriod(byte *word, unsigned int currPeriod,793const vector<int> &divisorsList) {794bool result = false;795unsigned int i, j, k;796797if(currPeriod == 1)798return true;799800//for every divisor d801for(i = 0; i < divisorsList.size(); i++) {802unsigned int d = divisorsList[i];803804result = false;805806//check in chunks of length d807for(j = 0; j < currPeriod; j+= d) {808//check that this chunk is same as other chunks809for(k = 0; k < d; k++) {810if(word[k + j] != word[k]) {811result = true;812break;813}814}815816if(result == true)817break;818}819820if(result == false)821break;822}823824return result;825}826827/*828* returns true if this word has least period p829*/830bool leastPeriod(unsigned int word, unsigned int currPeriod,831const vector<int> &divisorsList) {832bool result = false;833unsigned int i, j, k;834835if(currPeriod == 1)836return true;837838//for every divisor d839for(i = 0; i < divisorsList.size(); i++) {840unsigned int d = divisorsList[i];841842result = false;843844//check in chunks of length d845for(j = 0; j < currPeriod; j+= d) {846//check that this chunk is same as other chunks847for(k = 0; k < d; k++) {848if(((word >> (k + j)) & 1) != ((word >> k) & 1)) {849result = true;850break;851}852}853854if(result == true)855break;856}857858if(result == false)859break;860}861862return result;863}864865866867868869870871872873874875876877878879880881