123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 A priority queue of streams} *)moduletypeS=StreamQueue_intf.SmoduleMake(Stm:Stream_intf.S)=structmoduleStm=Stm(** {6 Weight functions} *)moduleWeightFun=structtypet=Stm.t->intletpenalty=Stm.penaltyletcombinews=assert(ws<>[]);assert(List.for_all(fun(_,c)->c>0)ws);func->List.fold_left(funsum(w,coeff)->sum+coeff*wc)0wsendmoduleH=CCHeap.Make(struct(* heap ordered by [weight, real age(id)] *)typet=(int*Stm.t)letleq(i1,s1)(i2,s2)=i1<i2||(i1=i2&&Stm.compares1s2<=0)end)(** A priority queue of streams *)typet={mutablehp:H.t;mutabletime_before_fair:int;(* cycles from 0 to ratio, changed at every [take_stm_nb] and [take_stm_nb_fix_stm];
when 0, remove an element from each stream in the queue;
otherwise use the chosen heuristic. *)ratio:int;guard:int;weight:Stm.t->int;(* function that assigns an initial weight to a stream (based on what criteria?) *)mutablestm_nb:int;name:string;}(** generic stream queue based on some ordering on streams, given
by a weight function *)letmake~guard~ratio~weightname=ifratio<0theninvalid_arg"StreamQueue.make: ratio must be greater or equal to 0";{weight;name;ratio;time_before_fair=ratio;guard;stm_nb=0;hp=H.empty;}letis_empty(q:t)=H.is_empty(q.hp)letlengthq=H.sizeq.hpletaddqs=(* No verification that the stream is already in there TODO: should it be verified?*)letw=q.weightsinlethp=H.insert(w,s)q.hpinq.stm_nb<-q.stm_nb+1;q.hp<-hpletadd_lstqsl=List.iter(addq)sl(* let rec _take_first_when_available guard q =
* if H.is_empty q.hp then None (\* TODO: replace with cheaper test q.stm_nb = 0 ? *\)
* else (
* if guard = 0 then raise Not_found;
* if q.time_before_drip = 0
* then (
* let dripped = ref None in
* let reduced_hp, (w, s) = H.take_exn q.hp in
* let new_hp =
* (
* try
* dripped := Stm.drip s;
* H.insert (w + (1), s) reduced_hp
* (\* No matter if a clause or None is dripped the penalty is the same:
* TODO: should the penalty be higher when None is dripped? *\)
* with
* | Stm.Empty_Stream ->
* assert (q.stm_nb > 0);
* q.stm_nb <- q.stm_nb - 1;
* reduced_hp
* ) in
* q.hp <- new_hp;
* match !dripped with
* | None -> _take_first_when_available (guard-1) q
* | Some _ ->
* q.time_before_drip <- q.ratio;
* !dripped
* ) else (
* assert (q.time_before_drip > 0);
* q.time_before_drip <- q.time_before_drip - 1;
* None
* )
* )
*
* let take_first_when_available q =
* assert (q.guard >= 0);
* _take_first_when_available q.guard q *)letrec_take_firstguardq=ifH.is_emptyq.hpthenNone(* TODO: replace with cheaper test q.stm_nb = 0 ? *)else(ifguard=0thenraiseNot_found;letdripped=refNoneinletreduced_hp,(w,s)=H.take_exnq.hpinletnew_hp=(trydripped:=Stm.drips;H.insert(w+Stm.penaltys,s)reduced_hp(* No matter if a clause or None is dripped the penalty is the same:
TODO: should the penalty be higher when None is dripped? *)with|Stm.Empty_Stream->assert(q.stm_nb>0);(* TODO: stronger but more costly assert using H.size ?*)q.stm_nb<-q.stm_nb-1;reduced_hp)inq.hp<-new_hp;match!drippedwith|None->_take_first(guard-1)q|Some_->!dripped)lettake_firstq=assert(q.guard>=0);_take_firstq.guardqletrectake_first_anywayq=ifH.is_emptyq.hpthenNoneelse(letdripped=refNoneinletreduced_hp,(w,s)=H.take_exnq.hpinletnew_hp=(trydripped:=Stm.drips;H.insert(w+Stm.penaltys,s)reduced_hpwith|Stm.Empty_Stream->assert(q.stm_nb>0);q.stm_nb<-q.stm_nb-1;reduced_hp)inq.hp<-new_hp;match!drippedwith|None->take_first_anywayq|Some_->q.time_before_fair<-q.ratio;(* TODO: is this still necessary here? *)!dripped)letrec_take_nbqnbprev_res=ifH.is_emptyq.hp||nb=0thenprev_reselsetry_take_nbq(nb-1)((take_firstq)::prev_res)with|Not_found->prev_reslettake_stm_nbq=ifq.time_before_fair=0then(q.time_before_fair<-q.ratio;(* TODO: the heap is fully traversed two times, can both operations be done with one traversal? *)q.hp<-H.filter(fun(_,s)->not(Stm.is_emptys))q.hp;H.fold(funres(_,s)->Stm.drips::res)[]q.hp)else(q.time_before_fair<-q.time_before_fair-1;_take_nbqq.stm_nb[])letrec_take_stm_nb_fix_stmqnres=ifn=0||H.is_emptyq.hpthenreselseletred_hp,(w,s)=H.take_exnq.hpintryifStm.is_emptysthen(q.stm_nb<-q.stm_nb-1;q.hp<-red_hp;_take_stm_nb_fix_stmqnres)else(letres=Stm.drip_nsnq.guardinq.hp<-H.insert(w+n*Stm.penaltys,s)red_hp;res)with|Stm.Drip_n_Unfinished(res',n')->_take_stm_nb_fix_stmqn'(res'@res)lettake_stm_nb_fix_stmq=ifq.time_before_fair=0then(q.time_before_fair<-q.ratio;(* TODO: the heap is fully traversed two times, can both operations be done with one traversal? *)q.hp<-H.filter(fun(_,s)->not(Stm.is_emptys))q.hp;H.fold(funres(_,s)->Stm.drips::res)[]q.hp)else(q.time_before_fair<-q.time_before_fair-1;_take_stm_nb_fix_stmqq.stm_nb[])letnameq=q.nameletdefault():t=letopenWeightFuninletweight=penaltyinmake~guard:100~ratio:6~weight"default"letppoutq=CCFormat.fprintfout"queue %s"(nameq)letto_string=CCFormat.to_stringppend