krychu / wfc

Wave Function Collapse library in C, plus a command-line tool

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Contradictions

MayorUshanka opened this issue · comments

What are some techniques for avoiding contradictions? I'm trying to do a wfc on nyan cat:
nyan3

But sooner or later it always seems to exit citing that some contradiction occurred. For example:

./wfc -m overlapping -w 256 -h 256 -W 3 -H 3 -x 0 -e 1 -y 0 -r 0 nyan.png output5.png

method:               overlapping
seed:                 1641508864

input image:          nyan.png
input size:           44x33
input components:     4
tile size:            3x3
expand input:         1
xflip tiles:          0
yflip tiles:          0
rotate tiles:         0
tile count:           566

output image:         output5.png
output size:          256x256
cell count:           65536

cells collapsed:      275
Contradiction occurred, try again

output5
Note that for an output size of 64 by 64, it seems to work ok!

This is a nice example image :)

One note upfront: make was set to build debug version by default. I pushed a change to build non-debug, optimized code by default which should give a significant speed up.

Some implementations use backtracking or partitioning to mitigate contradictions. But from what I've read, they don't always seem to give tangible benefits compared to restarting the simulation. I haven't looked into it much though.

One thing you can do is to keep your input image not extremely varied in terms of NxN tiles that are extracted (3x3 by default). Contradictions are less likely when the tiles are more robust, that is, they are flexible in all directions and can match many other tiles. Hope this makes some sense. Take a look at the examples in the readme, the input images are often fairly simple patterns, with few colors, and few details. However, some experimentation is definitely a good idea.

Nyan cat is pretty detailed in that regard, and it results in 2664 tiles if you run wfc without any arguments. That's quite a lot. But I too managed to get some results by toning down the number of tiles and output resolution:

48x48 output, 530 tiles: ./wfc -m overlapping -e 0 -x 0 -y 0 -r 0 -w 48 -h 48 ~/Downloads/nyan.png output.png
output

80x80 output, 530 tiles: ./wfc -m overlapping -e 0 -x 0 -y 0 -r 0 -w 80 -h 80 ~/Downloads/nyan.png output2.png
output2

64x64 output, 1447 tiles: ./wfc -m overlapping -e 0 -x 1 -y 1 -r 0 -w 64 -h 64 ~/Downloads/nyan.png output3.png
output3

I see what you're getting at about the flexible tiles. The poptart with pink spots should be easy enough, But the head probably contains some fairly restricted tiles (eyes and cheeks), and without rotation, the tail probably as well, due to its unique grey/black/rainbow connections.

So, I made a nyan cat with just the cat, no stars and no rainbow, which seemed not just way faster (fewer tiles!) but also more robust.

I also found that the bigger the output image, the more likely a contradiction (though it may have been flukes):

./wfc -m overlapping -e 0 -x 0 -y 0 -r 0 -w 1024 -h 1024 -W 3 -H 3 only_cat.png output8.png

method:               overlapping
seed:                 1641594663

input image:          only_cat.png
input size:           39x24
input components:     4
tile size:            3x3
expand input:         0
xflip tiles:          0
yflip tiles:          0
rotate tiles:         0
tile count:           334

output image:         output8.png
output size:          1024x1024
cell count:           1048576

cells collapsed:      19
Contradiction occurred, try again

Adding more tile options, like rotations and flips, made a collision less likely, which makes sense as it "cheats" to make tiles more robust. (though it also takes longer to collapse since there are now more superpositions)

I've officially spent 3 days of my life getting my computer to generate lovecraftian nyan cat horrors. Thank you for this awesome program.

output7
output11
output15

Perhaps more performance gains can be made by giving the process a CPU affinity, or maybe even running it on the GPU. On one hand the recursion makes it tough for the GPU, on the other hand the GPU can match tiles across the entire board in one go in a shader.

You get it, all good insights! Inspired by your observations I also tried some versions that immediately came to mind:

rainbow only, ./wfc -w 64 -h 64 -m overlapping input.png output.png:
nyan_a -> nyan_aa

square nyan, ./wfc -w 64 -h 64 -m overlapping input.png output.png:
nyan_c -> nyan_cc

no rainbow, ./wfc -m overlapping input.png output.png:
nyan_d -> nyan_ddd

head in body, ./wfc -w 64 -h 64 -m overlapping input.png output.png:
nyan_e -> nyan_ee

head only, ./wfc -x 1 -y 0 -r 0 -W 3 -H 6 -w 64 -h 64 -m overlapping input.png output.png:
nyan_f -> nyan_f2

I've officially spent 3 days of my life getting my computer to generate lovecraftian nyan cat horrors.

This is great to hear, although I feel guilty for not having non-debug, optimized code enabled earlier. Things would have generated for you probably 4-5x faster.

The problem with parallel processing, in the case of wfc, is that it's not local: processing of one cell affects unpredicted number of other cells. So multiple processes could be trying to update the same cell in incompatible ways. And also, the choice of the next cell to process requires the previous processing to complete. That's not to say it cannot be made parallel but it will require some creative thinking :) Perhaps multiple processes could attack distant parts of the output image, and some constraints as to which cell to pick next could be relaxed.

There is definitely room for optimization though. I have a few ideas which I'll be trying soon.

Thanks for using wfc!

Also got this, but took a bit longer to generate:

no legs no tail, ./wfc -w 64 -h 64 -m overlapping input.png output.png:
nyan_b -> nyan_bb

Interesting stuff. It might pay off to do link time optimizations. -flto for most compilers. Since I had to modify the makefile to add -lm, I also took the liberty of adding -Ofast -flto -funroll-loops -march=native. I see someone else has already pulled to add -O3.

I merged a few optimizations. I'm getting additional ~2x speed up. I have a few more ideas but they will require more effort.

Feel free to create a PR with the flags you added :)

@MayorUshanka quick note. There was a bug in the calculation of entropy that influenced which cell was picked next to be collapsed. This has a notable effect on the output images. Now, correctly, the frequency of tiles in the output image matches their frequency in the input image. It also leads to fewer contradictions. Here's the issue: #14.

So I've re-run some of the above examples and am getting more interesting results now. You might want to try it yourself :)

nyan_aa2

nyan_bb2

nyan_cc2

nyan_dd2

nyan_ee2

nyan_ff2

perf

nyan_gg2

I've also merged a number of performance improvements, things generate much faster now.