Time is money. I prefer to send it as it is than taking time to polish for readality.
Sorry for many possible junks for you.
(pt-min-coa.pl)
:- module(pt_mincoa, [pt_term_min_coa/3, pt_term_coa/3]).
:- use_module(library(random_terms)).
:- load_c('extract_coa').
:- load_c('paige_tarjan').
% ?- test_paige_tarjan_1.
% Test 1: Acyclic term graph with duplicate subterms (nodes 2 and 3 are identical atoms)
test_paige_tarjan_1 :-
X = nodes(f(2, 3), a, a),
paige_tarjan(X, M, Q),
format('M: ~w~nQ: ~w~n', [M, Q]).
% ?- test_paige_tarjan_2.
% Test 2: Symmetric cyclic rational term graph (mutual recursion)
test_paige_tarjan_2 :-
X = nodes(f(2), f(1)),
paige_tarjan(X, M, Q),
format('M: ~w~nQ: ~w~n', [M, Q]).
% ?- test_paige_tarjan_3.
% Test 3: Self-referential single node cycle (rational term)
test_paige_tarjan_3 :-
X = nodes(f(1)),
paige_tarjan(X, M, Q),
format('M: ~w~nQ: ~w~n', [M, Q]).
% ?- test_paige_tarjan_4.
% Test 4: Mixed types with distinct equivalence classes
test_paige_tarjan_4 :-
X = nodes(g(2, 3), f(4), a, a),
paige_tarjan(X, M, Q),
format('M: ~w~nQ: ~w~n', [M, Q]).
%! random_pt_variant(R, T).
% create random term R, mimimize to T, check it that R and T are variant.
% ?- N = 10000, forall(between(1, N, _), random_pt_variant(R, T)).
% ?- N = 100000, time(forall(between(1, N, _), random_pt_variant(R, T))).
% ?- N = 1000, forall(between(1, N, _), random_pt_variant(R, T)).
% ?- N = 10000, forall(between(1, N, _), random_pt_variant(R, T)).
% ?- N = 100000, forall(between(1, N, _), random_pt_variant(R, T)).
% ?- generate_hydras(10, _L), time(user:extract_coa(_L, _Js, _Tvec)),
% time(user:paige_tarjan(_Tvec, Coa, _Qvec)),
% functor(Coa,_,Csize), functor(_Qvec, _, Qsize).
%@ % 3,079 inferences, 0.002 CPU in 0.003 seconds (69% CPU, 1542585 Lips)
%@ % -1 inferences, 0.000 CPU in 0.000 seconds (78% CPU, -58824 Lips)
%@ Coa = u(h(1, 1)),
%@ Csize = 1,
%@ Qsize = 55 .
% ?- generate_hydras(100, _L), time(user:extract_coa(_L, _Js, _Tvec)),
% time(user:paige_tarjan(_Tvec, Coa, _Qvec)),
% functor(Coa,_,Csize), functor(_Qvec, _, Qsize).
%@ % 25,507,549 inferences, 17.221 CPU in 24.645 seconds (70% CPU, 1481191 Lips)
%@ % -1 inferences, 0.001 CPU in 0.001 seconds (90% CPU, -1218 Lips)
%@ Coa = u(h(1, 1)),
%@ Csize = 1,
%@ Qsize = 5050 .
% ?- generate_hydras(100, _L), time(user:extract_coa(_L, _Js, _Tvec)).
% ?- generate_hydras(100, _L).
%
random_pt_variant(R, T):-
random_term(R),
pt_term_coa([R], [I], Coa),
coa_to_equations(Coa, V, E),
maplist(call, E),
arg(I, V, T),
variant(R, T).
%! random_pt_variant_swi(R, T)
% ?- N = 10, forall(between(1, N, _), random_pt_variant_swi(R, T)).
% ?- N = 100, forall(between(1, N, _), random_pt_variant_swi(R, T)).
% ?- N = 1000, time((forall(between(1, N, _), random_pt_variant_swi(R, T)))).
random_pt_variant_swi(R, T):-
random_term(R),
pt_term_coa_swi([R], [I], Coa),
coa_to_equations(Coa, V, E),
maplist(call, E),
arg(I, V, T),
variant(R, T).
%
coa_to_equations(Coa, U, E):- functor(Coa, F, N),
functor(U, F, N),
coa_to_equations(1, Coa, U, E).
%
coa_to_equations(I, V, U, [X = Tc|E]):- arg(I, V, T), !,
arg(I, U, X),
convert_index_in_args(V, U, T, Tc),
J is I + 1,
coa_to_equations(J, V, U, E).
coa_to_equations(_, _, _, []).
%
convert_index_in_args(_, _, T, T):-
( var(T); atomic(T); compound_name_arity(T, _, 0) ), !.
convert_index_in_args(V, U, T, Tc):- functor(T, F, K),
functor(Tc, F, K),
convert_index_in_args(1, V, U, T, Tc).
%
convert_index_in_args(I, V, U, T, Tc):-arg(I, T, J), !,
arg(J, U, X),
arg(I, Tc , X),
K is I + 1,
convert_index_in_args(K, V, U, T, Tc).
convert_index_in_args(_, _, _, _, _).
% hydra as test data.
hydra(0, _).
hydra(N, h(X, X)):- N>0, N0 is N-1, hydra(N0, X).
% ?- hydra(3, H, A), A=H.
hydra(0, A, A).
hydra(N, h(X, X), A):- N>0, N0 is N-1, hydra(N0, X, A).
%
% ?- generate_hydras(3, X).
generate_hydras(0, []) :- !.
generate_hydras(N, [H|Rest]) :-
N > 0,
hydra(N, H, A), A=H,
N1 is N - 1,
generate_hydras(N1, Rest).
% ?- create_hydra_list(1, 100, _L), pt_term_coa(_L, _V, Coa, Q).
% ?- time((create_hydra_list(1, 1000, _L), pt_term_coa(_L, _V, Coa, Q))).
create_hydra_list(N, M, L):- numlist(N, M, Is),
maplist(create_hydra, Is, L).
%
create_hydra(K, H):- hydra(K, H, H).
% ?- N = 10, Len = 10000,
% time(forall(between(1, N, _), (find_shared_nodes(Len, Cs), writeln(Cs)))).
find_shared_nodes(Len, Cs):-compound_random_terms(Len, Rs),
pt_term_coa(Rs, _, Mvec, Qmap),
non_singleton_equiv_classes(Qmap, Mvec, Cs).
% ?-forall(between(1, 100000, _), compound_random_term(_)).
compound_random_term(R) :- random_term(R, []),
compound(R),
compound_name_arity(R, _, K),
K > 0,
!.
compound_random_term(R) :- compound_random_term(R).
% ?- compound_random_terms(1000, _Rs).
compound_random_terms(N, Rs):- length(Rs, N),
maplist(compound_random_term, Rs).
%
random_term(R):- random_term(R, []).
%
random_terms(N, Rs):- length(Rs, N), maplist(random_term, Rs).
%
rel_to_fun(L, R):- sort(L, L0), rel_to_fun(L0, [], R).
%
rel_to_fun([], X, X).
rel_to_fun([A-B|L], [A-U|V], R):-!,
rel_to_fun(L, [A-[B|U]|V], R).
rel_to_fun([A-B|L], U, R):-!,
rel_to_fun(L, [A-[B]|U], R).
% ?- non_singleton_equiv_classes(a(1,2,1,2), v(a, f(b)), R).
% ?- non_singleton_equiv_classes(a(1,2,1,2), v(A, f(b)), R).
non_singleton_equiv_classes(Qmap, Mvec, Cs):- Qmap=..[_|Js],
functor(Qmap, _, N),
zip(Js, Is, R),
numlist(1, N, Is),
sort(R, R0),
rel_to_fun(R0, Fun),
findall(X, (member(X-V, Fun),
V=[_,_|_],
arg(X, Mvec, A),
compound(A)
),
Cs).
% ?- pt_term_coa([a,A,"abc", 1], Is, Tvec, Mvec).
% ?- pt_term_coa([a,a()], Is, Tvec, Mvec).
% ?- pt_term_coa([b(),a()], Is, Tvec, Mvec).
% ?- pt_term_coa([a,b], Is, Tvec, Mvec).
% ?- X=f(X, X), Y=f(Y,Y), pt_term_coa([X, Y], Is, Tvec, Mvec).
% ?- N = 100000, hydra(N, _H, _R), _H = _R,
% time(term_factorized(_H, _A, _L)),
% time(pt_term_coa([_H], _I, _M, _Q)).
%@ % 96,662 inferences, 56.576 CPU in 56.641 seconds (100% CPU, 1709 Lips)
%@ % 4 inferences, 0.004 CPU in 0.005 seconds (75% CPU, 985 Lips)
%@ N = 100000 .
% ?- N = 100, hydra(N, _H, _R), _H = _R,
% term_factorized(_H, _A, _L), length(_L, Len), writeln(shared=Len),
% pt_term_coa([_H], _I, _M, Q).
% ?- time((generate_hydras(100, _Xs), extract_coa(_Xs, _Js, _Tvec))),
% time(user:paige_tarjan(_Js, _Tvec, Is)).
%@ % 25,517,950 inferences, 17.169 CPU in 24.511 seconds (70% CPU, 1486251 Lips)
%@ % -1 inferences, 0.000 CPU in 0.000 seconds (71% CPU, -100000 Lips)
%@ Is = q(1, 2) .
% ?- time((generate_hydras(2, _Xs), extract_coa(_Xs, _Js, _Tvec))),
% time(user:paige_tarjan(_Js, _Tvec, Is)).
% ?- generate_hydras(100, _Xs), time(pt_term_coa(_Xs, _Js, Coa)).
%@ % 25,507,753 inferences, 17.224 CPU in 24.660 seconds (70% CPU, 1480911 Lips)
%@ Coa = u(h(1, 1)) .
% ?- pt_term_coa([a], X, Y).
% ?- current_predicate(user:extract_coa/3).
pt_term_coa(Xs, Is, Mvec) :- pt_term_coa(Xs, Is, Mvec, _).
%
pt_term_coa(Xs, Is, Mvec, Qmap) :-
user:extract_coa(Xs, Js, Tvec),
user:paige_tarjan(Tvec, Mvec, Qmap),
% print_size_diff(Tvec, Mvec),
apply_quotient_map(Js, Qmap, Is).
%
print_size_diff(X, Y):-
functor(X, _, A),
functor(Y, _, B),
C is A - B,
( C = 0 -> true
; writeln(C)
).
% ?- time((generate_hydras(1000, _Xs), pt_term_coa_swi(_Xs, Is, _Mvec))).
% ?- pt_term_coa_swi([a,A,"abc", 1], Is, Tvec, Mvec).
% ?- pt_term_coa_swi([a,a()], Is, Tvec, Mvec).
% ?- pt_term_coa_swi([b(),a()], Is, Tvec, Mvec).
% ?- pt_term_coa_swi([a,b], Is, Tvec, Mvec).
% ?- X=f(X, X), Y=f(Y,Y), pt_term_coa_swi([X, Y], Is, Tvec, Mvec).
%
pt_term_coa_swi(Xs, Is, Mvec) :- pt_term_coa_swi(Xs, Is, Mvec, _).
%
pt_term_coa_swi(Xs, Is, Mvec, Qmap) :-
user:extract_coa(Xs, Js, Tvec),
paige_tarjan_p(Tvec, Mvec, Qmap),
apply_quotient_map(Js, Qmap, Is).
%
zip([], [], []).
zip([X|Xs], [Y|Ys], [X-Y|XYs]):- zip(Xs, Ys, XYs).
%
same_member(X, [Y|_]):- same_term(X, Y),!.
same_member(X, [_|R]):- same_member(X, R).
% ?- indexed_member(I-a, [J-a]).
% ?- indexed_member(I-a, [J-b]).
indexed_member(I-X, [J-Y|_]):- same_term(X, Y), !, I = J.
indexed_member(A, [_|R]):- indexed_member(A, R).
%%--------------------------------------------------
%! pt_minimum_coa(+Xs, -Is, -Mvec) id det.
%
% ?- X=f(X), pt_minimum_coa([X, f(X), f(f(X))], Is, Mvec).
% ?- X=f(Y,0),Y=f(X,1), pt_minimum_coa([X, Y, f(X,Y), f(Y,X)], Is, Mvec).
% ?- X=f(Y,0),Y=f(X,0), pt_minimum_coa([X, Y, f(X,Y), f(Y,X)], Is, Mvec).
% ?- pt_minimum_coa([X, Y, 1, "ab", ab], Is, Mvec).
pt_minimum_coa(Xs, Is, Mvec):-
pt_minimum_coa_by_same_term(Xs, Is, Mvec).
%
pt_minimum_coa_by_same_term(Xs, Is, Mvec):-
prepare_coa_as_vector(Xs, Js, Tvec),
paige_tarjan_p(Tvec, Mvec, Qmap),
apply_quotient_map(Js, Qmap, Is).
% ?- prepare_coa_as_vector([a], Is, Tvec).
% ?- X=f(Y,0), Y=f(X, 1), prepare_coa_as_vector([X, Y], Is, Tvec).
prepare_coa_as_vector(Xs, Is, Tvec):-
collect_subterms(Xs, [], Ts),
zip(_, Ts, Dict),
term_index(Xs, Is, Dict),
dict_to_coa(Dict, Dict, Coa),
coa_vector(Coa, Tvec).
% ?- X = f(X), collect_subterms([X], [], Ts), zip(_, Ts, Dict),
% dict_to_coa(Dict, Dict, R).
% ?- X = f(X,Y), Y=f(X, f(Y)), collect_subterms([X,Y], [], Ts), zip(_, Ts, Dict),
% dict_to_coa(Dict, Dict, R).
% ?- X = f(X,Y), Y=f(X, f(Y)), collect_subterms([X,Y], [], Ts), zip(_, Ts, Dict),
% dict_to_coa(Dict, Dict, R), index_coa(R).
% ?- X = f(X,Y), Y=f(X, f(Y)), collect_subterms([X,Y], [], Ts), zip(_, Ts, Dict),
% dict_to_coa(Dict, Dict, R), coa_vector(R, D).
dict_to_coa([], _, []).
dict_to_coa([I-T|D0], Dict, [I-T0|Coa]):-
( \+ compound(T) -> T0 = T
; T=..[F|As],
term_index(As, Is, Dict),
T0 =..[F|Is]
),
dict_to_coa(D0, Dict, Coa).
%
term_index([], [], _).
term_index([A|As], [J|Is], Dict):-
indexed_member(J-A, Dict),
!,
term_index(As, Is, Dict).
% ?- apply_quotient_map([4, 3], q(2, 1, 4, 1, 2), R).
apply_quotient_map([], _, []).
apply_quotient_map([I|Is], Vec, [J|Js]):- arg(I, Vec, J),
apply_quotient_map(Is, Vec, Js).
%! collect_subterms(+L, +X, -Y) is det.
% L, X, Y, are a list of terms.
% y in Y iff y in X or y is a subterm of some element of L.
% identity is decided by same_term/2, not ==/2.
%
% ?- collect_subterms([a,B,B, C], [], X).
% ?- A=f(A), collect_subterms([A], [], X).
% ?- B=f(C,D), C=f(B, 0), D=g(B, C), collect_subterms([a,B,C], [], X).
% ?- collect_subterms([a, b], [], X).
% ?- collect_subterms([a(1), b], [], X).
% ?- X=f(X), collect_subterms([X], [], R).
collect_subterms(L, X, Y):- collect_same_subterms(L, X, Y).
%
collect_same_subterms([], X, X):-!.
collect_same_subterms([A|As], X, Y):-
( same_member(A, X) -> X1 = X
; \+ compound(A) -> X1 = [A|X]
; X0 = [A|X],
A =..[_|Bs],
collect_same_subterms(Bs, X0, X1)
),
collect_same_subterms(As, X1, Y).
%
coa_vector(Coa, Vec):- zip(Is, Ls, Coa),
length(Is, N),
numlist(1, N, Is),
Vec=..[v|Ls].
% ?- print_vector(g(a(b), c(d))).
print_vector(Vec):- functor(Vec,_,N),
forall(between(1, N, J), (arg(J, Vec, A), writeln(J=A))).
%%---------------------------------------------------------
%! paige_tarjan_p(+X, -M, -Q) is det.
%
% paige_tarjan.c と入出力(クラスID順、代表元順)が完全に一致する同値版。
% ?- paige_tarjan_p(f(1), M, Q).
%@ M = u(1),
%@ Q = q(1).
% ?- paige_tarjan_p(#(f(1,2), f(2,1)), M, Q).
%@ M = u(f(1, 1)),
%@ Q = q(1, 1).
% ?- X = #(f(), f, g(), g, A, "bbb"),
% paige_tarjan_p(X, M, Q).
%@ X = #(..),
%@ M = u(f(), f, g(), g, A, "bbb"),
%@ Q = q(1, 2, 3, 4, 5, 6).
paige_tarjan_p(X, M, Q) :-
functor(X, _, N),
( N =:= 0
-> M = u, Q = q
; functor(BlockMap, q, N),
initial_partition(X, N, InitialBlocks),
initialize_block_map(InitialBlocks, BlockMap),
refine_partition(InitialBlocks, X, BlockMap, FinalBlocks),
normalize_blocks_appearance(FinalBlocks, OrderedBlocks),
build_outputs(OrderedBlocks, X, N, M, Q)
).
% --- Phase 1: 初期分割(インデックス直接走査) ---
initial_partition(X, N, Partition) :-
collect_terms(1, N, X, Items),
partition_initial(Items, Partition).
collect_terms(I, N, _, []) :- I > N, !.
collect_terms(I, N, X, [I-Term|Rest]) :-
arg(I, X, Term),
I1 is I + 1,
collect_terms(I1, N, X, Rest).
partition_initial([], []).
partition_initial([I-Term|Rest], [[I|Block]|Blocks]) :-
extract_initial_equiv(Rest, Term, Block, Remainder),
partition_initial(Remainder, Blocks).
extract_initial_equiv([], _, [], []).
extract_initial_equiv([J-T2|Rest], T1, [J|Matched], Unmatched) :-
initial_equiv(T1, T2), !,
extract_initial_equiv(Rest, T1, Matched, Unmatched).
extract_initial_equiv([Item|Rest], T1, Matched, [Item|Unmatched]) :-
extract_initial_equiv(Rest, T1, Matched, Unmatched).
initial_equiv(A, B) :-
( var(A)
-> A == B
; var(B)
-> fail
; compound(A)
-> compound(B),
compound_name_arity(A, F1, N1),
compound_name_arity(B, F2, N2),
F1 == F2, N1 =:= N2
; \+ compound(B),
A == B
).
% --- Phase 2: 反復細分化 ---
initialize_block_map([], _).
initialize_block_map([[Rep|Rest]|Blocks], BlockMap) :-
arg(Rep, BlockMap, Rep),
maplist(init_map_to(Rep, BlockMap), Rest),
initialize_block_map(Blocks, BlockMap).
init_map_to(Rep, BlockMap, I) :-
arg(I, BlockMap, Rep).
refine_partition(Blocks, X, BlockMap, FinalBlocks) :-
split_blocks(Blocks, X, BlockMap, NewBlocks, false, Changed),
( Changed == true
-> refine_partition(NewBlocks, X, BlockMap, FinalBlocks)
; FinalBlocks = Blocks
).
split_blocks([], _, _, [], Changed, Changed).
split_blocks([Block|Blocks], X, BlockMap, NewBlocks, FlagIn, FlagOut) :-
split_single_block(Block, X, BlockMap, Split),
( Split = [_]
-> NewBlocks = [Block|RestNew],
split_blocks(Blocks, X, BlockMap, RestNew, FlagIn, FlagOut)
; update_split_map(Split, BlockMap),
append(Split, RestNew, NewBlocks),
split_blocks(Blocks, X, BlockMap, RestNew, true, FlagOut)
).
split_single_block(Block, X, BlockMap, Split) :-
partition_by_transitions(Block, X, BlockMap, Split).
partition_by_transitions([], _, _, []).
partition_by_transitions([I|Rest], X, BlockMap, [[I|SubBlock]|SubBlocks]) :-
extract_same_transition(Rest, I, X, BlockMap, SubBlock, Remainder),
partition_by_transitions(Remainder, X, BlockMap, SubBlocks).
extract_same_transition([], _, _, _, [], []).
extract_same_transition([J|Rest], I, X, BlockMap, [J|Matched], Unmatched) :-
same_transition(I, J, X, BlockMap), !,
extract_same_transition(Rest, I, X, BlockMap, Matched, Unmatched).
extract_same_transition([J|Rest], I, X, BlockMap, Matched, [J|Unmatched]) :-
extract_same_transition(Rest, I, X, BlockMap, Matched, Unmatched).
same_transition(I, J, X, BlockMap) :-
arg(I, X, TI),
arg(J, X, TJ),
( compound(TI)
-> compound_name_arity(TI, _, A),
( A =:= 0
-> true
; check_args(1, A, TI, TJ, BlockMap)
)
; true
).
check_args(K, A, TI, TJ, BlockMap) :-
( K > A
-> true
; arg(K, TI, CI),
arg(K, TJ, CJ),
arg(CI, BlockMap, TargetI),
arg(CJ, BlockMap, TargetJ),
TargetI == TargetJ,
K1 is K + 1,
check_args(K1, A, TI, TJ, BlockMap)
).
update_split_map([_|NewBlocks], BlockMap) :-
maplist(destructively_update_block(BlockMap), NewBlocks).
destructively_update_block(BlockMap, [Rep|Rest]) :-
setarg(Rep, BlockMap, Rep),
maplist(set_map_destructively(BlockMap, Rep), Rest).
set_map_destructively(BlockMap, Rep, I) :-
setarg(I, BlockMap, Rep).
% --- 出現順正規化(最小インデックス昇順) ---
normalize_blocks_appearance(Blocks, OrderedBlocks) :-
maplist(tag_min_index, Blocks, Keyed),
keysort(Keyed, Sorted),
maplist(get_block, Sorted, OrderedBlocks).
tag_min_index(Block, Min-Block) :-
min_list(Block, Min).
get_block(_-Block, Block).
% --- Phase 3: 解の構成 ---
build_outputs(FinalBlocks, X, N, M, Q) :-
length(FinalBlocks, M_Size),
functor(Q, q, N),
functor(M, u, M_Size),
populate_q(FinalBlocks, 1, Q),
populate_m(FinalBlocks, 1, X, Q, M).
populate_q([], _, _).
populate_q([Block|Blocks], ClassId, Q) :-
maplist(set_q(Q, ClassId), Block),
NextClass is ClassId + 1,
populate_q(Blocks, NextClass, Q).
set_q(Q, ClassId, I) :-
arg(I, Q, ClassId).
populate_m([], _, _, _, _).
populate_m([Block|Blocks], ClassId, X, Q, M) :-
min_list(Block, Rep),
arg(Rep, X, Term),
( compound(Term)
-> compound_name_arity(Term, F, A),
( A > 0
-> Term =.. [F|Args],
maplist(get_map(Q), Args, MappedArgs),
MTerm =.. [F|MappedArgs]
; MTerm = Term
)
; MTerm = Term
),
arg(ClassId, M, MTerm),
NextClass is ClassId + 1,
populate_m(Blocks, NextClass, X, Q, M).
get_map(BlockMap, I, TargetId) :-
arg(I, BlockMap, TargetId).
%-----------------------------------------------------
%! find_back_edge_sources(+Root, +V, -Sources) is det.
% ?- V = u(f(1)),
% find_back_edge_sources(1, V, L).
%@ V = u(f(1)),
%@ L = [1].
% ?- V = u(f(2), f(3), f(1), a),
% find_back_edge_sources(1, V, L).
%@ V = u(f(2), f(3), f(1), a),
%@ L = [3].
% ?- V = u(f(2), f(2), a),
% find_back_edge_sources(1, V, L).
%@ V = u(f(2), f(2), a),
%@ L = [2].
% ?- V = u(h(1, 1)),
% find_back_edge_sources(1, V, L).
%@ V = u(h(1, 1)),
%@ L = [1].
% ?- V = u(f(2), g(3, 4), a, f(1)),
% find_back_edge_sources(1, V, L).
%@ V = u(f(2), g(3, 4), a, f(1)),
%@ L = [4].
find_back_edge_sources(Root, V, Sources) :-
functor(V, _, N),
functor(ColorTable, c, N), % var: 白, gray: 灰, black: 黒
dfs_sources(Root, V, ColorTable, RawSources, []),
sort(RawSources, Sources).
dfs_sources(U, V, ColorTable, Sources, Tail) :-
arg(U, ColorTable, Color),
( Color == black
-> Sources = Tail
; setarg(U, ColorTable, gray),
arg(U, V, Term),
( compound(Term)
-> compound_name_arity(Term, _, Arity),
traverse_edges(1, Arity, Term, U, V, ColorTable, Sources, Mid),
setarg(U, ColorTable, black),
Mid = Tail
; setarg(U, ColorTable, black),
Sources = Tail
)
).
traverse_edges(K, Arity, _, _, _, _, Sources, Tail) :-
K > Arity, !,
Sources = Tail.
traverse_edges(K, Arity, Term, U, V, ColorTable, Sources, Tail) :-
arg(K, Term, Next),
( integer(Next)
-> arg(Next, ColorTable, Color),
( Color == gray
-> % Back Edge 発見: 始点 U を収集
Sources = [U|Mid1],
traverse_edges_next(K, Arity, Term, U, V, ColorTable, Mid1, Tail)
; var(Color)
-> dfs_sources(Next, V, ColorTable, Sources, Mid1),
traverse_edges_next(K, Arity, Term, U, V, ColorTable, Mid1, Tail)
; % black
traverse_edges_next(K, Arity, Term, U, V, ColorTable, Sources, Tail)
)
; traverse_edges_next(K, Arity, Term, U, V, ColorTable, Sources, Tail)
).
traverse_edges_next(K, Arity, Term, U, V, ColorTable, Sources, Tail) :-
K1 is K + 1,
traverse_edges(K1, Arity, Term, U, V, ColorTable, Sources, Tail).
%%---------------------------------------------------------
%! pt_term_factorize(+Root, +Coa, -Skeleton, -Equations) is det.
%
% Root: ルートのインデックス
% Coa: Paige-Tarjan の出力ベクトル u(...)
% Skeleton: 循環が変数で切断された骨格項
% Equations: [Var = SubTerm, ...] のバインド方程式リスト
% ?- pt_term_factorize(1, coa(f(1)), S, E).
%@ E = [S=f(S)].
% ?- pt_term_factorize(1, coa(a), S, E).
% ?-N=10, time(forall(between(1, N, _), term_factorize_test)).
%@ % 1,967 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 9322275 Lips)
%@ N = 10.
% ?-N=100, time(forall(between(1, N, _), term_factorize_test)).
% ?- term_factorized(p(),_,_).
% 共有なし -> 成功(素通り)
% ?- term_factorized(f(q()), S, E).
% 共有あり(同一の q() が2箇所に出現) -> クラッシュ
% ?- term_factorized(f(q(), q()), S, E).
% ?- term_factorized(f(q()), S, E).
%@ S = f(q()),
%@ E = [].
% ?- term_factorized(f(q(), q()), S, E).
%@ S = f(_A, _A),
%@ E = [_A=q()].
% ?-N=1, time(forall(between(1, N, _), term_factorize_test)).
%@ % 87 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 2485714 Lips)
%@ N = 1.
% ?-N=2, time(forall(between(1, N, _), term_factorize_test)).
%@ % 869 inferences, 0.000 CPU in 0.000 seconds (94% CPU, 6437037 Lips)
%@ N = 2.
% ?-N=10, time(forall(between(1, N, _), term_factorize_test)).
%@ % 2,451 inferences, 0.000 CPU in 0.000 seconds (97% CPU, 9111524 Lips)
%@ N = 10.
% ?-N=100, time(forall(between(1, N, _), term_factorize_test)).
term_factorize_test :-
random_term(R),
term_factorized(R, _, _).
% ?-N=100000, time(forall(between(1, N, _), pt_term_factorize_test)).
%@ % 32,908,725 inferences, 4.378 CPU in 5.050 seconds (87% CPU, 7516297 Lips)
%@ N = 100000.
pt_term_factorize_test :-
random_term(R),
pt_term_coa([R], [I], Coa),
pt_term_factorize(I, Coa, _, _).
pt_term_factorize(Root, Coa, Skeleton, Equations) :-
find_back_edge_sources(Root, Coa, BackSources),
functor(Coa, _, N),
functor(Vars, v, N),
build_factorized(1, N, Coa, Vars, BackSources, Equations),
arg(Root, Vars, Skeleton).
build_factorized(I, N, _, _, _, []) :-
I > N, !.
build_factorized(I, N, Coa, Vars, BackSources, Equations) :-
arg(I, Coa, Term),
arg(I, Vars, VarI),
( memberchk(I, BackSources)
-> % Back Edge 始点: VarI は自由変数のまま残し、方程式 VarI = SubTerm を外に出す
instantiate_subterm(Term, Vars, SubTerm),
Equations = [VarI = SubTerm | RestEqs]
; % 通常ノード: VarI に展開後の部分項を直接ユニフィケーション(インライン結合)
instantiate_subterm(Term, Vars, VarI),
Equations = RestEqs
),
I1 is I + 1,
build_factorized(I1, N, Coa, Vars, BackSources, RestEqs).
instantiate_subterm(Term, Vars, Instantiated) :-
( compound(Term)
-> compound_name_arity(Term, F, Arity),
functor(Instantiated, F, Arity),
fill_args(1, Arity, Term, Vars, Instantiated)
; % 定数・アトム
Instantiated = Term
).
fill_args(K, Arity, _, _, _) :-
K > Arity, !.
fill_args(K, Arity, Term, Vars, Instantiated) :-
arg(K, Term, Next),
( integer(Next)
-> arg(Next, Vars, TargetVar),
arg(K, Instantiated, TargetVar)
; arg(K, Term, Leaf),
arg(K, Instantiated, Leaf)
),
K1 is K + 1,
fill_args(K1, Arity, Term, Vars, Instantiated).