I think I found a bug

I’m on

$ swipl --version
SWI-Prolog version 9.3.32 for x86_64-linux

And I wrote this code

:- use_module( library( 'clpfd')). % transpose

xmas( `XMAS`).

direction8( IDX_R_C) :- IDX_R_C = ( ROW, COL)
, FACTORS=[-1,0,1]
, member( ROW, FACTORS)
, member( COL, FACTORS)
, IDX_R_C \= ( 0, 0)
.

stretch8( IDX_R_C_OFFSET, IDX_R_C, L) :- IDX_R_C_OFFSET = ( ROW_O, COL_O), IDX_R_C = ( ROW, COL)
, TERM = ( between( 1, 3, F), R is ROW_O + F*ROW, C is COL_O + F*COL)
, findall( (R, C), TERM, L)
.

check_mas( IDX_R_C, COUNT) :- true
, xmas( [_|MAS])
, TERM1 = ( direction8( D) , stretch8( IDX_R_C, D, IDX_R_C_LIST),
   transpose( [IDX_R_C_LIST,MAS], IDX_R_C__CHAR__LIST))
, TERM2 = forall( member( [IDX_R_C_INNER, CHAR], IDX_R_C__CHAR__LIST), 
   char_index( CHAR, IDX_R_C_INNER))
, findall( 1, ( TERM1, TERM2), L)
, length( L, COUNT)
.

count_database(COUNT) :- true
, xmas( [X|_])
, TERM = ( char_index( X, IDX_R_C), check_mas( IDX_R_C, COUNT) )
, findall( COUNT, TERM, L)
, sum_list(L, COUNT)
.

fill_database3 :- true
, xmas( XMAS)
, TERM1 = ( between( 0, 139, ROW), between( 0, 139, COL))
, TERM2 = ( CHAR_IDX is ( ROW + COL ) mod 4, nth0( CHAR_IDX, XMAS, CHAR))
% , abolish( char_index/2)
% , dynamic( char_index/2)
, forall( (TERM1, TERM2), assertz( char_index( CHAR, (ROW, COL))))
.

print_contents_of_char_index :- true
, TERM1 = ( between( 0, 139, ROW) )
, TERM2 = ( between( 0, 139, COL), char_index( CHAR, (ROW,COL)))
, TERM3 = findall( CHAR, TERM2, LINE)
, forall( (TERM1, TERM3), format( '~s\n', [ LINE]))
.


demo_001 :- true
% , fill_database3
, aggregate_all( count, char_index(_,_), CI_COUNT)
, writeln( CI_COUNT)
, count_database(COUNT)
, writeln( COUNT)
.

I have currently no idea how to reduce it.

Now look at the execution times:

?- fill_database3, time( demo_001).
19600
9590
% 2,922,053 inferences, 5.115 CPU in 5.133 seconds (100% CPU, 571319 Lips)
true.

?- compile_predicates( [char_index/2]).
true.

?- time( demo_001).
19600
9590
% 2,919,998 inferences, 0.254 CPU in 0.254 seconds (100% CPU, 11506448 Lips)
true.

?- abolish( char_index/2), fill_database3, time( demo_001).
19600
9590
% 2,919,998 inferences, 14.312 CPU in 14.324 seconds (100% CPU, 204018 Lips)
true.

?- compile_predicates( [char_index/2]).
true.

?- time( demo_001).
19600
9590
% 2,919,998 inferences, 22.935 CPU in 22.942 seconds (100% CPU, 127318 Lips)
true.

This looks weird.

Thanks in advance,
Frank Schwidom

Maybe this behaviour is connected to the bug. I have this code:

xmas( `XMAS`).

fill_database3 :- true
, xmas( XMAS)
, TERM1 = ( between( 0, 139, ROW), between( 0, 139, COL))
, TERM2 = ( CHAR_IDX is ( ROW + COL ) mod 4, nth0( CHAR_IDX, XMAS, CHAR))
, abolish( char_index/2)
, dynamic( char_index/2)
, forall( (TERM1, TERM2), assertz( char_index( CHAR, (ROW, COL))))
.

demo_002 :- true
, fill_database3
, forall( (char_index( _, IDX), char_index( _, IDX)), true)
.

demo_002_slow :- true
, forall( (char_index( _, IDX), char_index( _, IDX)), true)
.

When I just start

?- time( demo_002).
% 158,042 inferences, 0.035 CPU in 0.035 seconds (100% CPU, 4536554 Lips)
true.

?- time( demo_002).
% 235,344 inferences, 0.070 CPU in 0.070 seconds (100% CPU, 3365190 Lips)
true.

?- time( demo_002).
% 352,944 inferences, 0.090 CPU in 0.090 seconds (100% CPU, 3923068 Lips)
true.

all is ok.

But when I first start the failing predicate (after restarting swipl):

?- demo_002_slow.
ERROR: Unknown procedure: char_index/2
ERROR: In:
ERROR:   [15] char_index(_174,_176)
ERROR:   [14] '<meta-call>'(user:(...,...)) <foreign>
ERROR:   [13] forall(user:(...,...),user:true) at /usr/local/lib/swipl/boot/apply.pl:52
ERROR:   [12] demo_002_slow at /home/ox/dev-git/adventofcode.com/2024/004/bugreport3.pl:20
ERROR:   [11] toplevel_call(user:user:demo_002_slow) at /usr/local/lib/swipl/boot/toplevel.pl:1520
   Exception: (15) char_index(_92, _94) ? abort
% Execution Aborted
?- time( demo_002).
% 157,268 inferences, 8.762 CPU in 8.762 seconds (100% CPU, 17949 Lips)
true.

?- time( demo_002).
% 156,955 inferences, 8.818 CPU in 8.818 seconds (100% CPU, 17800 Lips)
true.

?- time( demo_002).
% 156,955 inferences, 8.818 CPU in 8.818 seconds (100% CPU, 17800 Lips)
true.

I have the exact same behaviour in Version SWI-Prolog version 10.1.7 for x86_64-linux (both observations).

Btw. I have no idea how to compile swipl under Debian 13. I installed libutf8proc-dev but later became the errormessage :

[ 79%] Generating unicodesecurity.tex
cd /home/ox/mnt/nfs/sources_external/swi-prolog-devel/2026_05_23_14/build/packages/utf8proc && ../../src/swipl -f none --no-packs -x /home/ox/mnt/nfs/sources_external/swi-prolog-devel/2026_05_23_14/build/man/pldoc2tex -- --out=unicodesecurity.tex --section "library('unicode_security')"
ERROR: /home/ox/mnt/nfs/sources_external/swi-prolog-devel/2026_05_23_14/build/home/library/ext/utf8proc/unicode_security.pl:48:
ERROR:    /home/ox/mnt/nfs/sources_external/swi-prolog-devel/2026_05_23_14/build/home/library/ext/utf8proc/unicode_security.pl:48: Initialization goal raised exception:
ERROR:    open_shared_object/3: unicode_security4pl: cannot open shared object file: No such file or directory
ERROR: [Thread main] Exported procedure unicode_security:unicode_restriction_level/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_resolved_scripts/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_confusable/3 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_confusable/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_skeleton/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_identifier_status/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_script_extensions/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_script/2 is not defined
ERROR: [Thread main] Exported procedure unicode_security:unicode_identifier_type/2 is not defined
Warning: [Thread main] Halting with status 1 due to 10 errors and 0 warnings

Obviously the call use_foreign_library(foreign(unicode_security4pl)). fails in in build/home/library/ext/utf8proc/unicode_security.pl.

build$ ls -la home/library/ext/utf8proc
insgesamt 12
drwxrwxr-x  2 ox ox 4096 23. Mai 15:53 .
drwxrwxr-x 35 ox ox 4096 23. Mai 15:53 ..
-rw-rw-r--  1 ox ox    0  1. Jan 2000  .created
lrwxrwxrwx  1 ox ox   55 23. Mai 15:53 unicode.pl -> ../../../../../swipl-devel/packages/utf8proc/unicode.pl
lrwxrwxrwx  1 ox ox   64 23. Mai 15:53 unicode_security.pl -> ../../../../../swipl-devel/packages/utf8proc/unicode_security.pl
lrwxrwxrwx  1 ox ox   55 23. Mai 15:53 uniname.pl -> ../../../../../swipl-devel/packages/utf8proc/uniname.pl

Got it running now on another machine. Both bugs are alive and kicking. The abolish( char_index/2), fill_database3, time( demo_001). part don’t seem to come to an end.

In your first example, the just-in-time-index (jiti) list was created against char_index/2 for the second argument. When running the second query after the top-level error, there are no indexes against char_index/2, hence the difference in speed. I don’t think its possible to trigger an index build based upon searching for jiti in the post history.

?- time(demo_002),jiti_list(user:char_index/2).
% 183,919 inferences, 0.027 CPU in 0.025 seconds (108% CPU, 6795313 Lips)
Predicate                                                                                                                                    #Clauses  Index  Buckets  Speedup  Coll Flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
user:char_index/2                                                                                                                              19,600  2            2      1.0     - L
2:1+2   32,768  19600.0  3974
true.
?- time(demo_002),jiti_list(user:char_index/2).
% 157,112 inferences, 5.324 CPU in 5.124 seconds (104% CPU, 29507 Lips)
Predicate                                                                                                                                    #Clauses  Index  Buckets  Speedup  Coll Flags
―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
true.

I rewrote the code from char_index( CHAR, (ROW, COL)) to char_index( CHAR, ROW, COL).

:- use_module( library( 'clpfd')). % transpose

xmas( `XMAS`).

direction8( IDX_R_C) :- IDX_R_C = ( ROW, COL)
, FACTORS=[-1,0,1]
, member( ROW, FACTORS)
, member( COL, FACTORS)
, IDX_R_C \= ( 0, 0)
.

stretch8( ROW_O, COL_O, IDX_R_C, L) :- IDX_R_C = ( ROW, COL)
, TERM = ( between( 1, 3, F), R is ROW_O + F*ROW, C is COL_O + F*COL)
, findall( (R, C), TERM, L)
.

check_mas( RO, CO, COUNT) :- true
, xmas( [_|MAS])
, TERM1 = ( direction8( D) , stretch8( RO, CO, D, IDX_R_C_LIST),
   transpose( [IDX_R_C_LIST,MAS], IDX_R_C__CHAR__LIST))
, TERM2 = forall( member( [(R, C), CHAR], IDX_R_C__CHAR__LIST), char_index( CHAR, R, C))
, findall( 1, ( TERM1, TERM2), L)
, length( L, COUNT)
.

count_database(COUNT) :- true
, xmas( [X|_])
, TERM = ( char_index( X, R, C), check_mas( R, C, COUNT) )
, findall( COUNT, TERM, L)
, sum_list(L, COUNT)
.

fill_database3 :- true
, xmas( XMAS)
, TERM1 = ( between( 0, 139, ROW), between( 0, 139, COL))
, TERM2 = ( CHAR_IDX is ( ROW + COL ) mod 4, nth0( CHAR_IDX, XMAS, CHAR))
, forall( (TERM1, TERM2), assertz( char_index( CHAR, ROW, COL)))
.

% create_indexes :- true
% , xmas( XMAS)
% , forall( member(CHAR, XMAS), char_index( CHAR, _, _))
% , forall( char_index( _, R, C), char_index( _, R, C))
% .

print_contents_of_char_index :- true
, TERM1 = ( between( 0, 139, ROW) )
, TERM2 = ( between( 0, 139, COL), char_index( CHAR, ROW, COL))
, TERM3 = findall( CHAR, TERM2, LINE)
, forall( (TERM1, TERM3), format( '~s\n', [ LINE]))
.

demo_001 :- true
% , fill_database3
, aggregate_all( count, char_index(_,_,_), CI_COUNT)
, writeln( CI_COUNT)
, count_database(COUNT)
, writeln( COUNT)
.

Now it looks better from the beginning but some issues are still open. And I got a stacktrace.

?- abolish( char_index/3), fill_database3, time( demo_001).
19600
9590
% 2,920,649 inferences, 0.250 CPU in 0.250 seconds (100% CPU, 11693864 Lips)
true.

?- compile_predicates( [char_index/3]).
true.

?- time( demo_001).
19600
9590
% 2,919,998 inferences, 0.336 CPU in 0.336 seconds (100% CPU, 8680492 Lips)
true.

?- abolish( char_index/3), fill_database3, time( demo_001).

ERROR: Received fatal signal 11 (segv)
Time: Sun May 24 21:50:49 2026
Inferences: 43
Thread: 2 (gc)
C-stack trace labeled "crash":
19600
  [0] save_backtrace() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/os/pl-cstack.c:337 [0x7f5d73c17ddb]
  [1] sigCrashHandler() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/os/pl-cstack.c:937 [0x7f5d73c17f19]
  [2] __restore_rt() at sigaction.c:? [0x7f5d73907de0]
  [3] unallocClauseIndexTableEntries() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-index.c:942 [0x7f5d73bcba50]
  [4] freeHeap() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-alloc.c:214 [0x7f5d73bc609e]
  [5] maybeUnregisterDirtyDefinition() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-proc.c:2655 [0x7f5d73b3ad75]
  [6] PL_next_solution_guarded___LD() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-vmi.c:4359 [0x7f5d73adf56c]
  [7] PL_next_solution___LD() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-wam.c:3574 [0x7f5d73acf6e9]
  [8] PL_call_predicate() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-fli.c:4524 [0x7f5d73bdd50f]
  [9] GCmain() at /home/ox_external_source/sources_external/swi-prolog-github/swipl-devel_2025_10_22/swipl-devel/src/pl-thread.c:6989 [0x7f5d73b81167]
  [10] start_thread() at ./nptl/pthread_create.c:478 (discriminator 6) [0x7f5d738aeea7]
  [11] __GI___clone() at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:97 [0x7f5d739caadf]


PROLOG STACK (without arguments):
  [4] system:garbage_collect_clauses/0 [PC=2 in supervisor]
  [2] $gc:gc_loop/0 [PC=29 in clause 1]
  [0] system:$c_call_prolog/0 [PC=0 in top query clause]


PROLOG STACK (with arguments; may crash if data is corrupted):
Speicherzugriffsfehler

################################### Next Try ###################################

?- abolish( char_index/3), fill_database3, time( demo_001).
19600
9590
% 2,922,053 inferences, 0.249 CPU in 0.250 seconds (100% CPU, 11722013 Lips)
true.

?- compile_predicates( [char_index/3]).
true.

?- time( demo_001).
19600
9590
% 2,919,998 inferences, 0.291 CPU in 0.291 seconds (100% CPU, 10021758 Lips)
true.

?- abolish( char_index/3), fill_database3, time( demo_001).
19600
9590
% 2,919,998 inferences, 11.443 CPU in 11.443 seconds (100% CPU, 255176 Lips)
true.

Thanks. Fixed with 01c66bb0fac706fa668ad8b6b5a3f5e10f3f8d75

If you want to trigger an index build, simply call the predicate with arbitrary arguments (preferably, all atoms, and causing a failure). I use this to β€œprime” a lookup with a few hundred thousand facts, in a separate thread, while loading other stuff.

Yes. In this case though, the undefined call registers several candidate indexes as tried. This is now changed by causing major changes to the predicate to reset what has been tried.

Thank you.

Good to know. I wrote some complicated forall statements to achieve the same. With this trick I am able to properly initialize a table. This is better than compiling because the speed improvement which I got in the first case is not reproducible in the same swipl session. But I actually wonder why a dynamic predicate which consists only of facts is so slow. In gprolog I understand it because there is only the first parameter indexed. But pure facts should behave like tables in swipl when properly indexed.