/bng2/Perl2/HNauty.pm

https://code.google.com/ · Perl · 1010 lines · 861 code · 97 blank · 52 comment · 146 complexity · 6481de2ca3f6813b1f03a8dfa508e84b MD5 · raw file

  1. #!/usr/bin/perl
  2. package HNauty;
  3. require Exporter;
  4. our @ISA = qw(Exporter);
  5. our @EXPORT = qw(HNauty deepCopy adj_permute get_adj_str);
  6. #our @EXPORT_OK= qw(deepCopy);
  7. our $VERSION = 1.00;
  8. # NOTE: Many undeclared global symbols
  9. #use strict;
  10. ########### HNauty program
  11. # list of subroutines:
  12. # adj_permute
  13. # intersection
  14. # is_discrete
  15. # lex_ordered
  16. # HNauty
  17. # partition_meet
  18. # partition_value
  19. # pfixp
  20. # refinement
  21. # Run
  22. # update_automorphisms
  23. #Run();
  24. sub Run {
  25. use Data::Dumper;
  26. my $le_part = [];
  27. push @$le_part, [ 0, 1, 2, 3 ], [ 4, 5, 6, 7 ], [8], [9], [10];
  28. my $debug_partition_value = 0;
  29. if ($debug_partition_value) {
  30. my $value = partition_value($le_part);
  31. print "@$value\n";
  32. }
  33. my $le_adj = {
  34. 0 => { 6 => [0], 7 => [0], 8 => [0] },
  35. 1 => { 9 => [0], 10 => [0], 11 => [0] },
  36. 2 => { 9 => [0], 10 => [0], 11 => [0] },
  37. 3 => { 6 => [0], 12 => [0], 14 => [0] },
  38. 4 => { 7 => [0], 12 => [0], 13 => [0] },
  39. 5 => { 8 => [0], 13 => [0], 14 => [0] },
  40. 6 => { 3 => [0], 0 => [0] },
  41. 7 => { 4 => [0], 0 => [0] },
  42. 8 => { 0 => [0], 5 => [0] },
  43. 9 => { 2 => [0], 1 => [0] },
  44. 10 => { 1 => [0], 2 => [0] },
  45. 11 => { 1 => [0], 2 => [0] },
  46. 12 => { 3 => [0], 4 => [0] },
  47. 13 => { 5 => [0], 4 => [0] },
  48. 14 => { 3 => [0], 5 => [0] }
  49. };
  50. my $adj_out = deepCopy($le_adj);
  51. # define adj_in - note that this example is not really directed
  52. my ( $i, $j );
  53. $adj_in = {};
  54. for $i ( keys %$adj_out ) {
  55. for $j ( keys %{ $adj_out->{$i} } ) {
  56. @{ $adj_in->{$j}{$i} } = @{ $adj_out->{$i}{$j} };
  57. }
  58. }
  59. my $the_partition = [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ] ];
  60. my $le_perm = {
  61. 0 => 1,
  62. 1 => 2,
  63. 2 => 3,
  64. 3 => 4,
  65. 4 => 5,
  66. 5 => 6,
  67. 6 => 7,
  68. 7 => 8,
  69. 8 => 9,
  70. 9 => 10,
  71. 10 => 11,
  72. 11 => 12,
  73. 12 => 13,
  74. 13 => 14,
  75. 14 => 0
  76. };
  77. my $debug_adj_permute = 0;
  78. if ($debug_adj_permute) {
  79. my $new_adj = adj_permute( $le_adj, $le_perm );
  80. print Data::Dumper->Dump( [$new_adj] );
  81. }
  82. my $term = [
  83. [1], [2], [3], [4], [5], [6], [7], [8],
  84. [9], [10], [11], [12], [13], [14], [0]
  85. ];
  86. my $c_perm = [
  87. [0], [1], [2], [3], [4], [5], [6], [7],
  88. [8], [9], [10], [11], [12], [13], [14]
  89. ];
  90. my $debug_get_adj_str = 0;
  91. if ($debug_get_adj_str) {
  92. my $adj_infor = get_adj_str( $le_adj, $term );
  93. print Data::Dumper->Dump( [$adj_infor] );
  94. }
  95. my $debug_lex_ordered = 0;
  96. if ($debug_lex_ordered) {
  97. my $new_adj = adj_permute( $le_adj, $le_perm );
  98. # print Data::Dumper->Dump([$le_adj, $new_adj]);
  99. my $le_a = get_adj_str( $le_adj, $c_perm );
  100. my $le_b = get_adj_str( $new_adj, $c_perm );
  101. my $result = lex_ordered( $le_a, $le_b );
  102. print "@$result\n";
  103. $result = lex_ordered( [ 0, 1, 2, 2 ], [ 0, 1, 2, 2, 2 ] );
  104. print "@$result\n";
  105. $result = lex_ordered( [ 2, 1, 99 ], [ 2, 1, 0 ] );
  106. print "@$result\n";
  107. $result =
  108. lex_ordered( [ [ 1, 2, 3, 4 ], [2] ], [ [ 1, 2, 3, 4 ], [ 1, 10 ] ] );
  109. print "@$result\n";
  110. }
  111. my $debug_is_discrete = 0;
  112. if ($debug_is_discrete) {
  113. my $result = is_discrete($c_perm);
  114. print "$result\n";
  115. $result = is_discrete( [ [ 0, 1 ], [2], [3] ] );
  116. print "$result\n";
  117. }
  118. my $debug_intersection = 0;
  119. if ($debug_intersection) {
  120. my $result = intersection( [ 0, 1, 2, 3, 4, 5 ], [ 5, 1, 7, 8, 9, 0 ] );
  121. print "@$result\n";
  122. print Data::Dumper->Dump( [$result] );
  123. }
  124. my $debug_partition_meet = 0;
  125. if ($debug_partition_meet) {
  126. my $result = partition_meet(
  127. [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ],
  128. [
  129. [ [5], [ 1, 2, 3, 4, 6, 7, 8 ], [ 9, 0 ] ],
  130. [ [ 0, 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ],
  131. [ [ 9, 0, 4, 1, 3 ], [ 2, 5, 6, 7 ], [8] ]
  132. ]
  133. );
  134. print Data::Dumper->Dump( [$result] );
  135. }
  136. my $newp = [ [ 6, 11, 7, 9, 12, 14, 8, 10, 13 ], [4], [ 1, 3, 0, 2, 5 ] ];
  137. my $autos = [];
  138. my $otra_perm = {
  139. 1 => 1,
  140. 2 => 3,
  141. 3 => 4,
  142. 4 => 2,
  143. 5 => 6,
  144. 6 => 5,
  145. 7 => 7,
  146. 8 => 9,
  147. 9 => 10,
  148. 10 => 11,
  149. 11 => 12,
  150. 12 => 13,
  151. 13 => 8,
  152. 14 => 14,
  153. 0 => 0
  154. };
  155. my $fix;
  156. my $orbit_reps;
  157. my $otra_part =
  158. [ [0], [ 10, 2, 3 ], [1] . [ 4, 5, 6 ], [7], [ 8, 9, 11, 12, 13 ], [14] ];
  159. my $debug_update_automorphisms = 0;
  160. if ($debug_update_automorphisms) {
  161. ( $fix, $orbit_reps, $autos ) = update_automorphisms( $autos, $le_perm );
  162. print Data::Dumper->Dump( [ $fix, $orbit_reps, $autos ] );
  163. ( $fix, $orbit_reps, $autos ) = update_automorphisms( $autos, $otra_perm );
  164. print Data::Dumper->Dump( [ $fix, $orbit_reps, $autos ] );
  165. }
  166. my $debug_pfixp = 0;
  167. if ($debug_pfixp) {
  168. ( $fix, $orbit_reps, $autos ) = update_automorphisms( $autos, $otra_perm );
  169. print Data::Dumper->Dump( [$autos] );
  170. print pfixp( $c_perm, $fix ), "\n\n";
  171. print pfixp( $otra_part, $fix ), "\n\n";
  172. }
  173. my $some_cell = [ 0, 1, 2, 3, 4 ];
  174. my $debug_update_cell = 0;
  175. if ($debug_update_cell) {
  176. ( $fix, $orbit_reps, $autos ) = update_automorphisms( $autos, $le_perm );
  177. ( $fix, $orbit_reps, $autos ) = update_automorphisms( $autos, $otra_perm );
  178. print Data::Dumper->Dump( [$autos] );
  179. my $result = update_cell( $otra_part, $some_cell, $autos );
  180. print @$result;
  181. }
  182. my $he_out = {
  183. 0 => { 1 => [1], 12 => [0] },
  184. 1 => { 0 => [1], 14 => [0] },
  185. 2 => { 3 => [1], 14 => [0] },
  186. 3 => { 2 => [1], 13 => [0] },
  187. 4 => { 5 => [1], 14 => [0] },
  188. 5 => { 4 => [1], 15 => [0] },
  189. 6 => { 15 => [0], 7 => [1] },
  190. 7 => { 17 => [0], 6 => [1] },
  191. 8 => { 9 => [1], 17 => [0] },
  192. 9 => { 8 => [1], 20 => [0] },
  193. 10 => { 12 => [0] },
  194. 11 => { 12 => [0] },
  195. 16 => { 17 => [0] },
  196. 18 => { 20 => [0] },
  197. 19 => { 20 => [0] }
  198. };
  199. # define he_in
  200. $he_in = {};
  201. for $i ( keys %$he_out ) {
  202. for $j ( keys %{ $he_out->{$i} } ) {
  203. @{ $he_in->{$j}{$i} } = @{ $he_out->{$i}{$j} };
  204. }
  205. }
  206. my $he_part = [
  207. [
  208. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
  209. ]
  210. ];
  211. my $he_perm = {
  212. 0 => 1,
  213. 1 => 2,
  214. 2 => 20,
  215. 20 => 3,
  216. 3 => 19,
  217. 19 => 0,
  218. 4 => 5,
  219. 5 => 6,
  220. 6 => 7,
  221. 7 => 4,
  222. 8 => 8,
  223. 9 => 10,
  224. 10 => 9,
  225. 11 => 12,
  226. 12 => 13,
  227. 13 => 14,
  228. 14 => 15,
  229. 15 => 16,
  230. 16 => 17,
  231. 17 => 18,
  232. 18 => 11
  233. };
  234. my $debug_refinement = 0;
  235. if ($debug_refinement) {
  236. my $result;
  237. $result = refinement( $adj_in, $adj_out, $the_partition, $the_partition );
  238. print Data::Dumper->Dump( [$result] );
  239. print "#############\n";
  240. print "#############\n";
  241. $result = refinement( $adj_in, $adj_out, $newp, [ [4] ] );
  242. print Data::Dumper->Dump( [$result] );
  243. print "#############\n";
  244. print "#############\n";
  245. $result = refinement( $he_out, $he_in, $he_part, $he_part );
  246. print Data::Dumper->Dump( [$result] );
  247. }
  248. my $debug_nauty = 1;
  249. if ($debug_nauty) {
  250. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  251. my $adj_a = get_adj_str( $adj_out, @$result[0] );
  252. print @$adj_a, "\n\n";
  253. # permute
  254. $adj_in = adj_permute( $adj_in, $le_perm );
  255. $adj_out = adj_permute( $adj_out, $le_perm );
  256. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  257. my $adj_b = get_adj_str( $adj_out, @$result[0] );
  258. print @$adj_b;
  259. $is_equal = lex_ordered( $adj_a, $adj_b );
  260. if ( @$is_equal[0] eq 'eq' ) { print "\nThey are equal!\n\n"; }
  261. # permute
  262. $adj_in = adj_permute( $adj_in, $otra_perm );
  263. $adj_out = adj_permute( $adj_out, $otra_perm );
  264. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  265. my $adj_c = get_adj_str( $adj_out, @$result[0] );
  266. print @$adj_c;
  267. $is_equal = lex_ordered( $adj_c, $adj_b );
  268. if ( @$is_equal[0] eq 'eq' ) { print "\nThey are equal!\n\n"; }
  269. print "\n\n\nNow Hierarchical Example!!!\n";
  270. $adj_in = $he_in;
  271. $adj_out = $he_out;
  272. $the_partition = $he_part;
  273. $le_perm = $he_perm;
  274. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  275. $adj_a = get_adj_str( $adj_out, @$result[0] );
  276. print @$adj_a, "\n\n";
  277. # permute
  278. $adj_in = adj_permute( $adj_in, $le_perm );
  279. $adj_out = adj_permute( $adj_out, $le_perm );
  280. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  281. $adj_b = get_adj_str( $adj_out, @$result[0] );
  282. print @$adj_b;
  283. $is_equal = lex_ordered( $adj_a, $adj_b );
  284. if ( @$is_equal[0] eq 'eq' ) { print "\nThey are equal!\n\n"; }
  285. # permute
  286. $adj_in = adj_permute( $adj_in, $le_perm );
  287. $adj_out = adj_permute( $adj_out, $le_perm );
  288. @$result = HNauty( $adj_in, $adj_out, $the_partition );
  289. $adj_c = get_adj_str( $adj_out, @$result[0] );
  290. print @$adj_c;
  291. $is_equal = lex_ordered( $adj_c, $adj_b );
  292. if ( @$is_equal[0] eq 'eq' ) { print "\nThey are equal!\n\n"; }
  293. }
  294. }
  295. sub update_cell {
  296. my $node = shift; #array
  297. my $cell = shift; #array
  298. my $perms = shift; #array
  299. my $i;
  300. # $fixing = [];
  301. for $i ( 0 .. $#{$perms} ) {
  302. if ( pfixp( $node, @{ @$perms[$i] }[0] ) ) {
  303. $cell = intersection( $cell, @{ @$perms[$i] }[1] );
  304. }
  305. }
  306. return $cell;
  307. }
  308. sub pfixp {
  309. my $partition = shift; #array
  310. my $perm_fix = shift; #array
  311. my ( $i, $fixed );
  312. $fixed = 1;
  313. $i = 0;
  314. while ( $fixed and $i < scalar @$partition ) {
  315. if ( scalar @{ @$partition[$i] } == 1
  316. and ( not defined( $perm_fix->{ @{ @$partition[$i] }[0] } ) ) )
  317. {
  318. $fixed = 0;
  319. }
  320. $i++;
  321. }
  322. return $fixed;
  323. }
  324. sub update_automorphisms {
  325. my $autos = shift; #array
  326. my $perm = shift; #hash
  327. my $un_used; #hash
  328. my $fix = {}; #hash
  329. my $orbit_reps = []; # array
  330. my $something; #array
  331. my $cycle;
  332. my ( $element, $done, $cycle_done );
  333. $un_used = deepCopy($perm);
  334. $done = 0;
  335. while ( not $done ) {
  336. @$something = sort keys %$un_used;
  337. if ( scalar @$something == 0 ) {
  338. $done = 1;
  339. }
  340. else {
  341. $element = @$something[0];
  342. $cycle = [$element];
  343. $cycle_done = 0;
  344. while ( not $cycle_done ) {
  345. delete( $un_used->{$element} );
  346. $element = $perm->{$element};
  347. if ( not defined( $un_used->{$element} ) ) {
  348. $cycle_done = 1;
  349. }
  350. else {
  351. push @$cycle, $element;
  352. }
  353. }
  354. @$cycle = sort @$cycle;
  355. push @$orbit_reps, @$cycle[0];
  356. if ( scalar @$cycle == 1 ) {
  357. $fix->{ @$cycle[0] } = 1;
  358. }
  359. }
  360. }
  361. push @$autos, [ $fix, $orbit_reps ];
  362. return ( $fix, $orbit_reps, $autos );
  363. }
  364. sub partition_value {
  365. my $part = shift; #array
  366. my $indicator; #array
  367. my $tmp; #hash
  368. my $other; # array
  369. my $i;
  370. $tmp = {};
  371. for $j (@$part) {
  372. $tmp->{ scalar @$j } += 1;
  373. }
  374. $indicator = [];
  375. @$other = sort keys %$tmp;
  376. for $j ( 1 .. @$other[ $#{$other} ] ) {
  377. if ( defined( $tmp->{$j} ) ) {
  378. push @$indicator, $tmp->{$j};
  379. }
  380. else {
  381. push @$indicator, 0;
  382. }
  383. }
  384. return $indicator;
  385. }
  386. sub preprocess { }
  387. sub adj_permute {
  388. my $adj = shift; #hash adj_out
  389. my $perm = shift; #hash
  390. my $new_adj; #hash
  391. my ( $i, $j, $new_i, $new_j );
  392. $new_adj = {};
  393. for $i ( keys %$adj ) {
  394. $new_i = $perm->{$i};
  395. $new_adj->{$new_i} = {};
  396. for $j ( keys %{ $adj->{$i} } ) {
  397. $new_j = $perm->{$j};
  398. @{ $new_adj->{$new_i}{$new_j} } = @{ $adj->{$i}{$j} };
  399. }
  400. }
  401. return $new_adj;
  402. }
  403. sub get_adj_str {
  404. my $adj = shift; #hash (adj_out)
  405. my $part = shift; #array or $hsah
  406. my $perm; #hash
  407. my $new_adj; # array
  408. my $adj_info; # unfortunately also an array
  409. my ( $i, $j, $value, $e, $length );
  410. # $le_adj = [];
  411. if ( ref $part eq 'ARRAY' ) {
  412. for $i ( 0 .. $#{$part} ) {
  413. $perm->{ @{ @$part[$i] }[0] } = $i;
  414. }
  415. }
  416. else {
  417. $perm = $part;
  418. }
  419. $length = ( scalar keys %$adj ) - 1;
  420. $new_adj = adj_permute( $adj, $perm );
  421. for $i ( 0 .. $length ) {
  422. for $j ( 0 .. $length ) {
  423. if ( defined( $new_adj->{$i}{$j} ) ) {
  424. $value = 0;
  425. for $e ( @{ $new_adj->{$i}{$j} } ) {
  426. $value += 2**$e;
  427. }
  428. push @$adj_info, $value;
  429. }
  430. else {
  431. push @$adj_info, 0;
  432. }
  433. }
  434. }
  435. return $adj_info;
  436. }
  437. sub lex_ordered {
  438. # in fact returns appropriate counter value in this case..
  439. my $le_primier = shift; # array
  440. my $le_deuxieme = shift; #array
  441. my $tmp; #array
  442. my ( $i, $status, $length, $result );
  443. @$tmp = sort ( scalar @$le_primier, scalar @$le_deuxieme );
  444. $length = @$tmp[0];
  445. $status = ['eq'];
  446. $i = 0;
  447. while ( @$status[0] eq 'eq' && $i < $length ) {
  448. if ( ref @$le_primier[$i] eq 'ARRAY' ) {
  449. $status = lex_ordered( @$le_primier[$i], @$le_deuxieme[$i] );
  450. if ( @$status[0] eq 'eq' ) {
  451. $i++;
  452. }
  453. }
  454. else {
  455. if ( @$le_primier[$i] > @$le_deuxieme[$i] ) {
  456. @$status[0] = 'gt';
  457. }
  458. elsif ( @$le_primier[$i] < @$le_deuxieme[$i] ) {
  459. @$status[0] = 'lt';
  460. }
  461. else {
  462. $i += 1;
  463. }
  464. }
  465. }
  466. @$status[1] = $i;
  467. return $status;
  468. }
  469. sub is_discrete {
  470. my $partition = shift; #array
  471. my $discrete = 1;
  472. my $i;
  473. $i = 0;
  474. while ( $discrete and $i < scalar @$partition ) {
  475. if ( scalar @{ @$partition[$i] } > 1 ) {
  476. $discrete = 0;
  477. }
  478. $i += 1;
  479. }
  480. return $discrete;
  481. }
  482. sub intersection {
  483. my $le_premier = shift; #array
  484. my $le_deuxieme = shift; #array
  485. my ( $union, $inter, $ele, $result );
  486. $union = {};
  487. $inter = {};
  488. for $ele ( @$le_premier, @$le_deuxieme ) {
  489. $union->{$ele}++ && $inter->{$ele}++;
  490. }
  491. @$result = ( keys %$inter );
  492. return $result;
  493. }
  494. sub partition_meet {
  495. my $the_set = shift; #array
  496. my $partitions = shift; #array
  497. my $new_part; #array
  498. my $newer_part; #array
  499. my $p; #array
  500. my ( $i, $j );
  501. my $intersection; #array
  502. $new_part = deepCopy($the_set);
  503. for $p (@$partitions) {
  504. if ( not scalar @$p == 1 ) {
  505. $newer_part = [];
  506. for $i (@$new_part) {
  507. for $j (@$p) {
  508. $intersection = intersection( $i, $j );
  509. if ( scalar @$intersection > 0 ) {
  510. push @$newer_part, [];
  511. push @{ @$newer_part[ $#{$newer_part} ] }, @$intersection;
  512. }
  513. }
  514. }
  515. $new_part = deepCopy($newer_part);
  516. }
  517. }
  518. return $new_part;
  519. }
  520. sub refinement {
  521. my $adj_in = shift; #hash
  522. my $adj_out = shift; #hash
  523. my $ordered_partition = shift; #array
  524. my $thecells = shift; #array
  525. my $cells; #array
  526. my $equitable_partition; #array
  527. my $cell; #hash
  528. my $in_degree_count; #hash
  529. my $out_degree_count; #hash
  530. my $count; #hash
  531. my $parts; #array
  532. my $new_part; #array
  533. my $index_set; #array
  534. my ( $cell_count, $partition_count, $v, $u, $e, $size, $t, $n );
  535. #Everything is a scalar!!!!!
  536. $cell_count = 0;
  537. # need a cleen copy of cells and equitable_partition
  538. $cells = deepCopy($thecells);
  539. $equitable_partition = deepCopy($ordered_partition);
  540. while ( ( not is_discrete($equitable_partition) )
  541. and $cell_count < scalar @$cells )
  542. {
  543. # retrieve cell from the cells
  544. $cell = {};
  545. for $v ( @{ @$cells[$cell_count] } ) {
  546. $cell->{$v} = 1;
  547. }
  548. $cell_count += 1;
  549. $partition_count = 0;
  550. while ( $partition_count < scalar @$equitable_partition ) {
  551. if ( scalar @{ @$equitable_partition[$partition_count] } > 1 ) {
  552. $in_degree_count = {};
  553. $out_degree_count = {};
  554. for $v ( @{ @$equitable_partition[$partition_count] } ) {
  555. $count = {};
  556. for $u ( keys %{ $adj_out->{$v} } ) {
  557. for $e ( @{ $adj_out->{$v}{$u} } ) {
  558. if ( defined( $cell->{$u} ) ) {
  559. $count->{$e} += 1;
  560. }
  561. }
  562. }
  563. for $e ( keys %$count ) {
  564. if ( not defined( $out_degree_count->{$e} ) ) {
  565. $out_degree_count->{$e}{0} =
  566. +{ map { $_ => 1 }
  567. @{ @$equitable_partition[$partition_count] } };
  568. }
  569. $out_degree_count->{$e}{ $count->{$e} }{$v} = 1;
  570. if ( defined( $out_degree_count->{$e}{0}{$v} ) ) {
  571. delete( $out_degree_count->{$e}{0}{$v} );
  572. }
  573. }
  574. # repeat for out degrees:
  575. $count = {};
  576. for $u ( keys %{ $adj_in->{$v} } ) {
  577. for $e ( @{ $adj_in->{$v}{$u} } ) {
  578. if ( defined( $cell->{$u} ) ) {
  579. $count->{$e} += 1;
  580. }
  581. }
  582. }
  583. for $e ( keys %$count ) {
  584. if ( not defined( $in_degree_count->{$e} ) ) {
  585. $in_degree_count->{$e}{0} =
  586. +{ map { $_ => 1 }
  587. @{ @$equitable_partition[$partition_count] } };
  588. }
  589. $in_degree_count->{$e}{ $count->{$e} }{$v} = 1;
  590. if ( defined( $in_degree_count->{$e}{0}{$v} ) ) {
  591. delete( $in_degree_count->{$e}{0}{$v} );
  592. }
  593. }
  594. }
  595. # Now we make the partitions
  596. $parts = [];
  597. for $e ( sort ( keys %$out_degree_count ) ) {
  598. $new_part = [];
  599. for $size ( sort ( keys %{ $out_degree_count->{$e} } ) ) {
  600. if ( scalar( keys %{ $out_degree_count->{$e}{$size} } ) > 0 ) {
  601. push @$new_part, [];
  602. push @{ @$new_part[ $#{$new_part} ] },
  603. ( keys %{ $out_degree_count->{$e}{$size} } );
  604. }
  605. }
  606. if ( scalar @$new_part > 1 ) {
  607. push @$parts, [];
  608. push @{ @$parts[ $#{$parts} ] }, @$new_part;
  609. }
  610. }
  611. # do the same for $in_degree_count
  612. for $e ( sort ( keys %$in_degree_count ) ) {
  613. $new_part = [];
  614. for $size ( sort ( keys %{ $in_degree_count->{$e} } ) ) {
  615. if ( scalar( keys %{ $in_degree_count->{$e}{$size} } ) > 0 ) {
  616. push @$new_part, [];
  617. push @{ @$new_part[ $#{$new_part} ] },
  618. ( keys %{ $in_degree_count->{$e}{$size} } );
  619. }
  620. }
  621. if ( scalar @$new_part > 1 ) {
  622. push @$parts, [];
  623. push @{ @$parts[ $#{$parts} ] }, @$new_part;
  624. }
  625. }
  626. # now we update the equitable partition and cells
  627. if ( scalar @$parts > 0 ) {
  628. if ( scalar @$parts > 1 ) {
  629. $new_part =
  630. partition_meet( [ @$equitable_partition[$partition_count] ],
  631. $parts );
  632. }
  633. else {
  634. $new_part = @$parts[0];
  635. }
  636. # find smallest 't' bit here
  637. $size = 0;
  638. $t = 0;
  639. for $n ( 0 .. $#{$new_part} ) {
  640. if ( scalar @{ @$new_part[$n] } > $size ) {
  641. $t = $n;
  642. $size = scalar @{ @$new_part[$n] };
  643. }
  644. }
  645. # update cells list as appropriate
  646. for $n ( ( $cell_count + 1 ) .. $#{$cells} ) {
  647. if (
  648. @{ @$cells[$n] } == @{ @$equitable_partition[$partition_count] } )
  649. {
  650. push @{ @$cells[$n] }, @{ @$new_part[$t] };
  651. }
  652. }
  653. $index_set = [];
  654. for $n ( 0 .. $#{$new_part} ) {
  655. if ( not $n == $t ) {
  656. push @$cells, [];
  657. push @{ @$cells[ $#{$cells} ] }, @{ @$new_part[$n] };
  658. }
  659. }
  660. @$equitable_partition = (
  661. @$equitable_partition[ 0 .. $partition_count - 1 ],
  662. @$new_part,
  663. @$equitable_partition[ $partition_count +
  664. 1 .. $#{$equitable_partition} ]
  665. );
  666. }
  667. }
  668. $partition_count += 1;
  669. }
  670. }
  671. return $equitable_partition;
  672. }
  673. sub HNauty {
  674. my $adj_in = shift; #hash
  675. my $adj_out = shift; # hash
  676. my $node_coloring = shift; #array
  677. my $ordered_partition; #array
  678. my $current_node; #array
  679. my $cells; #array
  680. my $tplacement; #array
  681. my $node_indicator; #array
  682. my $best_node; #array
  683. my $first_terminal_node; #array
  684. my $automorphisms; #array
  685. my $generators; #array of hashes
  686. my $prune_autos; #array
  687. my $this_partition; #array
  688. my $newset; #array
  689. my $new_node; #array
  690. my $some_hash; #hash
  691. my $some_array; #array
  692. my $new_auto; #hash
  693. my $perm; #hash
  694. my $fix; #array
  695. my $orbit_reps; #array
  696. my ( $complete, $jump_back, $search_vertex ); #Boolean
  697. my $new_adj;
  698. my ( $counter, $i, $j, $t, $n, $size, $vertex, $a, $b, $ntn );
  699. # do some preprocessing????
  700. # for input: graph should vertices labeled from 0 to number vertices. Edge types
  701. # should also be labeled from 0 on up. adj_in is hash of hash of arrays
  702. # the hashes are keyed by vertices and the array adj_in{v1}{v2} contains the
  703. # edge types going from vertex from v2 INTO v1. adj_out is set up similarily.
  704. $complete = $jump_back = 0;
  705. $best_node = $first_terminal_node = [];
  706. $automorphisms = [];
  707. $counter = 0;
  708. $ntn = 0;
  709. $ordered_partition = deepCopy($node_coloring);
  710. push @$current_node,
  711. refinement( $adj_in, $adj_out, $ordered_partition, $ordered_partition );
  712. $node_indicator = [ [] ];
  713. if ( is_discrete( @$current_node[0] ) ) {
  714. $new_adj = get_adj_str( $adj_out, @$current_node[$counter] );
  715. @$best_node =
  716. ( @$current_node[$counter], @$node_indicator[$counter], $new_adj );
  717. $complete = 1;
  718. }
  719. while ( not $complete ) {
  720. #main loop
  721. if ( is_discrete( @$current_node[$counter] ) ) {
  722. # if ( $first_terminal_node == ( @$current_node[$counter], @$node_indicator[$counter], $new_adj ) ){
  723. if ( $first_terminal_node == [ @$current_node[$counter], @$node_indicator[$counter], $new_adj ] ){
  724. @$best_node = @$first_terminal_node;
  725. }
  726. $counter += -1;
  727. if ( $counter < 0 ) {
  728. $complete = 1;
  729. }
  730. }
  731. elsif ( not $jump_back ) {
  732. # find first nontrivial cell of smallest size
  733. $size = scalar keys %$adj_in;
  734. $t = 0;
  735. for $n ( 0 .. $#{ @$current_node[$counter] } ) {
  736. if ( 1 < scalar @{ @{ @$current_node[$counter] }[$n] }
  737. && scalar @{ @{ @$current_node[$counter] }[$n] } < $size )
  738. {
  739. $t = $n;
  740. $size = scalar @{ @{ @$current_node[$counter] }[$n] };
  741. }
  742. }
  743. @$cells[$counter] = [];
  744. push @{ @$cells[$counter] }, @{ @{ @$current_node[$counter] }[$t] };
  745. @$tplacement[$counter] = $t;
  746. @$prune_autos[$counter] = undef;
  747. @$current_node = @$current_node[ 0 .. $counter ];
  748. }
  749. else {
  750. @$current_node = @$current_node[ 0 .. $counter ];
  751. }
  752. $search_vertex = 1;
  753. $i = 0;
  754. while ( ( not $complete ) && $search_vertex ) {
  755. while ( scalar @{ @$cells[$counter] } == 0 && not $complete ) {
  756. $counter += -1;
  757. if ( $counter < 0 ) {
  758. $complete = 1;
  759. }
  760. }
  761. if ( ( not defined( @$prune_autos[$counter] ) ) or $complete ) {
  762. @$prune_autos[$counter] = 0;
  763. }
  764. else {
  765. if ( @$prune_autos[$counter] < scalar @$automorphisms ) {
  766. @$cells[$counter] = update_cell(
  767. @$current_node[$counter],
  768. @$cells[$counter],
  769. [
  770. @$automorphisms[ @$prune_autos[$counter] .. $#{$automorphisms} ]
  771. ]
  772. );
  773. @$prune_autos[$counter] = scalar $#{$automorphisms};
  774. }
  775. }
  776. if ( not scalar @{ @$cells[$counter] } == 0 ) {
  777. @{ @$cells[$counter] } = sort @{ @$cells[$counter] };
  778. $vertex = @{ @$cells[$counter] }[0];
  779. $search_vertex = 0;
  780. }
  781. }
  782. if ( not $complete ) {
  783. shift @{ @$cells[$counter] };
  784. $this_partition = deepCopy( @$current_node[$counter] );
  785. $t = @$tplacement[$counter];
  786. $newset = deepCopy( @$this_partition[$t] );
  787. $some_hash = +{ map { $_ => 1 } @$newset };
  788. delete( $some_hash->{$vertex} );
  789. @$newset = ( keys %$some_hash );
  790. @$this_partition = (
  791. @$this_partition[ 0 .. $t - 1 ],
  792. [$vertex], $newset, @$this_partition[ $t + 1 .. $#{$this_partition} ]
  793. );
  794. $new_node =
  795. refinement( $adj_in, $adj_out, $this_partition, [ [$vertex] ] );
  796. @$node_indicator[ $counter + 1 ] = partition_value($new_node);
  797. @$node_indicator = @$node_indicator[ 0 .. $counter + 1 ];
  798. # number of nodes += 1
  799. $jump_back = 0;
  800. if ( not scalar @$best_node == 0 ) {
  801. $i = lex_ordered( @$node_indicator[$counter], @$best_node[1] );
  802. if ( @$i[0] eq 'gt' ) {
  803. # $counter = $i[1] - 1;
  804. $counter = @$i[1] - 1;
  805. $jump_back = 1;
  806. }
  807. }
  808. if ( not $jump_back ) {
  809. @$current_node[ $counter + 1 ] = deepCopy($new_node);
  810. $counter += 1;
  811. if ( is_discrete($new_node) ) {
  812. $ntn++;
  813. $new_adj = get_adj_str( $adj_out, $new_node );
  814. if ( scalar @$first_terminal_node == 0 ) {
  815. @$first_terminal_node[0] = deepCopy( @$current_node[$counter] );
  816. @$first_terminal_node[1] = deepCopy( @$node_indicator[$counter] );
  817. @$first_terminal_node[2] = deepCopy($new_adj);
  818. $best_node = deepCopy($first_terminal_node);
  819. }
  820. else {
  821. $i = lex_ordered( [ @$best_node[ 1 .. 2 ] ],
  822. [ @$node_indicator[$counter], $new_adj ] );
  823. if ( @$i[0] eq 'lt' ) {
  824. $best_node = deepCopy(
  825. [
  826. @$current_node[$counter], @$node_indicator[$counter],
  827. $new_adj
  828. ]
  829. );
  830. }
  831. else {
  832. $i = lex_ordered( @$best_node[2], $new_adj );
  833. if ( @$i[0] eq 'eq' ) {
  834. # we have found an automorphism!
  835. $new_auto = {};
  836. for $i ( 0 .. $#{ @$best_node[0] } ) {
  837. $new_auto->{ @{ @{ @$best_node[0] }[$i] }[0] } =
  838. @{ @{ @$current_node[$counter] }[$i] }[0];
  839. }
  840. push @$generators, $new_auto;
  841. ( $fix, $orbit_reps, $automorphisms ) =
  842. update_automorphisms( $automorphisms, $new_auto );
  843. $some_array = [];
  844. for $i ( 0 .. $#{$current_node} ) {
  845. if ( $i < $counter and pfixp( @$current_node[$i], $fix ) ) {
  846. push @$some_array, $i;
  847. }
  848. }
  849. @$some_array = sort @$some_array;
  850. $a = @$some_array[ $#{$some_array} ];
  851. $jump_back = 1;
  852. }
  853. }
  854. $i = lex_ordered( @$first_terminal_node[2], $new_adj );
  855. if ( @$i[0] eq 'eq' ) {
  856. # we have found an automorphism with first term node!
  857. $new_auto = {};
  858. for $i ( 0 .. $#{ @$first_terminal_node[0] } ) {
  859. $new_auto->{ @{ @{ @$first_terminal_node[0] }[$i] }[0] } =
  860. @{ @{ @$current_node[$counter] }[$i] }[0];
  861. }
  862. push @$generators, $new_auto;
  863. ( $fix, $orbit_reps, $automorphisms ) =
  864. update_automorphisms( $automorphisms, $new_auto );
  865. $some_array = [];
  866. for $i ( 0 .. $#{$current_node} ) {
  867. if ( $i < $counter and pfixp( @$current_node[$i], $fix ) ) {
  868. push @$some_array, $i;
  869. }
  870. }
  871. @$some_array = sort @$some_array;
  872. $b = @$some_array[ $#{$some_array} ];
  873. if ($jump_back) {
  874. @$some_array = ( $a, $b );
  875. @$some_array = sort @$some_array;
  876. $counter = @$some_array[0];
  877. }
  878. else {
  879. $counter = $b;
  880. $jump_back = 1;
  881. }
  882. }
  883. elsif ($jump_back) {
  884. $counter = $a;
  885. }
  886. }
  887. }
  888. }
  889. }
  890. }
  891. $perm = {};
  892. for $i ( 0 .. $#{ @$best_node[0] } ) {
  893. $perm->{ @{ @{ @$best_node[0] }[$i] }[0] } = $i;
  894. }
  895. # maybe do some post processing here?
  896. # print "number terminal nodes: $ntn\n";
  897. # print "number autos: ", scalar @$generators, "\n";
  898. return ( $perm, $generators );
  899. }
  900. sub deepCopy {
  901. my $this = shift;
  902. if ( not ref $this ) {
  903. $this;
  904. }
  905. elsif ( ref $this eq "ARRAY" ) {
  906. [ map deepCopy($_), @$this ];
  907. }
  908. elsif ( ref $this eq "HASH" ) {
  909. +{ map { $_ => deepCopy( $this->{$_} ) } keys %$this };
  910. }
  911. else { die "what type is $_?" }
  912. }
  913. 1;