Logic Life Search (LLS)

For scripts to aid with computation or simulation in cellular automata.
User avatar
muzik
Posts: 6558
Joined: January 28th, 2016, 2:47 pm
Location: Scotland

Re: Logic Life Search

Post by muzik » March 25th, 2024, 7:08 am

Been trying to follow the wiki's instructions to get this to work, but I end up with this error:

Code: Select all

$ python lls -c -b 16 6 -s p3 x0 y1
Traceback (most recent call last):
  File "lls", line 7, in <module>
    import src.search_patterns
ImportError: No module named src.search_patterns
Parity Replicator Collection v1.6 is now live - please send all relevant discoveries here.

User avatar
confocaloid
Posts: 6697
Joined: February 8th, 2022, 3:15 pm
Location: learn to protect yourself against stray gliders and sparks and self-destruct mechanisms

Re: Logic Life Search

Post by confocaloid » March 25th, 2024, 7:16 am

muzik wrote:
March 25th, 2024, 7:08 am

Code: Select all

$ python lls -c -b 16 6 -s p3 x0 y1
Traceback (most recent call last):
  File "lls", line 7, in <module>
    import src.search_patterns
ImportError: No module named src.search_patterns
Since you were able to run the main script with "python lls", the script lls must be in the current directory. Check whether there is a subdirectory 'src' and a file 'src/search_patterns.py' there. If not, probably the files were not unpacked completely.
127:1 B3/S234c User:Confocal/R (isotropic CA, incomplete)
Unlikely events happen.
My silence does not imply agreement, nor indifference. If I disagreed with something in the past, then please do not construe my silence as something that could change that.

User avatar
muzik
Posts: 6558
Joined: January 28th, 2016, 2:47 pm
Location: Scotland

Re: Logic Life Search

Post by muzik » March 25th, 2024, 7:18 am

confocaloid wrote:
March 25th, 2024, 7:16 am
Check whether there is a subdirectory 'src' and a file 'src/search_patterns.py' there.
Affirmative. I've also tested the other two example commands; the second (for Brain) gives the same error as the first. The third reports that ".python" is not a valid command, so I'm not sure if there's a typo at the wiki or if the python installation I'm using is somehow at fault.
Parity Replicator Collection v1.6 is now live - please send all relevant discoveries here.

User avatar
confocaloid
Posts: 6697
Joined: February 8th, 2022, 3:15 pm
Location: learn to protect yourself against stray gliders and sparks and self-destruct mechanisms

Re: Logic Life Search

Post by confocaloid » March 25th, 2024, 7:32 am

The dot is a typo in the wiki tutorial - it should not be there.
Not sure about the cause of the first error. On my system I have "python3" for Python 3, and the modified invocation (python -> python3) worked

Code: Select all

# python3 lls -c -b 16 6 -s p3 x0 y1

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

Re: Logic Life Search

Post by dvgrn » March 25th, 2024, 9:33 am


User avatar
muzik
Posts: 6558
Joined: January 28th, 2016, 2:47 pm
Location: Scotland

Re: Logic Life Search

Post by muzik » March 25th, 2024, 1:32 pm

confocaloid wrote:
March 25th, 2024, 7:32 am
Not sure about the cause of the first error. On my system I have "python3" for Python 3, and the modified invocation (python -> python3) worked

Code: Select all

# python3 lls -c -b 16 6 -s p3 x0 y1
This did the trick. However, the second command on the page returns dart instead, which I don't think is right:

Code: Select all

$ python3 lls -c -b 17 12 -s p3 x0 y1 -s "D2|"
Getting search pattern...
Done

Preprocessing...
Done

Width: 19
Height: 14
Duration: 4

Number of undetermined cells: 315
Number of variables: 424
Number of clauses: 43364

x = 19, y = 14, rule = B3/S23
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbooboboboboboobbb$
bbobbbooobooobbbobb$
bbbobooboboboobobbb$
bbbboboobbboobobbbb$
bbbbbbbobbbobbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbobbbobbbbbbb$
bbbbbbbbooobbbbbbbb$
bbbbbbbbooobbbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb!


Total solver time: 6.353018522262573
The command I used is clearly the same as the one on the wiki, minus the python3 detail - did I do something wrong?

The third command works as expected.
Parity Replicator Collection v1.6 is now live - please send all relevant discoveries here.

User avatar
confocaloid
Posts: 6697
Joined: February 8th, 2022, 3:15 pm
Location: learn to protect yourself against stray gliders and sparks and self-destruct mechanisms

Re: Logic Life Search

Post by confocaloid » March 25th, 2024, 1:50 pm

muzik wrote:
March 25th, 2024, 1:32 pm
However, the second command on the page returns dart instead, which I don't think is right:
IIRC LLS traverses search space in different order on different invocations, and also depending on which SAT solver is used behind the scenes. If both spaceships are small enough to be solutions, either of them may be returned.

You can try running with "-n" to find all solutions (in some order, which again may be different for different invocations):

Code: Select all

# python3 lls -c -b 17 12 -s p3 x0 y1 -s "D2|" -n
Note though that in this case, for every found object, it will find every phase of every translation that fits in the bounds and print as a separate solution.

Edit: you can instead require that the population in first phase be equal to 62:

Code: Select all

#C found using LLS/kissat with:
#C python3 lls -c -b 17 12 -s p3 x0 y1 -s "D2|" -p "=62"
#C Total solver time: 140.29749941825867
x = 19, y = 14, rule = B3/S23
bbbbbbbbbbbbbbbbbbb$
bbbbbbbooboobbbbbbb$
booobobbbbbbbobooob$
bbbbbbbbobobbbbbbbb$
bbbobbobobobobbobbb$
bbbooboboboboboobbb$
bbbbbbobobobobbbbbb$
bbbbbobbobobbobbbbb$
bobobbbooboobbbobob$
bobbboobbbbboobbbob$
bbooboobbbbbooboobb$
bboobbbbbbbbbbboobb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb!
Or less than 35:

Code: Select all

#C found using LLS/kissat with:
#C python3 lls -c -b 17 12 -s p3 x0 y1 -s "D2|" -p "<35"
#C Total solver time: 309.99119544029236
x = 19, y = 14, rule = B3/S23
bbbbbbbbbbbbbbbbbbb$
bbbobooboboboobobbb$
bbobbbbbobobbbbbobb$
bbboobbbobobbboobbb$
bbbbobbbobobbbobbbb$
bbbbbboobbboobbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbooobbbbbbbb$
bbbbbbbobbbobbbbbbb$
bbbbbbbbobobbbbbbbb$
bbbbbbbbbobbbbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbbbbbb!
127:1 B3/S234c User:Confocal/R (isotropic CA, incomplete)
Unlikely events happen.
My silence does not imply agreement, nor indifference. If I disagreed with something in the past, then please do not construe my silence as something that could change that.

User avatar
hollowme
Posts: 6
Joined: November 19th, 2024, 5:07 pm

Re: Logic Life Search

Post by hollowme » November 27th, 2024, 7:05 am

Hi! I'm trying to use LLS to find the minimal immediate predecessor with kissat as a solver but ran into a problem. For this input:

[EDIT] tested with lengeling and got the same result..

Code: Select all

* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 
* * * * * * * * * * * * * * * * * * * * * * * * * 


0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 0 
0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 
1 1 0 1 0 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 0 1 1 0 1 
1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 
0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 
1 0 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 
1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 
1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 
0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 1 
0 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 
1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 1 0 0 0 1 
0 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 
1 0 0 0 0 0 0 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 1 1 1 
0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 
1 0 0 0 1 1 1 1 1 0 1 0 0 1 1 0 1 0 0 0 0 0 1 0 1 
1 1 0 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 
0 0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 1 1 1 0 1 1 1 0 0 
0 0 1 1 1 1 0 1 0 0 0 1 1 1 0 1 1 0 0 0 1 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 1 
1 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 
0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 
1 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 
1 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 
0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0

by repeatedly running LLS with population constraints, the minimal i could get is with 242 live cells in the following board:

Code: Select all

 python3 lls in.txt -p "<243"
Getting search pattern...
Done

Preprocessing...


Done

Width: 27
Height: 27
Duration: 2

Number of undetermined cells: 625
Number of variables: 6338
Number of clauses: 247086

x = 27, y = 27, rule = B3/S23
bbbbbbbbbbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbbobbboobbobbobbbb$
bbbboobboobbbbbbbbboobobbbb$
bbobbbbbbbboobobboobbbbobob$
bboboobbbbobbbbbbobbboooobb$
bobobbbobbbobbobbobobbbbbob$
bbobboobbbooobbbbbboooobbob$
bbbobbbbbboobobbbbobobobobb$
booobbobbobbbbbbbobbbboobob$
bbobobobooboobbbbbooobbbbbb$
bbbbbooooobbbobooooobbbbobb$
bbooobobbbobobbbobobobbooob$
bbbobbbobboooobobbbbbbboobb$
boobooobboobobbbobbbboobbbb$
bbbbboobobbobboobobobbbooob$
bobooboobbbboobobbbboobbobb$
bboobobboobbbboboboboobobob$
bbobbbbbbooobobbbbobboobbob$
bbobobbbbobbbboboooobbbbbbb$
bbbbboobooobboobbbbbbbobobb$
bbobbobbbbooobbobbbooobbbob$
bobboobbbobbooooobbbooobobb$
bbooobboobbbboboobbobobobbb$
boobobbbbbobbbbbooooooobobb$
bobooobbobobobbobbobbbobbbb$
bbbbbobbboobobbbobobobbbbbb$
bbbbbbbbbbbbbbbbbbbbbbbbbbb!
However, I know for a fact there is a 25x25 board that generates my objective with 235 live cells. This one in particular:

Code: Select all

bbbbbbbbbbbbbbbbbbbbbbbbbbb$
bbbbbbbbbbobbbboobbbobbbbbb$
bobbbbbbbbbobbbbbbbooboobbb$
bbobobbbobbobobbobobbbbobbb$
booobbbbobobobbbbobbbooooob$
bbbbobbooobbobbbbobobbbbbob$
bobbbbboobbbobobbbboooobbob$
boboobbooobbbbbbbbobobobobb$
booboobbobbbbbbbbobbbboobob$
bbobbbbbbbbobbbbbbooobbbbbb$
bbbbobobbbbobooboboobbbbobb$
bboobbbbobooobbboobbbobooob$
bbbobbbbbbbbooboobbbbboobob$
boooooboooboobbbbbbbbbobbob$
bbbbbbbbbboobboobbbbbbbbobb$
bboobbbbbbbbboobbbobbobbobb$
bbbbobobbooobbobbooooobbbbb$
boobbbooboobbbobbobboobooob$
bbooboboobbobboboboobbbbbbb$
bobbbboboboboobbbbbobobboob$
bbbobbobobbbobboobbooobbbbb$
bobobbbbooooobbbboboboobbob$
booooobobbboobobooobooboobb$
booobbbbbbbbbbboobbooboobbb$
bbboooboobobbbbobboobbbobbb$
bobbbooooobobbbbbooobbbbbbb$
bbbbbbbbbbbbbbbbbbbbbbbbbbb!
I added the extra 2 dimensions, making it 27x27, of dead cells to mimic what I was expecting LLS would give me. Any ideas on why LLS cant find the 235 board?

Also, can I make it never expand the extra 2 dimensions? Couldnt do it for the life of me. It always gives the answer with two extras...

User avatar
Macbi
Posts: 917
Joined: March 29th, 2009, 4:58 am
Contact:

Re: Logic Life Search

Post by Macbi » November 27th, 2024, 7:46 am

Hi hollowme, it looks like both of these issues are about how LLS treats the edge of the pattern. By default LLS adds a row of empty cells around the entire pattern, so that no cell outside the original bounding box can become live. However I see that your 235 cell pattern produces some additional cells outside the ones specified in in.txt:

Code: Select all

x = 27, y = 26, rule = LifeHistory
19.3A$10.3A2.3A.5A$9.3A5.4A$.2A.A5.A.2A2.2A4.2A.A$.A.A8.A6.A5.AC$2.3A
.A6.A8.4A$CA.4A2.A8.A.A3.2A$.A2.2A.3A7.A2.A.3A$.2A.A7.A7.A2.2A$2.A10.
A.2A6.3A$2.3A5.A7.A6.A$.A5.A5.A.3A3.A3.AC$2.3A3.2A6.A5.A2.A$.A6.4A2.
2A7.3A$3.A5.A2.2A3.2A2.A$.A3.5A.A2.2A.A5.A.A$.2A.A8.2A.A4.2A.A$3.A.A
5.2A.2A.3A.3A$3.4A.A3.3A.2A3.A$8.A7.A2.A4.2A$.A3.2A.3A12.2A$C7.2A2.2A
10.A$.A5.2A2.A2.A$.A.A.A4.A4.A6.2A$5.A3.2A6.A.A$6.3C9.C!
To get the 235 cell pattern from LLS you could use a search file like the following:

Code: Select all

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 * * * * * * * * * * * * * * * * * * * * * * * * * 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

* * * * * * * * * * * * * * * * * * * * * * * * * * *
* 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 *
* 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0 1 1 1 1 1 0 0 *
* 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 *
* 1 1 0 1 0 0 0 0 0 1 0 1 1 0 0 1 1 0 0 0 0 1 1 0 1 *
* 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 *
* 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 *
* 1 0 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 1 *
* 1 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 *
* 1 1 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 1 0 *
* 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 0 1 1 1 *
* 0 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 *
* 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 1 1 0 0 0 1 0 0 0 1 *
* 0 1 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 *
* 1 0 0 0 0 0 0 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 1 1 1 *
* 0 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 *
* 1 0 0 0 1 1 1 1 1 0 1 0 0 1 1 0 1 0 0 0 0 0 1 0 1 *
* 1 1 0 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 *
* 0 0 1 0 1 0 0 0 0 0 1 1 0 1 1 0 1 1 1 0 1 1 1 0 0 *
* 0 0 1 1 1 1 0 1 0 0 0 1 1 1 0 1 1 0 0 0 1 0 0 0 0 *
* 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 1 *
* 1 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 *
* 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 *
* 1 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 *
* 1 0 1 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 *
* 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 *
* * * * * * * * * * * * * * * * * * * * * * * * * * *
This forces the starting pattern to be 25 by 25, but allows the goal pattern to be one cell larger.

User avatar
hollowme
Posts: 6
Joined: November 19th, 2024, 5:07 pm

Re: Logic Life Search

Post by hollowme » November 27th, 2024, 8:26 am

Hey Macbi, thank you so much for your answer!! This makes a lot of sense. Let me ask you something real quick:

If my goal inputs always have a dead border, lets say:

Code: Select all

    0 0 0 0 0 0
    0 0 1 1 0 0
    0 0 0 1 1 0
    0 0 0 0 0 0
for example and the predecessor can have live cells at the edges in order to generate the goal, so this:

Code: Select all

    0 0 0 0 0 0
    0 0 1 1 0 0
    0 0 0 1 0 0
    0 0 0 1 0 0
is a valid predecessor (maybe not the minimal one, but valid!). Can I make LLS give an answer with the same dimensions as the input that will account for things like that?

[EDIT] if not, from what i understand, I can just let it add the extra border and then cut it out in my output since the extra border will always be dead, right?

User avatar
Macbi
Posts: 917
Joined: March 29th, 2009, 4:58 am
Contact:

Re: Logic Life Search

Post by Macbi » November 27th, 2024, 12:03 pm

hollowme wrote:
November 27th, 2024, 8:26 am
[EDIT] if not, from what i understand, I can just let it add the extra border and then cut it out in my output since the extra border will always be dead, right?
Right, so for that search you could just do

Code: Select all

* * * * * *
* * * * * *
* * * * * *
* * * * * *

0 0 0 0 0 0
0 0 1 1 0 0
0 0 0 1 1 0
0 0 0 0 0 0
and LLS would give you an answer that it claimed was 8 by 6, but it would actually be the 6 by 4 solution you wanted within a border of dead cells.

User avatar
TigerCub414
Posts: 160
Joined: March 25th, 2024, 9:51 pm

Re: Logic Life Search

Post by TigerCub414 » January 20th, 2025, 4:52 pm

I have encountered a problem with LLS. Running it with this search pattern

Code: Select all

0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,*,*,*,*,*,*,*,*,*,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0

0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,1,1,1,1,1,0,0,0,0,0,0
0,0,1,0,0,0,0,1,0,0,0,0,0
0,0,1,0,0,0,0,0,0,0,0,0,0
0,0,0,1,0,0,0,1,0,0,0,0,0
0,0,0,0,0,1,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,1,1,1,0,0
0,0,0,0,0,0,1,1,0,1,1,0,0
0,0,0,0,0,1,0,0,0,0,1,0,0
0,0,0,0,1,1,0,1,0,1,0,0,0
0,0,0,1,1,0,0,1,0,1,0,0,0
0,0,0,0,1,1,0,0,0,0,0,0,0
0,0,0,0,0,0,0,1,1,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0
results in an error -

Code: Select all

AssertionError: Search pattern is not cuboidal
idk, the search pattern looks cuboidal to me. How can I fix the search pattern so that LLS works?

Code: Select all

x = 32, y = 32, rule = B3aijkr5j/S2aek3-acy4iz6c
8b3o$7bo2bo$7bo2bo$7b3o4$28b3o$28bo2bo$28bo2bo$29b3o11$3o$o2bo$o2bo$b
3o4$22b3o$21bo2bo$21bo2bo$21b3o!

User avatar
confocaloid
Posts: 6697
Joined: February 8th, 2022, 3:15 pm
Location: learn to protect yourself against stray gliders and sparks and self-destruct mechanisms

Re: Logic Life Search

Post by confocaloid » January 20th, 2025, 5:02 pm

TigerCub414 wrote:
January 20th, 2025, 4:52 pm
I have encountered a problem with LLS. Running it with this search pattern [...][...] results in an error -

Code: Select all

AssertionError: Search pattern is not cuboidal
idk, the search pattern looks cuboidal to me. How can I fix the search pattern so that LLS works?
How are you running LLS with that pattern?

I just saved your search pattern (without any changes) into a text file "post202057.txt" and ran LLS, and got the following. At least it doesn't complain with the error you mentioned, so something must be different.
./lls post202057.txt wrote:

Code: Select all

Getting search pattern...
Done

Preprocessing...
Done

Width: 15
Height: 18
Duration: 2

Number of undetermined cells: 108
Number of variables: 108
Number of clauses: 7112

x = 15, y = 18, rule = B3/S23
bbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbb$
bbbooboobbbobbb$
bbboobbobbbbbbb$
bbbbbooobbbbbbb$
bbbboooooobbbbb$
bbbbobbbbbbobbb$
bbboobbbooobbbb$
bbboobobbbobbbb$
bbbbobbobbbobbb$
bbbobobbbobobbb$
bbbbbobbobbbbbb$
bbbbbbooobobbbb$
bbbbobbboobbbbb$
bbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbb$
bbbbbbbbbbbbbbb!


Total solver time: 0.05301833152770996
127:1 B3/S234c User:Confocal/R (isotropic CA, incomplete)
Unlikely events happen.
My silence does not imply agreement, nor indifference. If I disagreed with something in the past, then please do not construe my silence as something that could change that.

User avatar
TigerCub414
Posts: 160
Joined: March 25th, 2024, 9:51 pm

Re: Logic Life Search

Post by TigerCub414 » January 20th, 2025, 7:04 pm

Never mind, I copied this exact search pattern and ran LLS again, and it worked. I must have copied the wrong pattern the first time :oops:

Code: Select all

x = 32, y = 32, rule = B3aijkr5j/S2aek3-acy4iz6c
8b3o$7bo2bo$7bo2bo$7b3o4$28b3o$28bo2bo$28bo2bo$29b3o11$3o$o2bo$o2bo$b
3o4$22b3o$21bo2bo$21bo2bo$21b3o!

yoleo
Posts: 139
Joined: October 26th, 2021, 11:48 pm

Re: Logic Life Search

Post by yoleo » February 1st, 2025, 6:50 pm

LLS's symmetry notation seems incomplete and/or inconsistent. The help says:
"p2 RE\ x1" imposes the glide-reflect symmetry of a glider.
Based on that, "p1 RO1" should mean "rotates by 90 degrees every generation." Yet that option isn't recognized, and instead triggers the assert on line 271 of lls. Is there a reason for this?

"p2 RE\ x1" is a spacetime transformation, not a group (though it does generate a group). Maybe to make the parsing easier, the symmetry flag could be split into two separate flags: one where you provide a group, and the other where you provide a list of spacetime transformations (under which the pattern must be invariant). I'd settle for just a single arbitrary spacetime transformation (with an isometry as its spatial part), like my "p1 RO1" above.

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » February 5th, 2025, 10:37 am

I wrote a quick Python script to convert certain JLS save files into LLS input files. Unfortunately at the moment it only supports oscillator searches, and even then I don't think it supports all of the JLS/LLS features that it could, although I haven't looked carefully to see what overlap there is. At least it works with subperiodic cells.

Here is the script, which I called "jls-to-lls.py"

EDIT: the following code is obsolete. See below for the updated code.

Code: Select all

import re
import sys

# get the nth (0-indexed) curly brace array from the given line
def get_array(line, n):
    return eval('[' + re.findall(r"\{(.*?)\}", line)[n] + ']')

cell_rows = []
subperiod_array = []

# Read the JLS save file and extract important info
with open(sys.argv[1], 'r') as file:
    for line in file:
        
        # Get width, height, and depth
        if line.startswith("columns=") or line.startswith("rows=") or line.startswith("generations="):
            exec(line)
        
        # Element 0 of subperiods is the same as generations, and elements 1 through 6 are the subperiods
        if line.startswith("periods="):
            subperiods = get_array(line,0)
        
        # Get the cell array as a big list of rows. We'll organize this later.
        if line.startswith("cells"):
            cell_rows.append(get_array(line,1))
        
        # To get the subperiod settings for each cell, we must reduce every entry in the "stacks" array mod 8.
        if line.startswith("stacks"):
            subperiod_array.append(list(map(lambda x: x % 8, get_array(line,1))))
        
        # Lines after "[Search]" represent the search state. We only want the initial setup.
        if line.startswith("[Search]"):
            break

cell_array = []

# Convert our list of cell rows into a 3D array
for gen in range(generations):
    cell_array.append(cell_rows[gen*rows:(gen+1)*rows])

# Create the LLS input file text
lls_input = ""
for gen in range(generations):
    for row in range(rows):
        for column in range(columns):
            the_cell = cell_array[gen][row][column]
            if the_cell == 0:
                lls_input += "0,"
            elif the_cell == 1:
                lls_input += "1,"
            elif the_cell == 2:
                if subperiod_array[row][column]:
                    lls_input += "x" + str(gen % subperiods[subperiod_array[row][column]]) + "-" + str(row) + "-" + str(column) + ","
                elif gen == 0:
                    lls_input += "x0" + "-" + str(row) + "-" + str(column) + ","
                else:
                    lls_input += "*,"
            else:
                lls_input += "*',"
        lls_input += "\n"
    if gen == 0:
        first_gen = lls_input
    lls_input += "\n"
lls_input += first_gen

print(lls_input)
The script takes one argument, the file name of a JLS save file residing in the same folder as the script, and sends the text of the LLS input file to stdout. Usage looks like the following:

Code: Select all

python3 jls-to-lls.py jls-save-file.jdf > lls-input-file.txt
Anyone is welcome to modify the script if they want. I might eventually get around to adding more features, like translation, but I offer no guarantees.

Edit: fixed substantial bug where non-subperiodic empty cells were set to "*" in generation 0, when they should have been set to a variable name.
-Matthias Merzenich

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » February 9th, 2025, 8:28 am

As I was attempting to reduce the p35 gun, I realized that the above script was insufficient for my needs, so I updated it:

Code: Select all

# jls-to-lls.py version 1.0 by Matthias Merzenich
# This is a script for converting JLS save files into LLS input files.
#
# Usage: python3 jls-to-lls.py [JLS-save-file-name] > [LLS-input-file-name]
# 
# This version only supports oscillator searches.  Since there are no LLS
# cell states precisely corresponding to JLS UNCHECKED or UNSET cells, this
# script uses the following conventions:
# - UNCHECKED cells do not obey subperiodicity but do obey the CA rules.
# - UNSET cells do obey subperiodicity but do not obey the CA rules.
# - UNCHECKED_FROZEN cells behave identically to FROZEN cells (i.e, they
#   match the cell state in the same position in the previous generation).

import re
import sys

# There is almost never a need for six different subperiods, so the sixth
# subperiod is used to indicate that all cells at that position do not need
# to obey the CA rules.  This can be useful when there are intentional errors
# in your initial setup.  To turn this feature off and have subperiod six be
# treated as a normal subperiod, set subperiod_six_no_rule to False.
subperiod_six_no_rule = True

# JLS cell states
OFF              = 0
ON               = 1
EMPTY            = 2
UNCHECKED        = 6    # 'X'
UNSET            = 10   # '#' Obtained in JLS by selecting a cell and pressing 'U'.
FROZEN           = 18   # 'F'
UNCHECKED_FROZEN = 22   # Obtained in JLS by selecting a frozen cell and pressing 'O'.

cell_rows = []
cell_array = []
subperiod_array = []

# get the nth (0-indexed) curly brace array from the given line
def get_array(line, n):
    return eval('[' + re.findall(r"\{(.*?)\}", line)[n] + ']')

# Read the JLS save file and extract important info
with open(sys.argv[1], 'r') as file:
    for line in file:
        
        # Get width, height, and depth
        if line.startswith("columns=") or line.startswith("rows=") or line.startswith("generations="):
            exec(line)
        
        # Element 0 of subperiods is the full period (same as generations), and elements 1 through 6 are the subperiods
        if line.startswith("periods="):
            subperiods = get_array(line,0)
        
        # Get the cell array as a big list of row arrays. We'll organize this later.
        if line.startswith("cells"):
            cell_rows.append(get_array(line,1))
        
        # To get the subperiod settings for each cell, we must reduce every entry in the "stacks" array mod 8.
        if line.startswith("stacks"):
            subperiod_array.append(list(map(lambda x: x % 8, get_array(line,1))))
        
        # Lines after "[Search]" represent the search state. We only want the initial setup.
        if line.startswith("[Search]"):
            break

# Convert our list of cell rows into a 3D array
for gen in range(generations):
    cell_array.append(cell_rows[gen*rows:(gen+1)*rows])

# Set the sixth subperiod to be the full period when using
# it to identify cells that do not need to obey the CA rules
if subperiod_six_no_rule:
    subperiods[6] = generations

# For each cell in cell_array, set the corresponding variable name in lls_array
lls_array = [[[0 for k in range(columns)] for j in range(rows)] for i in range(generations)]

for gen in range(generations):
    for row in range(rows):
        for column in range(columns):
            the_cell = cell_array[gen][row][column]
            if the_cell == OFF:
                lls_array[gen][row][column] = "0"
            elif the_cell == ON:
                lls_array[gen][row][column] = "1"
            elif gen == 0 or the_cell == EMPTY or the_cell == UNCHECKED or the_cell == UNSET:
                lls_array[gen][row][column] = "x" + str(gen % subperiods[subperiod_array[row][column]]) + "-" + str(row) + "-" + str(column)
            elif the_cell == FROZEN or the_cell == UNCHECKED_FROZEN:
                lls_array[gen][row][column] = lls_array[gen - 1][row][column]
            
            # Special suffix so that unchecked cells don't have to have to obey subperiodicity
            if the_cell == UNCHECKED:
                lls_array[gen][row][column] += "-uc" + str(gen)
            
            # apostrophe suffix means the cell does not need to obey the CA rules
            if the_cell == UNSET or (subperiod_six_no_rule and subperiod_array[row][column] == 6):
                lls_array[gen][row][column] += "'"

# We have to do a second pass of generation 0 in case it contains any frozen cells
for row in range(rows):
    for column in range(columns):
        the_cell = cell_array[0][row][column]
        if the_cell == FROZEN or the_cell == UNCHECKED_FROZEN:
            lls_array[0][row][column] = lls_array[generations - 1][row][column]

# Create the LLS input file text
lls_input = ""

for gen in range(generations):
    for row in range(rows):
        for column in range(columns):
            lls_input += lls_array[gen][row][column] + ","
        lls_input += "\n"
    if gen == 0:
        gen_zero = lls_input
    lls_input += "\n"


lls_input += gen_zero

print(lls_input)
Notably, I made changes to how UNCHECKED and UNSET cells work, and I used the sixth subperiod to mark cells that do not have to obey the CA rules. Specifically,
  • JLS UNSET cells ("#" cells) obey subperiodicity, but do not need to obey the CA rules (they get the apostrophe suffix in LLS). I'm not sure if obeying subperiodicity really matters for cells that don't obey the CA rules, but I figured it gives more control over the cell, so it can't hurt
  • JLS UNCHECKED cells ("X" cells) do not obey subperiodicity, but they do obey the CA rules. I think this could be convenient for finding bespoke catalytic oscillators. It allows the user to set a cell to be subperiodic, but then choose certain generations in which the cell is allowed to violate that subperiodicity. The trick is that the user needs to set the UNCHECKED cells first, and only then can they set the cell to be subperiodic. If they set the cell to be subperiodic first, then every time they set the cell to be UNCHECKED in one generation, it will also become UNCHECKED in all corresponding generations defined by the subperiod.
  • Since I never use six different subperiods, I used subperiod six to designate cells that should get an apostrophe suffix in LLS. This is very useful if your JLS setup has regions with intentional errors. Just cover the area in subperiod six cells so that it won't immediately return unsat in LLS. Note that it's generally wise to change subperiod six to have period equal to the full period, as this will prevent JLS from using the subperiod to determine cell states.
I think it's also worth noting that jls-to-lls.py only copies cells set by the user, and not the JLS preprocessed cells (shown in a lighter color in JLS). For this reason, I think it's generally a good idea to select "Search->Accept displayed state" before saving the JLS file, which turns those JLS preprocessed cells into properly set cells.

The script still only works for oscillators. It shouldn't be too hard to add support for translation and predecessor searches, so I'll probably do that eventually.
-Matthias Merzenich

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » March 5th, 2025, 9:36 am

Here is a zip archive containing a simple example JLS save file that can be run directly with LLS to find the p3 catalysts for the p57 oscillator in this post:
p57-setup.zip
(17.28 KiB) Downloaded 87 times
The purpose of this example isn't to run the search, but to view the setup in JLS. Start JLS and open p57-setup.jdf. Notice that the period is set to 57 and the entire engine is included in the search. I initially added the engine to JLS using the following method:
  1. Set the period of the search to 57 and set the width and height to be sufficiently large to contain the engine and the expected solution (I chose a width and height of 66).
  2. Make sure we are in generation 0, press CTRL+A to select the entire grid, then simply press A to fill the grid with OFF cells.
  3. Copy the following engine, including supporting sparks, and paste it into JLS:

    Code: Select all

    x = 33, y = 34, rule = B3/S23
    12b2o5b2o$12b2o5b2o6$bo3b2o19b2o3bo$bo3b2o7b2ob2o7b2o3bo$o2b2o6bo2b2ob
    2o2bo6b2o2bo$2bo2bo3bobo9bobo3bo2bo$2bobo6bo9bo6bobo$3bo4bobo11bobo4bo
    $8b2o13b2o7$8b2o13b2o$3bo4bobo11bobo4bo$2bobo6bo9bo6bobo$2bo2bo3bobo9b
    obo3bo2bo$o2b2o6bo2b2ob2o2bo6b2o2bo$bo3b2o7b2ob2o7b2o3bo$bo3b2o19b2o3b
    o6$12b2o5b2o$12b2o5b2o!
  4. Select "Search->Accept displayed state"
  5. Set the symmetry to "vertical mirror", press CTRL+A, and hold SHIFT while using the arrow keys to move the engine vertically to the center of the grid.
Here are some things that I think are important to notice about the JLS input file (this assumes some familiarity with JLS):
  • I'm only searching for a solution on the right side of the engine. On the left side I have just left the sparks in place. However, to prevent LLS from instantly returning "unsatisfiable", I have to tell it to ignore the fact that these sparks don't reappear in 57 generations. To do this, I highlighted them and pressed '6' to set them to the sixth subperiod. In jls-to-lls.py, the sixth subperiod is used to indicate that a cell in all generations does not need to obey the Life rules.
  • I have bounded the p3 search area by a layer of sixth subperiod cells, followed by a 1-cell thick layer of period-1 '#' cells (select a box of cells and press 'U' to set them to be '#' cells), followed by a 2-cell thick layer of period-1 empty cells. This allows the search to find solutions with incomplete stators, so that I don't have to worry about how wide or tall the stator might be.
  • I have chosen the starting phase to be the phase just before I expect the catalyst to interact. At and around the site of the expected interaction I have set the cells to be 'X' cells up to generation 9 and empty after that. I have also set these cells to be subperiodic with period 3. What this means is that these cells will be period-3 except possibly in the generations where the cell is set to 'X'. This gives a catalyst recovery time of up to 9 generations.

    Larger recovery times can be allowed by adding 'X' cells in more generations, but it may make the search slower. Note that when setting these 'X' cells, you need to first turn off subperiodicity (select the cells and press '0'). If you try to set an 'X' cell when the subperiod is set to 3, then it will also add an 'X' cell in every third generation, which is not what we want. After you have added any 'X' cells you want, then you can set the cell to be subperiodic.
  • Notice that there is a bunch of red in the cells within the catalyst area, indicating cell errors. This is because these cells are set to period 3 when they can't actually be period-3. This would cause problems if we wanted to search with JLS, but LLS will interpret these cells differently.
  • Edit: The instructions below are no longer necessary. You should instead use the option `-j <folder_name>` to save solutions in .jdf format in the folder given by <folder_name> (if no such folder exists, LLS will create it). The output solutions will have the name <folder_name>_n, for n = 1, 2, 3, ...

    I placed a block in the upper left corner (and due to symmetry in the lower left as well). This is used as a reference point for copying and pasting LLS solutions back into JLS. When LLS gives a solution, it adds an extra ring of OFF cells around the search area. If I try to copy and paste the solution directly from LLS output into my JLS file, it will not line up with the existing pattern. Instead, I copy the LLS output to a Life simulation program (e.g., LifeViewer or Golly), select the pattern starting from the upper left corner of the upper left block, and then I can paste that solution directly over my search setup in JLS.

    In this case, I might paste the solution into JLS in order to complete the stator. When pasting a solution back into JLS, it's important to set all of those p3 cells in the catalysis region to be full-period cells (select and press '0'), so that there are no cell errors. When pasting the pattern, only the ON cells are pasted, so you will likely have to fill in some of the gaps with OFF cells by hand. After a little bit of this, JLS usually has enough information to uniquely determine the pattern in all generations. Once this occurs, you can select "Search->Accept displayed state". You can then expand the stator area and run a quick search to find a working stator. I don't try to optimize the stator in JLS. Instead, I find a quick, large stator using JLS and minimize it using rstatoropt
Edit: The instructions below are now obsolete, as LLS can read the JLS status file directly without the need for the intermediary jls-to-lls.py script.

To perform the search with LLS, place the .jdf file in the same folder as lls and jls-to-lls.py. Then run

Code: Select all

python3 jls-to-lls.py p57-setup.jdf > p57-lls-input.txt
to convert it to the LLS input file format. Now the ('D2-' symmetric) search can be performed by running

Code: Select all

./lls p57-lls-input.txt -s 'D2-'
This should find a solution fairly quickly (less than 2 minutes). The solution will have an incomplete stator, which will need to be completed in some way.
-Matthias Merzenich

User avatar
Sylvani
Posts: 146
Joined: September 26th, 2024, 3:23 am

Re: Logic Life Search

Post by Sylvani » March 12th, 2025, 1:11 pm

Sokwe wrote:
February 9th, 2025, 8:28 am
As I was attempting to reduce the p35 gun, I realized that the above script was insufficient for my needs, so I updated it:
As I was attempting to find a "stabilization" (idrk the term) for a p30 rake, I realized that the above script had no option for translations (Agars are still not supported) so I hope this helps anyone :)

Code: Select all

# jls-to-lls.py version 1.0+0.1 by Matthias Merzenich (Modified by Sylvani)
# This is a script for converting JLS save files into LLS input files.
#
# Usage: python3 jls-to-lls.py [JLS-save-file-name] > [LLS-input-file-name]
# 
# This version doesn't support agar/wave searches.  Since there are no LLS
# cell states precisely corresponding to JLS UNCHECKED or UNSET cells, this
# script uses the following conventions:
# - UNCHECKED cells do not obey subperiodicity but do obey the CA rules.
# - UNSET cells do obey subperiodicity but do not obey the CA rules.
# - UNCHECKED_FROZEN cells behave identically to FROZEN cells (i.e, they
#   match the cell state in the same position in the previous generation).

import re
import sys

# There is almost never a need for six different subperiods, so the sixth
# subperiod is used to indicate that all cells at that position do not need
# to obey the CA rules.  This can be useful when there are intentional errors
# in your initial setup.  To turn this feature off and have subperiod six be
# treated as a normal subperiod, set subperiod_six_no_rule to False.
subperiod_six_no_rule = True

# JLS cell states
OFF              = 0
ON               = 1
EMPTY            = 2
UNCHECKED        = 6    # 'X'
UNSET            = 10   # '#' Obtained in JLS by selecting a cell and pressing 'U'.
FROZEN           = 18   # 'F'
UNCHECKED_FROZEN = 22   # Obtained in JLS by selecting a frozen cell and pressing 'O'.

cell_rows = []
cell_array = []
subperiod_array = []

# get the nth (0-indexed) curly brace array from the given line
def get_array(line, n):
    return eval('[' + re.findall(r"\{(.*?)\}", line)[n] + ']')

# Read the JLS save file and extract important info
with open(sys.argv[1], 'r') as file:
    for line in file:
        
        # Get width, height, and depth
        if line.startswith("columns=") or line.startswith("rows=") or line.startswith("generations=") or line.startswith("tile_temporal_shift_right=") or line.startswith("tile_temporal_shift_down="):
            exec(line)
       
        
        # Element 0 of subperiods is the full period (same as generations), and elements 1 through 6 are the subperiods
        if line.startswith("periods="):
            subperiods = get_array(line,0)
        
        # Get the cell array as a big list of row arrays. We'll organize this later.
        if line.startswith("cells"):
            cell_rows.append(get_array(line,1))
        
        # To get the subperiod settings for each cell, we must reduce every entry in the "stacks" array mod 8.
        if line.startswith("stacks"):
            subperiod_array.append(list(map(lambda x: x % 8, get_array(line,1))))
        
        # Lines after "[Search]" represent the search state. We only want the initial setup.
        if line.startswith("[Search]"):
            break

# Convert our list of cell rows into a 3D array
for gen in range(generations):
    cell_array.append(cell_rows[gen*rows:(gen+1)*rows])

# Set the sixth subperiod to be the full period when using
# it to identify cells that do not need to obey the CA rules
if subperiod_six_no_rule:
    subperiods[6] = generations

# For each cell in cell_array, set the corresponding variable name in lls_array
lls_array = [[[0 for k in range(columns)] for j in range(rows)] for i in range(generations + 1)]

for gen in range(generations + 1):
    for row in range(rows):
        for column in range(columns):
            row1 = row + (tile_temporal_shift_down if gen == generations else 0)
            col1 = column + (tile_temporal_shift_right if gen == generations else 0)
            the_cell = cell_array[gen % generations][row][column]
            if the_cell == OFF:
                lls_array[gen][row1][col1] = "0"
            elif the_cell == ON:
                lls_array[gen][row1][col1] = "1"
            elif gen == 0 or the_cell == EMPTY or the_cell == UNCHECKED or the_cell == UNSET:
                lls_array[gen][row1][col1] = "x" + str(gen % subperiods[subperiod_array[row][column]]) + "-" + str(row) + "-" + str(column)
            elif the_cell == FROZEN or the_cell == UNCHECKED_FROZEN:
                lls_array[gen][row1][col1] = lls_array[gen - 1][row1][col1]
            
            # Special suffix so that unchecked cells don't have to have to obey subperiodicity
            if the_cell == UNCHECKED:
                lls_array[gen][row1][col1] += "-uc" + str(gen % generations)
            
            # apostrophe suffix means the cell does not need to obey the CA rules
            if the_cell == UNSET or (subperiod_six_no_rule and subperiod_array[row1][col1] == 6):
                lls_array[gen][row1][col1] += "'"

# We have to do a second pass of generation 0 in case it contains any frozen cells
for gen in [0, generations]:
    for row in range(rows):
        for column in range(columns):
            the_cell = cell_array[gen % generations][row][column]
            if the_cell == FROZEN or the_cell == UNCHECKED_FROZEN:
                lls_array[gen][row][column] = lls_array[generations - 1][row][column]

# Create the LLS input file text
lls_input = ""

for gen in range(generations + 1):
    for row in range(rows):
        for column in range(columns):
            lls_input += lls_array[gen][row][column] + ","
        lls_input += "\n"
    lls_input += "\n"
print(lls_input)
I think to implement the torus stuff, the entire cell array would have to be padded by a copy of itself, perhaps with the modulo operation.

User avatar
Sylvani
Posts: 146
Joined: September 26th, 2024, 3:23 am

Re: Logic Life Search

Post by Sylvani » March 14th, 2025, 7:19 pm

How I got LLS to work on Windows:

1. Install glucose-syrup by following the instructions at https://github.com/audemard/glucose.
2. Go to your logic life search directory, and replace the "format_dimacs_output" function at src/sat_solvers.py with this:

Code: Select all

def format_dimacs_output(dimacs_output):

    lines = dimacs_output.strip('\n').split('\n')

    statuses = [line[2:] for line in lines if line[0] == 's']
    variable_lines = [line[2:] for line in lines if line[0] == 'v']
    
    if len(statuses) != 1:
        raise Exception('Wrong number of status lines')
    if statuses[0].startswith('UNSATISFIABLE'):
        return Status.UNSAT, None
    elif statuses[0].startswith('SATISFIABLE'):
        solution = set(literal for line in variable_lines for literal in line.split() if literal != '0')
        return Status.SAT, solution
3. To get LLS to see the output of glucose-syrup, you need to add [--parameters="-model"] (without the brackets) to whatever command you are running.

I might have missed a step so please let me know if there are problems.

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » March 21st, 2025, 9:00 am

Edit: this patch has been incorporated into the latest version of LLS, so there is no need to apply the patch anymore.

To simplify the use of jls-to-lls.py, I made the following patch that allows LLS to read JLS status files (.jdf files) directly:
jls-to-lls-patch.zip
(3.64 KiB) Downloaded 74 times
To apply the patch, unzip the above archive, copy jls-to-lls.patch into your logic-life-search folder, open a command terminal in that folder, and run

Code: Select all

git apply jls-to-lls.patch
Applying this patch adds two features to LLS:
  1. LLS can read JLS status files directly, rather than needing the separate jls-to-lls.py script. Unfortunately, it's still limited to the feature set from version 1.0. To use this feature, just use the JLS status file in place of the standard LLS input file.
  2. There is a new option, -j, that saves LLS solutions into JLS format. Unfortunately, if you gave a JLS status file as input, it does not copy the settings to the output JLS files; it only copies the period and grid size. To use this feature, include "-j <JLS_output_folder_name>" in your options. When using this option, JLS status files will be saved to the specified folder whenever a solution is found.
I might end up making my own fork of LLS eventually, but I don't intend to work on it any more right now.

Let me know if you have any difficulties with this patch.
-Matthias Merzenich

User avatar
Macbi
Posts: 917
Joined: March 29th, 2009, 4:58 am
Contact:

Re: Logic Life Search

Post by Macbi » March 21st, 2025, 9:21 am

Sokwe wrote:
March 21st, 2025, 9:00 am
To simplify the use of jls-to-lls.py, I made the following patch that allows LLS to read JLS status files (.jdf files) directly:
jls-to-lls-patch.zip
I went ahead and merged these into LLS (version 5).

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » March 21st, 2025, 9:33 am

Macbi wrote:
March 21st, 2025, 9:21 am
Sokwe wrote:
March 21st, 2025, 9:00 am
To simplify the use of jls-to-lls.py, I made the following patch that allows LLS to read JLS status files (.jdf files) directly:
jls-to-lls-patch.zip
I went ahead and merged these into LLS (version 5).
Great! There's still a lot of features missing from this (no support for translations, for example), and there's not really any documentation for how to set up a JLS search to be compatible with LLS. It would also be nice to pull settings from JLS and apply them automatically to the LLS search, but I'm not sure how much work that would be.

Edit: If you're still accepting additions to LLS in the form of patches, there was one other hacked-in feature I used to help find the p14 gun: limiting the rotor cells per "layer", which I have now reworked into a more fully formed feature in the patch below.
lls_layers_max_rotor.zip
(2.46 KiB) Downloaded 72 times
This is a feature from JLS and can be found by selecting "Search->Options..." and going to the "Constraints" tab. For example, this allows the user to search for patterns with at most 3 rotor cells per column. Unfortunately, the settings for the feature are not loaded with the JLS status file, and need to be added separately as command line options. The new command line options are:
  • --layer_max_rotor <max_rotor_cells_per_layer>
  • --layer_shape <column|row|diagonal|back_diagonal|box|diamond|circle>
  • --layer_center <x-coordinate> <y-coordinate> (only applicable for layer_shape of "box", "diamond", or "circle")
  • --layer_rotor_include_set_cells (includes manually set cells in rotor calculation; manually set cells are not counted by default)
-Matthias Merzenich

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

Re: Logic Life Search

Post by Scorbie » March 24th, 2025, 8:30 pm

Small diff for making sat solvers natively compiled on windows work with lls

Code: Select all

diff --git a/src/sat_solvers.py b/src/sat_solvers.py
index f85634b..a78ca77 100644
--- a/src/sat_solvers.py
+++ b/src/sat_solvers.py
@@ -90,7 +90,7 @@ def use_solver(solver, dimacs_string, parameters=None, timeout=None):

 def format_dimacs_output(dimacs_output):

-    lines = dimacs_output.strip('\n').split('\n')
+    lines = dimacs_output.replace('\r\n', '\n').strip('\n').split('\n')

     statuses = [line[2:] for line in lines if line[0] == 's']
     variable_lines = [line[2:] for line in lines if line[0] == 'v']


---

Edit:
Here's an example of a search where JLS is faster than LLS.
I thought it would be nice to share.
OS: Windows 10 Home, 64 bit
CPU: Intel(R) Core(TM) i5-3337U CPU @ 1.80GHz 1.80 GHz
Ram: 8.00GB
LLS compiled with msys2 using the ucrt compiler
capture.png
capture.png (25.54 KiB) Viewed 2013 times
p7-hw-2.zip
(4.99 KiB) Downloaded 73 times

Sokwe
Moderator
Posts: 3367
Joined: July 9th, 2009, 2:44 pm

Re: Logic Life Search

Post by Sokwe » March 26th, 2025, 6:13 am

Scorbie wrote:
March 24th, 2025, 8:30 pm
Here's an example of a search where JLS is faster than LLS.
When I tried this, it took ~17 minutes with JLS, and ~10 minutes with LLS. One thing that's not obvious is that most JLS settings, including symmetry, are not read by LLS, so they need to be set manually in the command line. In this case you would need to run

Code: Select all

lls p7-hw-2.jdf -s D2-
-Matthias Merzenich

Post Reply