(************************************************************************) (* * The Rocq Prover / The Rocq Development Team *) (* v * Copyright INRIA, CNRS and contributors *) (* <O___,, * (see version control and CREDITS file for authors & dates) *) (* \VV/ **************************************************************) (* // * This file is distributed under the terms of the *) (* * GNU Lesser General Public License Version 2.1 *) (* * (see LICENSE file for the text of the license) *) (************************************************************************)
open Univ open UnivSubst
(* To disallow minimization to Set *) let { Goptions.get = get_set_minimization } =
Goptions.declare_bool_option_and_ref
~key:["Universe";"Minimization";"ToSet"]
~value:true
()
(** Simplification *)
let add_list_map u t map = try let l = Level.Map.find u mapin
Level.Map.set u (t :: l) map with Not_found ->
Level.Map.add u [t] map
(** Precondition: flexible <= ctx *) let choose_canonical ctx flexible algebraic s = let global = Level.Set.diff s ctx in let flexible, rigid = Level.Set.partition flexible (Level.Set.inter s ctx) in (* If there is a global universe in the set, choose it *) ifnot (Level.Set.is_empty global) then let canon = Level.Set.choose global in
canon, (Level.Set.remove canon global, rigid, flexible) else(* No global in the equivalence class, choose a rigid one *) ifnot (Level.Set.is_empty rigid) then let canon = Level.Set.choose rigid in
canon, (global, Level.Set.remove canon rigid, flexible) else(* There are only flexible universes in the equivalence
class, choose a non-algebraic. *) let algs, nonalgs = Level.Set.partition algebraic flexible in ifnot (Level.Set.is_empty nonalgs) then let canon = Level.Set.choose nonalgs in
canon, (global, rigid, Level.Set.remove canon flexible) else let canon = Level.Set.choose algs in
canon, (global, rigid, Level.Set.remove canon flexible)
(* Eq < Le < Lt *) let compare_constraint_type d d' = match d, d' with
| Eq, Eq -> 0
| Eq, _ -> -1
| _, Eq -> 1
| Le, Le -> 0
| Le, _ -> -1
| _, Le -> 1
| Lt, Lt -> 0
type lowermap = constraint_type Level.Map.t
let lower_union = let merge k a b = match a, b with
| Some _, None -> a
| None, Some _ -> b
| None, None -> None
| Some l, Some r -> if compare_constraint_type l r >= 0 then a else b in Level.Map.merge merge
let lower_add l c m = trylet c' = Level.Map.find l m in if compare_constraint_type c c' > 0 then
Level.Map.add l c m else m with Not_found -> Level.Map.add l c m
let lower_of_list l = List.fold_left (fun acc (d,l) -> Level.Map.add l d acc) Level.Map.empty l
module LBMap : sig type t = private { lbmap : lbound Level.Map.t; lbrev : (Level.t * lowermap) Universe.Map.t } val empty : t val add : Level.t -> lbound -> t -> t end = struct type t = { lbmap : lbound Level.Map.t; lbrev : (Level.t * lowermap) Universe.Map.t } (* lbrev is uniquely given from lbmap as a partial reverse mapping *) let empty = { lbmap = Level.Map.empty; lbrev = Universe.Map.empty } let add u bnd m = let lbmap = Level.Map.add u bnd m.lbmap in let lbrev = ifnot bnd.alg && bnd.enforce then match Universe.Map.find bnd.lbound m.lbrev with
| (v, _) -> if Level.compare u v <= 0 then
Universe.Map.add bnd.lbound (u, bnd.lower) m.lbrev else m.lbrev
| exception Not_found ->
Universe.Map.add bnd.lbound (u, bnd.lower) m.lbrev else m.lbrev in
{ lbmap; lbrev } end
let find_inst insts v = Universe.Map.find v insts.LBMap.lbrev
let compute_lbound left = (* The universe variable was not fixed yet.
Compute its level using its lower bound. *) let sup l lbound = match lbound with
| None -> Some l
| Some l' -> Some (Universe.sup l l') in List.fold_left (fun lbound (d, l) -> if d == Le (* l <= ?u *) then sup l lbound else(* l < ?u *)
(assert (d == Lt); ifnot (Universe.level l == None) then
sup (Universe.super l) lbound else None))
None left
let instantiate_with_lbound u lbound lower ~alg ~enforce (ctx, us, insts, cstrs) = if enforce then let inst = Universe.make u in let cstrs' = enforce_leq lbound inst cstrs in
(ctx, UnivFlex.make_nonalgebraic_variable us u,
LBMap.add u {enforce;alg;lbound;lower} insts, cstrs'),
{enforce; alg; lbound=inst; lower} else(* Actually instantiate *)
(Level.Set.remove u ctx, UnivFlex.define u lbound us,
LBMap.add u {enforce;alg;lbound;lower} insts, cstrs),
{enforce; alg; lbound; lower}
type constraints_map = (constraint_type * Level.Map.key) list Level.Map.t
let _pr_constraints_map (cmap:constraints_map) = letopen Pp in
Level.Map.fold (fun l cstrs acc ->
Level.raw_pr l ++ str " => " ++
prlist_with_sep spc (fun (d,r) -> pr_constraint_type d ++ Level.raw_pr r) cstrs ++
fnl () ++ acc)
cmap (mt ())
let remove_alg l (ctx, us, insts, cstrs) =
(ctx, UnivFlex.make_nonalgebraic_variable us l, insts, cstrs)
let not_lower lower (d,l) = (* We're checking if (d,l) is already implied by the lower constraints on some level u. If it represents l < u (d is Lt or d is Le and i > 0, the i < 0 case is impossible due to invariants of Univ), and the lower constraints only have l <=
u then it is not implied. *)
Universe.exists
(fun (l,i) -> let d = if i == 0 then d elsematch d with
| Le -> Lt
| d -> d in trylet d' = Level.Map.find l lower in (* If d is stronger than the already implied lower
* constraints we must keep it. *)
compare_constraint_type d d' > 0 with Not_found -> (* No constraint existing on l *) true) l
exception UpperBoundedAlg
(** [enforce_uppers upper lbound cstrs] interprets [upper] as upper constraints to [lbound], adding them to [cstrs].
@raise UpperBoundedAlg if any [upper] constraints are strict and
[lbound] algebraic. *) let enforce_uppers upper lbound cstrs = List.fold_left (fun cstrs (d, r) -> if d == Le then
enforce_leq lbound (Universe.make r) cstrs else match Universe.level lbound with
| Some lev -> Constraints.add (lev, d, r) cstrs
| None -> raise UpperBoundedAlg)
cstrs upper
let minimize_univ_variables ctx us left right cstrs = let left, lbounds =
Level.Map.fold (fun r lower (left, lbounds as acc) -> if UnivFlex.mem r us || not (Level.Set.mem r ctx) then acc else(* Fixed universe, just compute its glb for sharing *) let lbounds = match compute_lbound (List.map (fun (d,l) -> d, Universe.make l) lower) with
| None -> lbounds
| Some lbound -> LBMap.add r {enforce=true; alg=false; lbound; lower=lower_of_list lower}
lbounds in (Level.Map.remove r left, lbounds))
left (left, LBMap.empty) in let rec instance (ctx, us, insts, cstrs as acc) u = let acc, left, lower = match Level.Map.find u left with
| exception Not_found -> acc, [], Level.Map.empty
| l -> let acc, left, newlow, lower = List.fold_left
(fun (acc, left, newlow, lower') (d, l) -> let acc', {enforce=enf;alg;lbound=l';lower} = aux acc l in let l' = if enf then Universe.make l else l' in acc', (d, l') :: left,
lower_add l d newlow, lower_union lower lower')
(acc, [], Level.Map.empty, Level.Map.empty) l in let left = CList.uniquize (List.filter (not_lower lower) left) in
(acc, left, Level.Map.lunion newlow lower) in let instantiate_lbound lbound = let alg = UnivFlex.is_algebraic u us in if Universe.is_type0 lbound && not (get_set_minimization()) then (* Minim to Set disabled, do not instantiate with Set *)
instantiate_with_lbound u lbound lower ~alg ~enforce:true acc elseif alg then (* u is algebraic: we instantiate it with its lower bound, if any,
or enforce the constraints if it is bounded from the top. *) let lower = Level.Set.fold Level.Map.remove (Universe.levels lbound) lower in
instantiate_with_lbound u lbound lower ~alg:true ~enforce:false acc else(* u is non algebraic *) match Universe.level lbound with
| Some l -> (* The lowerbound is directly a level *) (* u is not algebraic but has no upper bounds, we instantiate it with its lower bound if it is a
different level, otherwise we keep it. *) let lower = Level.Map.remove l lower in ifnot (Level.equal l u) then (* Should check that u does not
have upper constraints that are not already in right *) let acc = remove_alg l acc in
instantiate_with_lbound u lbound lower ~alg:false ~enforce:false acc else acc, {enforce=true; alg=false; lbound; lower}
| None -> beginmatch find_inst insts lbound with
| can, lower -> (* Another universe represents the same lower bound,
we can share them with no harm. *) let lower = Level.Map.remove can lower in
instantiate_with_lbound u (Universe.make can) lower ~alg:false ~enforce:false acc
| exception Not_found -> (* We set u as the canonical universe representing lbound *)
instantiate_with_lbound u lbound lower ~alg:false ~enforce:true acc end in let enforce_uppers ((ctx,us,insts,cstrs), b as acc) = match Level.Map.find u right with
| exception Not_found -> acc
| upper -> let upper = List.filter (fun (d, r) -> not (UnivFlex.mem r us)) upper in let cstrs = enforce_uppers upper b.lbound cstrs in
(ctx, us, insts, cstrs), b in ifnot (Level.Set.mem u ctx) then enforce_uppers (acc, {enforce=true; alg=false; lbound=Universe.make u; lower}) else let lbound = compute_lbound left in match lbound with
| None -> (* Nothing to do *)
enforce_uppers (acc, {enforce=true;alg=false;lbound=Universe.make u; lower})
| Some lbound -> try enforce_uppers (instantiate_lbound lbound) with UpperBoundedAlg ->
enforce_uppers (acc, {enforce=true; alg=false; lbound=Universe.make u; lower}) and aux (ctx, us, seen, cstrs as acc) u = try acc, Level.Map.find u seen.LBMap.lbmap with Not_found -> instance acc u in
UnivFlex.fold (fun u ~is_defined (ctx, us, seen, cstrs as acc) -> ifnot is_defined then fst (aux acc u) else Level.Set.remove u ctx, UnivFlex.make_nonalgebraic_variable us u, seen, cstrs)
us (ctx, us, lbounds, cstrs)
let extra_union a b = {
weak_constraints = UPairSet.union a.weak_constraints b.weak_constraints;
above_prop = Level.Set.union a.above_prop b.above_prop;
}
let normalize_context_set g ctx (us:UnivFlex.t) {weak_constraints=weak;above_prop} = let (ctx, csts) = ContextSet.levels ctx, ContextSet.constraints ctx in (* Keep the Set <= i constraints separate *) let smallles, csts =
Constraints.partition (fun (l,d,r) -> d == Le && Level.is_set l) csts in (* Process weak constraints: when one side is flexible and the 2
universes are unrelated unify them. *) let smallles, csts, g = UPairSet.fold (fun (u,v) (smallles, csts, g as acc) -> let norm = level_subst_of (UnivFlex.normalize_univ_variable us) in let u = norm u and v = norm v in if (Level.is_set u || Level.is_set v) thenbegin if get_set_minimization() thenbegin if Level.is_set u then (Constraints.add (u,Le,v) smallles,csts,g) else (Constraints.add (v,Le,u) smallles,csts,g) endelse acc endelse let set_to a b =
(smallles,
Constraints.add (a,Eq,b) csts,
UGraph.enforce_constraint (a,Eq,b) g) in let check_le a b = UGraph.check_constraint g (a,Le,b) in if check_le u v || check_le v u then acc elseif UnivFlex.mem u us then set_to u v elseif UnivFlex.mem v us then set_to v u else acc)
weak (smallles, csts, g) in let smallles = if get_set_minimization () then
Constraints.filter (fun (l,d,r) -> UnivFlex.mem r us) smallles else Constraints.empty (* constraints Set <= u may be dropped *) in let smallles = if get_set_minimization() then let fold u accu = if UnivFlex.mem u us then Constraints.add (Level.set, Le, u) accu else accu in
Level.Set.fold fold above_prop smallles else smallles in let csts, partition = (* We first put constraints in a normal-form: all self-loops are collapsed
to equalities. *) let g = UGraph.initial_universes_with g in (* use lbound:Set to collapse [u <= v <= Set] into [u = v = Set] *) let g = Level.Set.fold (fun v g -> UGraph.add_universe ~strict:false v g)
ctx g in let add_soft u g = ifnot (Level.is_set u || Level.Set.mem u ctx) thentry UGraph.add_universe ~strict:false u g with UGraph.AlreadyDeclared -> g else g in let g = Constraints.fold
(fun (l, d, r) g -> add_soft r (add_soft l g))
csts g in let g = UGraph.merge_constraints csts g in
UGraph.constraints_of_universes g in (* Ignore constraints from lbound:Set *) let noneqs =
Constraints.filter
(fun (l,d,r) -> not (d == Le && Level.is_set l))
csts in (* Put back constraints [Set <= u] from type inference *) let noneqs = Constraints.union noneqs smallles in let flex x = UnivFlex.mem x us in let algebraic x = UnivFlex.is_algebraic x us in let ctx, us, eqs = List.fold_left (fun (ctx, us, cstrs) s -> let canon, (global, rigid, flexible) = choose_canonical ctx flex algebraic s in (* Add equalities for globals which can't be merged anymore. *) let cstrs = Level.Set.fold (fun g cst ->
Constraints.add (canon, Eq, g) cst) global
cstrs in (* Also add equalities for rigid variables *) let cstrs = Level.Set.fold (fun g cst ->
Constraints.add (canon, Eq, g) cst) rigid
cstrs in let canonu = Universe.make canon in let us = Level.Set.fold (fun f -> UnivFlex.define f canonu) flexible us in
(Level.Set.diff ctx flexible, us, cstrs))
(ctx, us, Constraints.empty) partition in (* Noneqs is now in canonical form w.r.t. equality constraints,
and contains only inequality constraints. *) let noneqs = let norm = level_subst_of (UnivFlex.normalize_univ_variable us) in let fold (u,d,v) noneqs = let u = norm u and v = norm v in if d != Lt && Level.equal u v then noneqs else Constraints.add (u,d,v) noneqs in
Constraints.fold fold noneqs Constraints.empty in (* Compute the left and right set of flexible variables, constraints
mentioning other variables remain in noneqs. *) let noneqs, ucstrsl, ucstrsr =
Constraints.fold (fun (l,d,r as cstr) (noneq, ucstrsl, ucstrsr) -> let lus = UnivFlex.mem l us and rus = UnivFlex.mem r us in let ucstrsl' = if lus then add_list_map l (d, r) ucstrsl else ucstrsl and ucstrsr' =
add_list_map r (d, l) ucstrsr in let noneqs = if lus || rus then noneq else Constraints.add cstr noneq in (noneqs, ucstrsl', ucstrsr'))
noneqs (Constraints.empty, Level.Map.empty, Level.Map.empty) in (* Now we construct the instantiation of each variable. *) let ctx', us, inst, noneqs =
minimize_univ_variables ctx us ucstrsr ucstrsl noneqs in let us = UnivFlex.normalize us in
us, (ctx', Constraints.union noneqs eqs)
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.