34
35:- module(top_k,
36 [ top_k/3 37 ]). 38
39:- use_module(library(heaps)). 40:- use_module(library(assoc)). 41:- use_module(library(option)). 42:- use_module(library(solution_sequences)). 43
74
76
79
81
85
90
91:- meta_predicate top_k(+, 0, -). 92
93top_k(Options1, Goal, Result) :-
94 select_option(return(Return), Options1, Options, backtrack),
95 top_k(Return, Options, Goal, Result).
96
97top_k(Return, Options, Goal, Result) :-
98 ( Options = [_, _|_]
99 -> run_optimized(Return, Options, Goal, Result)
100 ; dispatch_singles(Return, Options, Goal, Result)
101 ).
102
103dispatch_singles(backtrack, Opts, Goal, Goal) :- dispatch_singles(Opts, Goal, _).
104dispatch_singles(list(Term), Opts, Goal, List) :-
105 option(group_by(Group), Opts, ungrouped),
106 ( group_by(GK, Term, dispatch_singles(Opts, Goal, GK), List)
107 *-> Group = GK 108 ; ground(Group),
109 List = [] 110 ).
111
112dispatch_singles([], Goal, ungrouped) :- call(Goal).
113dispatch_singles([Opt], Goal, GK) :- dispatch_single(Opt, Goal, GK).
114
115ordered_term_variables(Term, Vars) :-
116 term_variables(Term, UVars),
117 sort(UVars, Vars).
118
119dispatch_single(order_by(Spec), Goal, ungrouped) :- order_by([Spec], Goal).
120dispatch_single(limit(K), Goal, ungrouped) :- limit(K, Goal).
121dispatch_single(distinct(W), Goal, ungrouped) :- distinct(W, Goal).
122dispatch_single(group_by(Group), Goal, Group) :- dispatch_group_by(Goal, Group).
123
124dispatch_group_by(Goal, Group) :-
125 ordered_term_variables(Goal, GVars),
126 ordered_term_variables(Group, KVars),
127 ord_subtract(GVars, KVars, TVars),
128 Term =.. [v|TVars],
129 bagof(Term, Goal, List),
130 member(Term, List).
131
132run_optimized(Return, Opts, Goal, Result) :-
133 option(limit(Count), Opts, inf),
134 option(order_by(OrderSpec), Opts, asc(unordered)),
135 option(group_by(Group), Opts, ungrouped),
136 ( option(distinct(Witness), Opts)
137 -> Distinct = true
138 ; Distinct = false
139 ),
140 priority_for(OrderSpec, Pri, Key),
141 run_optimized(Goal, Count, Pri, Key, Distinct, Witness, Group, Return, Result).
142
144
156
157priority_for(asc(Key), @=<, Key).
158priority_for(desc(Key), @>=, Key).
159
162better_than(@>=, P1, P2) :- P1 @> P2.
163better_than(@=<, P1, P2) :- P1 @< P2.
164
165setup_state(false, none).
166setup_state(true, state(DictHolder)) :-
167 empty_assoc(D0),
168 DictHolder = holder(D0).
169
170seen_hash(state(holder(D)), Hash, Key) :-
171 get_assoc(Hash, D, Key).
172
173mark_hash(state(DictHolder), Hash, Key) :-
174 DictHolder = holder(D0),
175 put_assoc(Hash, D0, Key, D1),
176 nb_setarg(1, DictHolder, D1),
177 true.
178
179update_topk(Count, Pri, Key, Entry, HHolder) :-
180 HHolder = holder(N0, H0),
181 ( N0 < Count
182 -> add_to_heap(H0, Key, Entry, H1),
183 N1 is N0 + 1,
184 nb_setarg(1, HHolder, N1),
185 nb_setarg(2, HHolder, H1)
186 ; 187 replace_topk(_WorstKey, Pri, Key, Entry, HHolder)
188 ).
189
190revdel_from_heap(Q0,Px,X,Q) :-
191 get_from_heap(Q0,Py,Y,Q1),
192 revdel_from_heap(Q1,Px,X,Q2),
193 add_to_heap(Q2,Py,Y,Q),
194 !.
195revdel_from_heap(Q0,P,X,Q) :-
196 get_from_heap(Q0,P,X,Q).
197
198pri_del_from_heap(Pri, H0, Key, Entry, HRest) :-
199 ( var(Key),
200 Pri == (@=<)
201 -> revdel_from_heap(H0, Key, Entry, HRest)
202 ; delete_from_heap(H0, Key, Entry, HRest)
203 ).
204
205replace_topk(Key1, Pri, Key, Entry, HHolder) :-
206 HHolder = holder(_, H0),
207 pri_del_from_heap(Pri, H0, Key1, _, HRest),
208 ( better_than(Pri, Key, Key1)
209 -> add_to_heap(HRest, Key, Entry, H1),
210 nb_setarg(2, HHolder, H1)
211 ; fail
212 ).
213
214heap_to_list(holder(_N, H), Pri, SortedKeyVars) :-
215 heap_to_list(H, KV0),
216 ( Pri == (@=<)
217 -> 218 219 reverse(KV0, KV1)
220 ; KV1 = KV0
221 ),
222 sort(1, Pri, KV1, SortedKeyVars).
223
225
226run_optimized(Goal, Count, Pri, Key, Distinct, Witness, Group, Return, Result) :-
227 term_variables(Goal, Vars),
228 setup_state(Distinct, State),
229 empty_assoc(G0),
230 GHolder = holder(G0), 231 term_variables(Witness, WVars),
232 WTerm =.. [w|WVars],
233 ( ground(Group)
234 ->create_bucket(GHolder, Group, _)
235 ; true
236 ),
237 forall(Goal,
238 ignore(consider_solution(Count, Pri, Key, Distinct, WTerm, Group, Vars, State, GHolder))),
239 finalize(Group, Return, Pri, GHolder, Vars, Goal, Result).
240
241consider_solution(Count, Pri, Key, Distinct, WTerm, Group, Vars, State, GHolder) :-
242 Entry = Vars,
243 ( Distinct == true
244 ->variant_sha1(WTerm, Hash),
245 ( seen_hash(State, Hash, Key1)
246 ->get_or_create_bucket(GHolder, Group, Bucket),
247 replace_topk(Key1, Pri, Key, Entry, Bucket),
248 GHolder = holder(G0),
249 put_assoc(Group, G0, Bucket, G1),
250 nb_setarg(1, GHolder, G1),
251 mark_hash(State, Hash, Key),
252 fail
253 ; true
254 )
255 ; true
256 ),
257 get_or_create_bucket(GHolder, Group, Bucket),
258 update_topk(Count, Pri, Key, Entry, Bucket),
259 GHolder = holder(G0),
260 put_assoc(Group, G0, Bucket, G1),
261 nb_setarg(1, GHolder, G1),
262 ( Distinct == true
263 ->mark_hash(State, Hash, Key)
264 ; true
265 ).
266
267get_or_create_bucket(GHolder, Group, Bucket) :-
268 GHolder = holder(G0),
269 ( get_assoc(Group, G0, Bucket)
270 -> true
271 ; create_bucket(GHolder, Group, Bucket)
272 ).
273
274create_bucket(GHolder, Group, Bucket) :-
275 GHolder = holder(G0),
276 empty_heap(H0),
277 Bucket = holder(0, H0),
278 put_assoc(Group, G0, Bucket, G1),
279 nb_setarg(1, GHolder, G1).
280
281finalize(Group, Return, Pri, holder(G), Vars, Goal, Result) :-
282 gen_assoc(Group, G, Bucket),
283 (Group == ungrouped -> ! ; true), 284 heap_to_list(Bucket, Pri, List),
285 emit_result(Return, Vars, List, Goal, Result).
286
287emit_result(list(Term), Vars, List, _, Result) :- findall(Term, member(_Key-Vars, List), Result).
288emit_result(backtrack, Vars, List, Goal, Goal) :- member(_Key-Vars, List)