[Total order on rational terms using term_automaton/2 and automaton_minimal/3]
Using the recent term_automaton/2 and automaton_minimal/3, I wrote rt_compare/3 to compare rational terms.
It defines a total order extending the standard order of finite terms (dropping subterm compatibility, as expected). It uses a history stack to detect visited node pairs during traversal. While it may not be optimal in efficiency, sorting a list of 10,000 random terms took about 0.58 seconds.
Please note that I have not formally verified the correctness of rt_compare/3. It is a quick implementation based on my earlier compare_with_stack/3. The complete code is attached below; I would appreciate it if anyone interested could test it.
% ?- maplist(random_term, [X, Y, Z]), predsort(rt_compare, [X,Y,Z], R).
%@ X = _S1, % where
%@ _S1 = f(44, f(_S2, _S1, _S2), g(22.82, 28, "b")),
%@ _S2 = p(f(_S2, _S1, _S2), g(22.82, 28, "b"), g(22.82, 28, "b")),
%@ Y = -5.84,
%@ Z = f(_S4, _S3, _S4), % where
%@ _S3 = p(f(_S3, _, _)),
%@ _S4 = p(f(_S4, _S3, _S4)),
%@ R = [-5.84, f(44, f(_S2, _S1, _S2), g(22.82, 28, "b")), f(_S4, _S3, _S4)] .
% ?- N = 10000, length(_Ns, N), maplist(random_term, _Ns), time(predsort(rt_compare, _Ns, _Sorted)).
%@ % 2,529,162 inferences, 0.564 CPU in 0.584 seconds (97% CPU, 4483519 Lips)
%@ N = 10000 .
% ?- term_min_coa(a(b,c(1)), V, I).
%@ V = automaton(a(3, 2), c(4), b, 1),
%@ I = map(1, 2, 3, 4).
% ?- X=f(Y, 0), Y=f(X, 1), rt_compare(C, X, Y).
% ?- X=f(Y, X), Y=f(X, X), rt_compare(C, X, Y).
% ?- non_terminal_compound(f(a), F, N).
% ?- non_terminal_compound(1, F, N).
non_terminal_compound(X, F, N):- nonvar(X),
functor(X, F, N, compound),
N > 0.
%
find_pair(I, J, [[I|_]|_],[[J|_]|_]):-!.
find_pair(I, J, [_|As], [_|Bs]):- find_pair(I, J, As, Bs).
% ?- rt_pop([], [], X, Y).
% ?- rt_pop([[]], [[]], X, Y).
% ?- rt_pop([[1,2]], [[3, 4]], X, Y).
% ?- rt_pop([[1],[2], [3, 7]], [[4], [5], [6, 8]], X, Y).
%@ X = [[7]],
%@ Y = [[8]].
rt_pop([], [], [], []):-!.
rt_pop([[]|As], [[]|Bs], As0, Bs0):-!, rt_pop(As, Bs, As0, Bs0).
rt_pop([[_]|As], [[_]|Bs], As0, Bs0):-!, rt_pop(As, Bs, As0, Bs0).
rt_pop([[_|Is]|As], [[_|Js]|Bs], [Is|As], [Js|Bs]).
%
term_min_coa(X, V, I):-
term_automaton(X, Y),
automaton_minimal(Y, V, I).
%
random_term(R):- random_term(R, []).
% main
rt_compare(C, X, Y):-
term_min_coa(X, U, MapX),
arg(I, MapX, I0),
term_min_coa(Y, V, MapY),
arg(J, MapY, J0),
rt_compare(C, [[I0]], [[J0]], U-V).
%
rt_compare(=, [], [], _):-!.
rt_compare(C, [[]|As], [[]|Bs], Z):-!,
rt_pop(As, Bs, As0, Bs0),
rt_compare(C, As0, Bs0, Z).
rt_compare(C, [[I|Is]|As], [[J|Js]|Bs], X-Y):-!,
arg(I, X, A),
arg(J, Y, B),
( non_terminal_compound(A, F, K),
non_terminal_compound(B, G, L) ->
compare(D, K/F, L/G),
( D = (=) ->
( find_pair(I, J, As, Bs) ->
rt_pop([Is|As], [Js|Bs], As0, Bs0),
rt_compare(C, As0, Bs0, X-Y)
; A=..[_|Ps],
B=..[_|Qs],
rt_compare(C, [Ps, [I|Is]|As], [Qs, [J|Js]|Bs], X-Y)
)
; C = D
)
; compare(D, A, B),
( D = (=) ->
rt_pop([Is|As], [Js|Bs], As0, Bs0),
rt_compare(C, As0, Bs0, X-Y)
; C = D
)
).