Mainly for Raul, as nobody else has commented,  at least publicly.

OK - I didn't mean to spend much time on this,  but I started wondering how a
coordinate representation would work.

The 3-d boolean representation is more intuitive,  but one advantage of the
coordinate approach is that the representation is less ragged,  at least when we're dealing with all n-polycubes ;  I haven't yet dealt with sets of polycubes with different numbers of component cubes.  So the set of 29 penta-polycubes
has shape 29 5 3, for example.

The main problem with this approach is that I didn't quite solve the canonicity
task.  "Rotating" (and sorting) the coords does most of what's needed,  but
I've resorted to flipping to binary,  applying Raul's "canon" routine,  then flipping back to coordinates.  Naturally this adds to the computation time. It would
be much neater to remove all the isomers at the coordinate level.

So here's quite a long screed copied from my current script,  which includes Raul's definitions.  I'll keep those in for completeness but will remove his inline comments;  these definitions appear first. Apologies if I've omitted anything.

NB. https://en.wikipedia.org/wiki/Polycube
rotx=: |."2@(2 1&|:)  NB. rotate in the yz plane about the x axis
roty=: |.@(1 0 2&|:)  NB. rotate in the xz plane about the y axis
rotz=: |.@(2 1 0&|:)  NB. rotate in the xy plane about the z axis

rotall=: {{
  'X Y Z'=: y
  rotx^:X roty^:Y rotz^:Z x
}}"3 1&(4 4 4#:(i.20),24+i.4)

extend=: {{
  Y=. (-2+$y){.(1+$y){.y
  new=. ($#:I.@,)Y < (_1&|. +. 1&|. +. 1&|."2 +. _1&|."2 +. 1&|."1 +. _1&|."1) Y
  ~.new {{canon 1 (,:x)} y}}"1 3 Y
}}

offset=: {{ 1 i.~ +./+./"2 x |: *y }}
trim=: {{ (0 offset y)}."3 (1 offset y)}."2 (2 offset y)}."1 y }}
rtrim=: trim&.|.&.:(|."2)&.:(|."1)

canon=: {{ rtrim {. (/: +/@p:@I.@,"3) rotall trim y }}^:2

extendall=: {{ ~.;<@extend"3 y }}

NB.    #@> extendall&.>^:(i.8)<1 1 1 1$1
NB. 1 1 2 8 29 166 1023 6922

NB. My stuff follows (the copy & paste has removed my indentations!)
NB. I've repaired a couple of line-wraps - others might lurk!
NB. =============================================================
NB. Prepare all 24 general 3x3 rotation matrices

mm =: +/ . * matrix multiply

I3 =: =i.3 3x3 identity matrix


NB. define some helper matrices

Rx =: >1 0 0; 0 0 _1; 0 1 0 NB. pi/2 rotation around x axis

Ry =: _1 |. _1 |."1 Rx NB. y

Rz =: 1 |. 1 |."1 Rx NB. z

Rx3=: Rx mm Rx2=: mm~ Rx

Ry3=: Rx mm Ry2=: mm~ Ry

Rz3=: Rx mm Rz2=: mm~ Rz

Rxs=: I3,Rx,Rx2,:Rx3 NB. rotations by 0, pi/2, pi, 3pi/2 around x axis

Rys=: I3,Ry,Ry2,:Ry3 NB. rotations by 0, pi/2, pi, 3pi/2 around y axis

Rzs=: I3,Rz,Rz2,:Rz3 NB. rotations by 0, pi/2, pi, 3pi/2 around z axis


NB. combine them to generate all 24 rotation matrices


irots =: (4 4 4#:(i.20),24+i.4)


makeRots =: {{

r =. {{1 (0 1 2 ,each y) } 3 3$0}}"1 tap 3 NB. all 6 permutations of 3 axes' labels ==> 3 x 1s in 3x3 matrices

s =. ~./:~,/1 _1,"0 1/,/(,"0)/~1 _1 NB. 8 sets of triples of signs

rs =. ,/s *"1/ r NB. output 48 (!) matrices

}}


NB. Can't see yet how to limit them to 24 except by comparison with Raul's rotall results on

NB. an irregular set of 3 points

# q=. {{/:~ {{y +"1 <./y}} y -"1 <./ y}}each ($#:I.@:,)each <"3 rotall 1 (0 0 0;1 1 1;1 2 3) } 2 3 4$0 24

#r =. {{ /:~ {{y +"1 <./y}} y -"1 <./ y}}each<"2(0 0 0,1 1 1,:1 2 3) mm "2 Rots =: makeRots''48

+/ok =. r e. q

24

NB. Global set of rotation matrices to help with finding canonical coordinate representations

Rots =: (q i.~ ok#r){ ok# Rots NB. !!!!!!!


NB. Demonstration that the 24 Rots can be used for rotation analogously to Raul's rotall function

NB. compare with Raul's results on all flips of these 3 points - not face-adjacent cubes as it happens!

NB. but that doesn't matter much

NB. limited to 1st 16 for easier display...

NB. 16{.{{/:~ {{y +"1 <./y}} y -"1 <./ y}}each ($#:I.@:,)each <"3 rotall 1 (0 0 0;1 1 1;1 2 3) } 2 3 4$0

NB. ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐

NB. │0 0 0│0 2 1│0 1 2│0 0 1│0 1 3│0 0 1│0 0 0│0 3 0│0 0 3│0 2 0│0 2 3│0 0 0│0 1 0│0 0 0│0 0 3│0 3 1│

NB. │1 1 1│2 1 1│0 2 0│1 1 0│1 1 1│1 2 1│1 0 2│1 1 0│0 1 1│1 1 1│1 0 0│2 1 0│1 0 1│1 1 1│1 1 2│1 2 0│

NB. │1 2 3│3 0 0│1 0 3│3 2 0│2 0 0│2 3 0│2 1 3│2 0 1│1 2 0│3 0 1│1 1 2│3 2 1│2 0 3│2 3 1│2 1 0│2 0 0│

NB. └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘


NB. 16{.{{ /:~ {{y +"1 <./y}} y -"1 <./ y}}each<"2(0 0 0,1 1 1,:1 2 3) mm "2 Rots

NB. ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐

NB. │0 0 0│0 2 1│0 1 2│0 0 1│0 1 3│0 0 1│0 0 0│0 3 0│0 0 3│0 2 0│0 2 3│0 0 0│0 1 0│0 0 0│0 0 3│0 3 1│

NB. │1 1 1│2 1 1│0 2 0│1 1 0│1 1 1│1 2 1│1 0 2│1 1 0│0 1 1│1 1 1│1 0 0│2 1 0│1 0 1│1 1 1│1 1 2│1 2 0│

NB. │1 2 3│3 0 0│1 0 3│3 2 0│2 0 0│2 3 0│2 1 3│2 0 1│1 2 0│3 0 1│1 1 2│3 2 1│2 0 3│2 3 1│2 1 0│2 0 0│

NB. └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘


NB. Like trim - easier -

NB. eg to cut planes x < 2 , just subtract 2 0 0

tc =: trimcoords =: {{ /:~ y -"1 <./ y}}


NB. convert binary array representation to coord rep

b2c =: ($#:I.@:,) :. c2b


NB. convert coord rep to binary array

c2b =: {{

1 (<"1 y) } 0$~ >: >./ y

}} :. b2c


NB. global for add3 (function def follows)

ADD3 =: _1 0 0,1 0 0,0 _1 0, 0 1 0, 0 0 _1,: 0 0 1


NB. append new cubes by means of their 3-coords

NB. too loopy but ok for now...

add3 =: {{

out =. 0$~ 0,1 0+$y

head=. 0$~0, {:$y

tail=. y

NB. left as loop for now!!!

for_i. i.#y do.

iy =. i{y

new =. iy +"1 ADD3

if. +/ ok =. -. new e. y do.

for_newi. ok#new do.

out =. out, /:~ head, newi, tail

end.

end.

head =. head, iy

tail =. }. tail

end.

NB. "trim" coords to help with checking canonicity

tc"2 ~. out

}}


mycanon =: {{ ~. mycanon1 "2 ~. add3 y }}


mycanon1 =: {{

if. 1 < # q =. Rots ~.@:(tc@:mm"2~)y do.

q{~{./:@:((+/@:p:@:I.@:,)"2) q

else.

{.q

end.

}}


myexpand =: {{ r =. ~. /:~,/ mycanon"2 y

if. 0 = +/, {.r do. r =. }. r end.}}


NB. Performance - starts ok!


NB. Build Raul's extended sets for comparison purposes, and as seeds

NB. uncomment these to run!

NB. e7=.extendall each^:6<1 1 1 1$1

NB. e6=.extendall each^:5<1 1 1 1$1

NB. e5=.extendall each^:4<1 1 1 1$1

NB. e4=.extendall each^:3<1 1 1 1$1

NB. e3=.extendall each^:2<1 1 1 1$1

NB. e2=.extendall each^:1<1 1 1 1$1


NB. Compare results from "myexpand", run on Raul's arrays turned into coordinate lists, with his own results


NB. timer'$ myexpand b2c"3;e2'  NB. b2c"3 turns binary array ;e2 into sets of coords

NB. ┌─────────┬─────┐

NB. │0.0019989│2 3 3│

NB. └─────────┴─────┘

NB. timer'#extendall >e2'

NB. ┌─────────┬─┐

NB. │0.0059967│2│

NB. └─────────┴─┘


NB. timer'$ myexpand b2c"3;e3'

NB. ┌──────────┬─────┐

NB. │0.00800323│8 4 3│

NB. └──────────┴─────┘

NB. timer'#extendall >e3'

NB. ┌─────────┬─┐

NB. │0.0130005│8│

NB. └─────────┴─┘


NB. timer'$ myexpand b2c"3;e4'

NB. ┌─────────┬──────┐

NB. │0.0279999│29 5 3│

NB. └─────────┴──────┘

NB. timer'#extendall >e4'

NB. ┌─────────┬──┐

NB. │0.0589981│29│

NB. └─────────┴──┘


NB. timer'$q6=: myexpand b2c"3;e5'

NB. ┌────────┬───────┐

NB. │0.129005│169 6 3│ NB. too big - whoops!

NB. └────────┴───────┘

NB. Raul's canon can clean it up!!!

NB. timer'$bq6=:~.>canon@:c2b each <"2 q6 '

NB. ┌─────────┬─────────┐

NB. │0.0830002│166 3 4 6│

NB. └─────────┴─────────┘

NB. timer'$cq6 =: b2c"3 bq6' NB. restoring to coord form

NB. ┌───────────┬───────┐

NB. │0.000999451│166 6 3│

NB. └───────────┴───────┘

NB. timer'#extendall >e5'

NB. ┌────────┬───┐

NB. │0.276001│166│

NB. └────────┴───┘

NB. Wrap up the clean-up ...

NB. timer'${{~.canon&.:c2b "2 myexpand b2c"3 y}};e5 '

NB. ┌────────┬───────┐

NB. │0.230003│166 6 3│

NB. └────────┴───────┘


NB. So incorporate this clean-up in a new routine:


myexpanda =: {{~.canon&.:c2b "2 myexpand b2c"3 y}}


NB. timer'$myexpanda ;e5'

NB. ┌────────┬───────┐

NB. │0.142998│166 6 3│

NB. └────────┴───────┘


NB. timer'$myexpanda ;e6'

NB. ┌────────┬────────┐

NB. │0.976997│1023 7 3│

NB. └────────┴────────┘

NB. timer'#extendall >e6'

NB. ┌─────┬────┐

NB. │2.119│1023│

NB. └─────┴────┘


NB. timer'$myexpanda ;e7'

NB. ┌─────┬────────┐

NB. │6.975│6922 8 3│

NB. └─────┴────────┘

NB. timer'#extendall >e7'

NB. ┌──────┬────┐

NB. │16.231│6922│

NB. └──────┴────┘


Sorry for the length of this offering,


Mike



On 15/07/2023 22:03, Raul Miller wrote:
https://en.wikipedia.org/wiki/Polycube

Today, I've been playing with polycubes (which are polyhedrons formed
from adjacent cubes).

This requires both a representation -- a rank 3 bit array works
nicely, and a mechanism for working with cubic symmetry. For that, I
went with:

rotx=: |."2@(2 1&|:) NB. rotate in the yz plane about the x axis roty=: |.@(1 0 2&|:) NB. rotate in the xz plane about the y axis rotz=: |.@(2 1 0&|:) NB. rotate in the xy plane about the z axis rotall=: {{ 'X Y Z'=. y rotx^:X roty^:Y rotz^:Z x }}"3 1&(4 4 4#:(i.20),24+i.4)

Here, rotall gives all 24 rotations of cubic symmetry for a given
collection of 3d cubes represented by a bit array.

I decided to use a brute force mechanism to generate polycubes. In
other words, pad the array representation and add a single cube in
each of the locations adjacent to an existing cube:

extend=: {{
   Y=. (-2+$y){.(1+$y){.y
   new=. ($#:I.@,)Y < (_1&|. +. 1&|. +. 1&|."2 +. _1&|."2 +. 1&|."1 +.
_1&|."1) Y
   ~.new {{canon 1 (,:x)} y}}"1 3 Y
}}

Here, Y is the y argument with an extra 0 of padding in all
directions. And 'new' is the list of indices of empty locations
adjacent to cube locations.

I also need a verb (canon) to reduce these generated polycubes to
canonical form. A lot of this is removing unnecessary rows, columns
and planes of zeros. The rest of it is picking arbitrarily (but
consistently) from the 24 possible symmetric rotations:

offset=: {{ 1 i.~ +./+./"2 x |: *y }}
trim=: {{ (0 offset y)}."3 (1 offset y)}."2 (2 offset y)}."1 y }}
rtrim=: trim&.|.&.:(|."2)&.:(|."1)

canon=: {{ rtrim {. (/: +/@p:@I.@,"3) rotall trim y }}^:2

Finally, I want to work with all polycubes of a specific order, and to
get larger batch I can use;

extendall=: {{ ~.;<@extend"3 y }}

And, testing, this works:

    #@> extendall&.>^:(i.8)<1 1 1 1$1
1 1 2 8 29 166 1023 6922

But, this is slow -- since I'm brute forcing all possibilities then
discarding duplicates, I need almost seven seconds on the little
laptop I'm currently using, to generate the above sequence.

(Also, for visualization of individual polycubes,
https://code.jsoftware.com/wiki/Scripts/Plot_3D  works passibly well.)

Does anyone here have the geometric insight to improve on this approach?

Thanks,

----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to