Creating pairs of variables from a clause

Hi!

I am trying to create pairs of variables from a list of literals that contain unbound variables (ClauseLiterals below): I want to create pairs that combine all the variables in each literal with all the variables in the other literals. See the program at the bottom of this post.

Running test1 yields the behavior I was expecting:

?- test1.
[h(_15806,_15808),p(_15818,_15820),q(_15830,_15832,_15834)]
_15806,_15818
_15806,_15820
_15808,_15818
_15808,_15820
_15806,_15830
_15806,_15832
_15806,_15834
_15808,_15830
_15808,_15832
_15808,_15834
_15818,_15830
_15818,_15832
_15818,_15834
_15820,_15830
_15820,_15832
_15820,_15834
false.

But running test2, which I want to use in my real program, yields this:

?- test2.
[h(_17660,_17662),p(_17672,_17674),q(_17684,_17686,_17688)]
_28,_32
_28,_34
_30,_32
_30,_34
_28,_36
_28,_38
_28,_40
_30,_36
_30,_38
_30,_40
_32,_36
_32,_38
_32,_40
_34,_36
_34,_38
_34,_40
true.

Why are new variables seemingly introduced? I.e. _34, _40, and so on?

Thanks in advance!

Cheers/JCR

test1:- 
    ClauseLiterals = [h(A, B), p(C, D), q(E, F, G)],
    writeln(ClauseLiterals),
    variablePairForClause(ClauseLiterals, Pair),
    writeln(Pair),
    fail.

test2:-
    ClauseLiterals = [h(A, B), p(C, D), q(E, F, G)],
    writeln(ClauseLiterals),
    bagof(Pair, variablePairForClause(ClauseLiterals, Pair), Pairs),
    maplist(writeln, Pairs).

variablePairForClause(ClauseLiterals, Pair):-
    length(ClauseLiterals, NLiterals),
    between(1, NLiterals, Index1),
    StartIndex2 is Index1 + 1,
    between(StartIndex2, NLiterals, Index2),
    nth1(Index1, ClauseLiterals, Literal1),
    nth1(Index2, ClauseLiterals, Literal2),
    term_variables(Literal1, Literal1Vars),
    term_variables(Literal2, Literal2Vars),
    member(P1, Literal1Vars),
    member(P2, Literal2Vars),
    Pair = (P1, P2).

Quick guess without testing.

You did not use the existential (caret) ^ operator with bagof/3. Off the top of my head I have never tried something like you seek and don’t know how/if it can be done.

Thanks @EricGT. I tried the caret but that did not seem to work…
Cheers/JCR

Sorry, :slightly_frowning_face: I did not mean that you should try using the existential operator ^ as your result is all variables and the existential operator excludes variables (if that is the right wording) and thus why I noted I have never tried something like you seek and don’t know how/if it can be done.

I have not looked at what you are trying to do in detail to offer anything new at the moment.

I cannot observe the behaviour you reported:

?- test2.
[h(_11830,_11832),p(_11842,_11844),q(_11854,_11856,_11858)]
_11830,_11842
_11830,_11844
_11832,_11842
_11832,_11844
_11830,_11854
_11830,_11856
_11830,_11858
_11832,_11854
_11832,_11856
_11832,_11858
_11842,_11854
_11842,_11856
_11842,_11858
_11844,_11854
_11844,_11856
_11844,_11858
true.

so, I think it could derive from garbage collection.

1 Like

To second what CapelliC noted, I too get a correct result.

?- test2.
[h(_15956,_15958),p(_15968,_15970),q(_15980,_15982,_15984)]
_15956,_15968
_15956,_15970
_15958,_15968
_15958,_15970
_15956,_15980
_15956,_15982
_15956,_15984
_15958,_15980
_15958,_15982
_15958,_15984
_15968,_15980
_15968,_15982
_15968,_15984
_15970,_15980
_15970,_15982
_15970,_15984
true.
1 Like

Aha, that’s interesting. Just to be sure I ran my queries again. Still get the same results as I got before. I’ll try to see what happens when integrate the predicate in my real program :upside_down_face:

In the past, I used to think that bagof/3 not copies terms from the template into output, and also used such feature solving a CLP(FD) puzzle, but then I got corrected by (iirc) @false, and Jan as well, so I’m not sure anymore…

Here is a slight variation that uses numbervars/1.

test2a:-
    ClauseLiterals = [h(A, B), p(C, D), q(E, F, G)],
    bagof(Pair, variablePairForClause(ClauseLiterals, Pair), Pairs),
    numbervars(ClauseLiterals),
    writeln(ClauseLiterals),
    maplist(writeln, Pairs).
?- test2a.
[h(A,B),p(C,D),q(E,F,G)]
A,C
A,D
B,C
B,D
A,E
A,F
A,G
B,E
B,F
B,G
C,E
C,F
C,G
D,E
D,F
D,G
true.

AFAIK the printing of the variables has to come after numbervars/1 and the use of numbervars/1 has to come after using the variables thus the change in order of some of the statements.

@JCR thanks for this puzzle. I did not know that bagof/3 has a valid use case with all variables and without the existential operator (^/2) and thanks to @CapelliC for showing a correct result.

2 Likes

Cool! That seems promising :slight_smile:

so clever !

I can not take any credit for it. Thanks go to those that created it and to Jan W. for using it in the SWI-Prolog code which I look at often for examples. I also had to re-read my post that used numbervars/N to figure it out.

With your test2a @EricGT I get the same (correct) results as you.

So, seems like your initial problem does not highlight a problem ? To clarify: differently ‘named’ variables in setof/3 output still share with those from ClauseLiterals ? A little experiment could be to assign to A before maplist(writeln, Pairs) and also move there writeln(ClauseLiterals)

To me it is really confusing why the results in the output from test1 and test2 differ and that the variables e.g. _17660 do not correspond to the variables e.g. _28.

I tried as you suggested to play around by putting writeln/1 in different places as shown in test2 and test2b in PROGRAM below (I did not understand what you meant by “A little experiment could be to assign to A before maplist(writeln, Pairs)”; what is A?). Now I am even more confused: The output changes depending on in which order I query ?-test2 and ?-test2b :upside_down_face:. See OUTPUT1 and OUTPUT2. What is happening?

PROGRAM:

test2:-
    ClauseLiterals = [h(A, B), p(C, D), q(E, F, G)],
    writeln(ClauseLiterals),
    bagof(Pair, variablePairForClause(ClauseLiterals, Pair), Pairs),
    maplist(writeln, Pairs).

test2b:-
    ClauseLiterals = [h(A, B), p(C, D), q(E, F, G)],
    bagof(Pair, variablePairForClause(ClauseLiterals, Pair), Pairs),
    writeln(ClauseLiterals),
    maplist(writeln, Pairs).

variablePairForClause(ClauseLiterals, Pair):-
    length(ClauseLiterals, NLiterals),
    between(1, NLiterals, Index1),
    StartIndex2 is Index1 + 1,
    between(StartIndex2, NLiterals, Index2),
    nth1(Index1, ClauseLiterals, Literal1),
    nth1(Index2, ClauseLiterals, Literal2),
    term_variables(Literal1, Literal1Vars),
    term_variables(Literal2, Literal2Vars),
    member(P1, Literal1Vars),
    member(P2, Literal2Vars),
    Pair = (P1, P2).

OUTPUT 1:

?- test2.
[h(_17376,_17378),p(_17388,_17390),q(_17400,_17402,_17404)]
_28,_32
_28,_34
_30,_32
_30,_34
_28,_36
_28,_38
_28,_40
_30,_36
_30,_38
_30,_40
_32,_36
_32,_38
_32,_40
_34,_36
_34,_38
_34,_40
true.

?- test2b.
[h(_2858,_2860),p(_2870,_2872),q(_2882,_2884,_2886)]
_2858,_2870
_2858,_2872
_2860,_2870
_2860,_2872
_2858,_2882
_2858,_2884
_2858,_2886
_2860,_2882
_2860,_2884
_2860,_2886
_2870,_2882
_2870,_2884
_2870,_2886
_2872,_2882
_2872,_2884
_2872,_2886
true.

…here I quit swipl and restarted it…

OUTPUT2:

?- test2b.
[h(_17378,_17380),p(_17390,_17392),q(_17402,_17404,_17406)]
_28,_32
_28,_34
_30,_32
_30,_34
_28,_36
_28,_38
_28,_40
_30,_36
_30,_38
_30,_40
_32,_36
_32,_38
_32,_40
_34,_36
_34,_38
_34,_40
true.

?- test2.
[h(_2856,_2858),p(_2868,_2870),q(_2880,_2882,_2884)]
_2856,_2868
_2856,_2870
_2858,_2868
_2858,_2870
_2856,_2880
_2856,_2882
_2856,_2884
_2858,_2880
_2858,_2882
_2858,_2884
_2868,_2880
_2868,_2882
_2868,_2884
_2870,_2880
_2870,_2882
_2870,_2884
true.

I’m afraid I have no time to read all the details, but one should be aware that calling write/1 twice on the same variable may not print the same value. This is because the print output are stack offsets and GC changes these. There is not a good way around that. For this type of exploration you may consider

 ?- set_prolog_flag(gc, false).

to disable garbage collection. Be careful, you easily run out of memory.

As is, variables appearing in a term that is printed as a whole are consistent, unless portray hooks are used. Printing a term reliably is pretty hard. The toplevel uses copy_term/3 to move the constraints out, if the term is cyclic it factorizes the cycles. Next it uses numbervars/4 with the singletons(true) argument and finally prints using portray and numbervars options.

4 Likes

Thanks @jan; this info helps!