CITE THIS NOTEBOOK: Hat tilings via HTPF equivalence by Brad Klee. Wolfram Community MAR 27 2023.

Rethinking The Data Structure

Once the novelty of our favorite new patterns wears off, we’ll want more performant computer code. The previous inflation function was just a quick and obvious solution. In this solution, every tile object contains a symbolic representation of a vector path from the global origin to the tile’s local origin. Instead we should make a GraphicsComplex-type object that allows relative specification of locations by keeping its own list of important vertices.
In[]:=
InitializeTiling[sym_]:=Association[​​"Tiles"->{Tile[sym][1,0]},​​"Vertices"->Association[​​0->{0,0},​​1->{0,{}}​​]​​]
In[]:=
Inflate[tiling_Association]:=Module[​​{nextTiling,nextTiles,nextVertices,max},​​nextTiles=Catenate[Map[Function[{type,or,dir},​​Thread[or->TileReplacements[type][dir]]​​][First[Head[#]],Sequence@@#]&,​​tiling["Tiles"]]​​];​​nextVertices=Union[​​{First[#],First[Last[#]]}&/@nextTiles];​​max=Max[Keys[tiling["Vertices"]]];​​nextVertices=Association[MapIndexed[#1->#2[[1]]+max&,​​nextVertices]];​​nextTiles=MapThread[Function[{or1,type,or2,dir},​​Tile[type][nextVertices[{or1,or2}],Mod[dir,6]]​​][#1,First[Head[#2]],Sequence@@#2]&,​​{First/@nextTiles,Last/@nextTiles}];​​nextVertices=Association[KeyValueMap[#2->#1&,​​nextVertices]];​​nextTiling=Association["Tiles"->nextTiles];​​nextTiling["Vertices"]=Merge[​​{tiling["Vertices"],nextVertices},First];​​Map[Set[nextTiling["Vertices",#],{​​First[nextTiling["Vertices",#]],​​Catenate[Last[nextTiling["Vertices",#]​​]/.edgeReps]​​}]&,Range[max]];​​nextTiling​​]
After data has been generated in the new form, we can project it back to the old form using this function:
In[]:=
SetVertices[tiling_]:=Module[{vertexMap,res},​​vertexMap=tiling["Vertices"];​​Map[Set[vertexMap[#],​​Plus[vertexMap[First[vertexMap[#]]],​​Total[Last[vertexMap[#]]/.vecReps]​​]]&,Range[Max[Keys[vertexMap]]]];​​Map[Function[{type,or,dir},​​Tile[type][{vertexMap[or]},dir]​​][First[Head[#]],Sequence@@#]&,​​tiling["Tiles"]]​​]
This function works somewhat like DirectedAcyclicEvaluate when calculating explicit vertex coordinates. It also uses a neat trick, double-wrapping local origins, to maintain backwards compatibility with previous code for depictions.
With the new data structure, now it takes less than two minutes to generate hundreds of thousands of data points:
In[]:=
AbsoluteTiming[​​allHPTFPatches=Map[Map[SetVertices[#]&,​​NestList[Inflate,InitializeTiling[#],6]]&,​​{"H","P","F"}];]
Out[]=
{60.6021,Null}
In[]:=
AbsoluteTiming[​​allHatPatches=Map[Catenate[​​ReplaceAll[#,Tile->ProjectHats]​​]&,allHPTFPatches,{2}];​​]
Out[]=
{26.247,Null}
Previously we could only go through four inflation operations in this amount of time. The advantage to faster computation is that we now get more terms when counting from output data. For example, none of these sequences counting HFPT tiles in progressively larger HFPT super-tiles are in OEIS:
In[]:=
MapThread[#2->#1&,{Map[Length,allHPTFPatches,{2}],​​{"H","P","F"}}]//Column
Out[]=
H{1,10,64,442,3025,20737,142129}
P{1,5,37,251,1723,11807,80929}
F{1,6,43,294,2017,13824,94753}
They should just be linear recurrences, and simple equations can be read off the substitution rules (later). We can also count the number of hat tiles in progressively larger HTPF super-tiles:
In[]:=
MapThread[#2->#1&,{Map[Length,allHatPatches,{2}],​​{"H","P","F"}}]//Column
Out[]=
H{4,25,169,1156,7921,54289,372100}
P{2,14,96,658,4510,30912,211874}
F{2,16,112,770,5280,36192,248066}
And these are found easily searching Online Encyclopedia of Integer sequences (A081068, A004187, A092521). However, these sequences are not exactly probing the hierarchical structure, so we want to dive deeper into details.
Edges of the hat tiling are vectors pointing to a vertex, so they can be numbered according to which vertex they point to. Then when vertex coordinates are computed, they can be backed out according to number. Vertex index determines coincidence with hexagonal lattice or not, which we will need to know to construct and measure parity and triality patterns. The next two functions overwrite two that we had before:
In[]:=
TileEdgeList[1][dir_:0,ref_:False,pt_:0​​]:=Module[{res,delta},​​delta=If[TrueQ[ref],1,0];​​res={​​e[1-delta][Sqrt[3],dir-1],​​e[2-delta][Sqrt[3],dir+1],​​e[3-delta][1,dir+4],​​e[4-delta][1,dir+6],​​e[5-delta][Sqrt[3],dir+3],​​e[6-delta][Sqrt[3],dir+5],​​e[7-delta][1,dir+8],​​e[8-delta][1,dir+6],​​e[9-delta][Sqrt[3],dir+9],​​e[10-delta][Sqrt[3],dir+7],​​e[11-delta][1,dir+10],​​e[12-delta][2,dir+12],​​e[0-delta][1,dir+14]​​};​​res=RotateLeft[res,pt];​​res=If[TrueQ[ref],​​ReplaceAll[Reverse[res],​​e[ind_][len_,x_Integer]:>e[Mod[ind,13]​​][len,Mod[-x,12]]],​​ReplaceAll[res,​​e[ind_][len_,x_Integer]:>e[Mod[ind,13]​​][len,Mod[x,12]]​​]​​];​​res​​]
In[]:=
TilePolygon[​​Tile[1][or_,dir_,ref_:False,pt_:0]​​]:=With[{vecs=ReplaceAll[​​TileEdgeList[1][dir,ref,pt]​​,e[ind_][len_,x_]:>Times[​​len,ReIm[Exp[IPi/6x]]]]},​​Polygon[FoldList[Plus,or,vecs]]​​]
The next function doesn’t look like too much, but it will allow us to do a lot of deep analysis where we look for the hat tiling (or PHTF) as a color pattern on the hexagonal lattice:
In[]:=
HatVertexMap[​​Tile[1][or_,dir_,ref_:False,pt_:0]​​]:=Module[{edges,vecs,res},​​edges=TileEdgeList[1][dir,ref,pt];​​vecs=ReplaceAll[edges,​​e[ind_][len_,x_]:>Times[​​len,ReIm[Exp[IPi/6x]]​​]];​​res=MapThread[Rule,{Head/@edges,​​Rest[FoldList[Plus,or,vecs]]}​​];​​res=Append[Reverse/@res,Mean[{​​e[1],e[5],e[9]​​}/.res]->c[0]];​​res=Select[res,MemberQ[​​Append[e/@{1,3,5,7,9,11,12},c[0]],​​Last[#]]&];​​Map[First[#]->{Last[#],ref,Mod[dir,12]}&,res]​​]
An example output looks like this:
The tile orientation is carried along with exact location of vertices that fall onto the hexagonal lattice. With multiple tiles in a list, complete vertex figures can be obtained by grouping First → Last and then checking a Total. This allows us to compute the total number of hat-tiling vertex figures over the hexagonal lattice is 124:
If we ignore the rotation number, the number of keys reduces to 22 (thus the 22-color pattern above):
Also ignoring the reflection bit, we get only 14 different keys:
In our coded notation, those keys are:

Color patterns: Initial impressions

Previously, we had rushed forward to print off a few interesting hexagonal patterns, without providing the click to copy code. We want to give away computable definitions for the best patterns we’ve found so far. Here they are for three of our previous results:
Notice that all three of these functions call to HatVertexap. The inversion pattern (from Ed Pegg) is not on the same hexagonal lattice as the other two patterns, so we need to be careful about that when writing new depiction functions:
Here is Ed’s pattern with hat overlay
And with HTPF overlay:
Here is the missing centers parity pattern with both overlays:
It seems visually obvious that red edges of PTFH tiling fall on blue tiles only, and from the parity pattern to the triality pattern, we split the blue symbol into blue and dark green:
And now the dark green hexagons always seem to be coincident with a red FHTP edge. This is the nice property that should makes the triality pattern more readily prove-able MLD (mutually locally derivable) to the hat tiling. At present, we have no idea if Ed’s parity pattern is MLD to the hat tiling, and we haven’t noticed any structural tells. It’s good enough having the triality pattern, but in the next section we will explore more parity patterns with the hope of finding something MLD to hat.

More parity patterns

For example we can plot 14 different parity patterns by turning one subset on and leaving the rest off:
This is immediately suggestive, but also not entirely helpful. We really want to start from the triality pattern by leaving on all dark green symbols and turning off all light green symbols. This leaves the light blue symbols, which we can turn on or off selectively using a 5-bit boolean vector. The case table is here:
On inspecting the results, it looks like we have two winners: 16370 and 16372. Let’s see them in more detail, and with an overlay for the PTFH tiling:
The perfect rotational symmetry of 16372 forbids that it can be MLD to the hat tiling. We can always blow the triangle up to infinity by repeated inflation, and if we do so, we can not recover the A/B edge parity split uniquely. Nevertheless, 16372 is a very interesting pattern LD from the Hat, which looks to feature a very regular plane filling ternary tree. And this tree seems to be the best completion of the fragments from the triality pattern 16320.
Now, pattern 16370 really stands out, because it could be a tiling by three polyiamonds. Let’s first look at a larger patch:
From this patch, we read off the three tiles:
As a sanity check, let’s obtain the nearest neighbor graph for the white interiors:
Then we can roughly filter the pieces using IsomorphicGraphQ:
Here, the last element only appears due to edge effects. Just by looking at the overlay, it’s almost obvious that this tiling by three parity tiles is MLD to the hat tiling. The 3-tile parity pattern is not really that different from the triality pattern, so this shouldn’t be too much of a surprise. We still don’t know about the bigger question: Does the set of three parity tiles only allow aperiodic configurations MLD to Hat tiling? More on this later...

The triality trees growth sequence

In the previous two sections we introduced the triality pattern, and described how it’s potentially important to understanding structure of the hat tiling. In particular, we observed that the dark green signals break the A/B edge symmetry, and provide a means to orient the tiling. Now we will take a closer look at the “triality trees”, by extracting them from parity coloring 16320. Here is the function to extract the trees:
The result of this function can be used to depict the trees:
Up to graph isomorphism, the trees can be extracted as:
And these trees satisfy a nice inclusion property:
We can count their vertices manually:
Or using an extra method of the filter function, we can map the count over some a variety of patches:
And attempt to continue the sequence for H patches only:
And attempt to continue the sequence for H patches only :
And finally, print the graph with 295 vertices:
We can try to calculate one or maybe two more terms, but I think this will take a while: