diff options
-rw-r--r-- | doc/refman/RefMan-ext.tex | 8 | ||||
-rw-r--r-- | kernel/univ.ml | 154 | ||||
-rw-r--r-- | kernel/univ.mli | 2 | ||||
-rw-r--r-- | parsing/g_vernac.ml4 | 3 | ||||
-rw-r--r-- | parsing/ppvernac.ml | 2 | ||||
-rw-r--r-- | test-suite/success/PrintSortedUniverses.v | 2 | ||||
-rw-r--r-- | toplevel/vernacentries.ml | 16 | ||||
-rw-r--r-- | toplevel/vernacexpr.ml | 2 |
8 files changed, 180 insertions, 9 deletions
diff --git a/doc/refman/RefMan-ext.tex b/doc/refman/RefMan-ext.tex index 7a56426c4..942530c3b 100644 --- a/doc/refman/RefMan-ext.tex +++ b/doc/refman/RefMan-ext.tex @@ -1750,16 +1750,20 @@ of the occurrences of {\Type}, use \end{quote} \comindex{Print Universes} +\comindex{Print Sorted Universes} The constraints on the internal level of the occurrences of {\Type} (see Section~\ref{Sorts}) can be printed using the command \begin{quote} -{\tt Print Universes.} +{\tt Print [Sorted] Universes.} \end{quote} +If the optional {\tt Sorted} option is given, each universe will be +made equivalent to a numbered label reflecting its level (with a +linear ordering) in the universe hierarchy. This command also accepts an optional output filename: \begin{quote} -\tt Print Universes {\str}. +\tt Print [Sorted] Universes {\str}. \end{quote} If {\str} ends in \texttt{.dot} or \texttt{.gv}, the constraints are printed in the DOT language, and can be processed by Graphviz diff --git a/kernel/univ.ml b/kernel/univ.ml index db5b07131..227fe0417 100644 --- a/kernel/univ.ml +++ b/kernel/univ.ml @@ -494,6 +494,160 @@ let enforce_eq u v c = let merge_constraints c g = Constraint.fold enforce_constraint c g +(* Normalization *) + +module UniverseLSet = + Set.Make (struct type t = universe_level let compare = cmp_univ_level end) + +let lookup_level u g = + try Some (UniverseLMap.find u g) with Not_found -> None + +(** [normalize_universes g] returns a graph where all edges point + directly to the canonical representent of their target. The output + graph should be equivalent to the input graph from a logical point + of view, but optimized. We maintain the invariant that the key of + a [Canonical] element is its own name, by keeping [Equiv] edges + (see the assertion)... I (Stéphane Glondu) am not sure if this + plays a role in the rest of the module. *) +let normalize_universes g = + let rec visit u arc cache = match lookup_level u cache with + | Some x -> x, cache + | None -> match Lazy.force arc with + | None -> + u, UniverseLMap.add u u cache + | Some (Canonical {univ=v; lt=_; le=_}) -> + v, UniverseLMap.add u v cache + | Some (Equiv v) -> + let v, cache = visit v (lazy (lookup_level v g)) cache in + v, UniverseLMap.add u v cache + in + let cache = UniverseLMap.fold + (fun u arc cache -> snd (visit u (Lazy.lazy_from_val (Some arc)) cache)) + g UniverseLMap.empty + in + let repr x = UniverseLMap.find x cache in + let lrepr us = List.fold_left + (fun e x -> UniverseLSet.add (repr x) e) UniverseLSet.empty us + in + let canonicalize u = function + | Equiv _ -> Equiv (repr u) + | Canonical {univ=v; lt=lt; le=le} -> + assert (u == v); + (* avoid duplicates and self-loops *) + let lt = lrepr lt and le = lrepr le in + let le = UniverseLSet.filter + (fun x -> x != u && not (UniverseLSet.mem x lt)) le + in + UniverseLSet.iter (fun x -> assert (x != u)) lt; + Canonical { + univ = v; + lt = UniverseLSet.elements lt; + le = UniverseLSet.elements le; + } + in + UniverseLMap.mapi canonicalize g + +(** [check_sorted g sorted]: [g] being a universe graph, [sorted] + being a map to [Equiv Type.n] (with [n] being a natural number), + checks that all constraints in [g] are satisfied in [sorted]. *) +let check_sorted g sorted = + let get u = match UniverseLMap.find u sorted with + | Equiv x -> x + | Canonical _ -> assert false + in + UniverseLMap.iter (fun u arc -> let repr_u = get u in match arc with + | Equiv v -> assert (get v == repr_u) + | Canonical {univ=u'; lt=lt; le=le} -> + assert (u == u'); + List.iter (fun v -> assert (cmp_univ_level repr_u (get v) <= 0)) le; + List.iter (fun v -> assert (cmp_univ_level repr_u (get v) < 0)) lt) g + +(** [sort_universes g] builds a map from universes in [g] to natural + numbers. It outputs a graph containing equivalence edges from each + level appearing in [g] to [Type.n], and [lt] edges between the + [Type.n]s. The output graph should imply the input graph (and the + implication will be strict most of the time), but is not + necessarily minimal. Note: the result is unspecified if the input + graph already contains [Type.n] nodes (calling a module Type is + probably a bad idea anyway). *) +let sort_universes orig_g = + (* basically a topological sort with grouping, le and lt nodes are + treated the same way *) + let mp = Names.make_dirpath [Names.id_of_string "Type"] in + let g = normalize_universes orig_g in + let degs = (* map from canonical nodes to incoming degrees *) + UniverseLMap.fold + (fun u arc res -> match arc with + | Equiv _ -> res + | Canonical {univ=_; lt=lt; le=le} -> + let res = + try let _ = UniverseLMap.find u res in res + with Not_found -> UniverseLMap.add u 0 res + in + let cumul res u = + let x = try UniverseLMap.find u res with Not_found -> 0 in + UniverseLMap.add u (x+1) res + in + List.fold_left cumul (List.fold_left cumul res lt) le) + g UniverseLMap.empty + in + let init = (* canonical nodes with zero incoming edges *) + UniverseLMap.fold + (fun u deg res -> if deg = 0 then u::res else res) degs [] + in + (* [init] should contain at least [Set] *) + assert (List.mem Set init); + let make_level n = Level (mp, n) in + let rec reduce level_map degs n level us = + (* preconditions: [us] is the list of nodes for universe number + [n], supposed non-empty, and [level] is the [universe_level] + corresponding to [n] *) + assert (us <> [] && level = make_level n); + let edge = Equiv level in + let visit (level_map, degs, next) u = + let level_map = UniverseLMap.add u edge level_map in + match lookup_level u g with + | None -> level_map, degs, next + | Some (Equiv _) -> assert false + | Some (Canonical {univ=_; lt=lt; le=le}) -> + (* virtually remove [u] from the graph, decrement incoming + edge counts in [degs], and collect nodes for the next + level (i.e. nodes for which the edge count reached + zero) *) + let cumul (degs, next) u = + let x = UniverseLMap.find u degs - 1 in + assert (x >= 0); + let degs = UniverseLMap.add u x degs in + if x = 0 then degs, u::next else degs, next + in + let z = List.fold_left cumul (degs, next) lt in + let degs, next = List.fold_left cumul z le in + level_map, degs, next + in + let level_map, degs, next = + List.fold_left visit (level_map, degs, []) us + in + if next = [] then + let () = UniverseLMap.iter (fun _ x -> assert (x = 0)) degs in + level_map + else + (* we create [next_level] here to keep physical equality of all + [Type.n]s *) + let next_level = make_level (n+1) in + let level_map = UniverseLMap.add level + (Canonical {univ=level; lt=[next_level]; le=[]}) level_map + in + reduce level_map degs (n+1) next_level next + in + let level_map = reduce UniverseLMap.empty degs 0 (make_level 0) init in + let level_map = UniverseLMap.fold (fun u arc res -> match arc with + | Equiv v -> UniverseLMap.add u (UniverseLMap.find v res) res + | Canonical _ -> res) g level_map + in + (* defensively check that the result makes sense *) + check_sorted orig_g level_map; + level_map + (**********************************************************************) (* Tools for sort-polymorphic inductive types *) diff --git a/kernel/univ.mli b/kernel/univ.mli index 83c69da99..e19a4c43a 100644 --- a/kernel/univ.mli +++ b/kernel/univ.mli @@ -63,6 +63,8 @@ type constraint_type = Lt | Le | Eq exception UniverseInconsistency of constraint_type * universe * universe val merge_constraints : constraints -> universes -> universes +val normalize_universes : universes -> universes +val sort_universes : universes -> universes (** {6 Support for sort-polymorphic inductive types } *) diff --git a/parsing/g_vernac.ml4 b/parsing/g_vernac.ml4 index 5ff8e06a6..4abc2e5eb 100644 --- a/parsing/g_vernac.ml4 +++ b/parsing/g_vernac.ml4 @@ -755,7 +755,8 @@ GEXTEND Gram | IDENT "Scope"; s = IDENT -> PrintScope s | IDENT "Visibility"; s = OPT [x = IDENT -> x ] -> PrintVisibility s | IDENT "Implicit"; qid = smart_global -> PrintImplicit qid - | IDENT "Universes"; fopt = OPT ne_string -> PrintUniverses fopt + | IDENT "Universes"; fopt = OPT ne_string -> PrintUniverses (false, fopt) + | IDENT "Sorted"; IDENT "Universes"; fopt = OPT ne_string -> PrintUniverses (true, fopt) | IDENT "Assumptions"; qid = smart_global -> PrintAssumptions (false, qid) | IDENT "Opaque"; IDENT "Dependencies"; qid = smart_global -> PrintAssumptions (true, qid) ] ] ; diff --git a/parsing/ppvernac.ml b/parsing/ppvernac.ml index 8d74fd1f8..0583403df 100644 --- a/parsing/ppvernac.ml +++ b/parsing/ppvernac.ml @@ -878,7 +878,7 @@ let rec pr_vernac = function | PrintHintDb -> str"Print Hint *" | PrintHintDbName s -> str"Print HintDb" ++ spc() ++ str s | PrintRewriteHintDbName s -> str"Print Rewrite HintDb" ++ spc() ++ str s - | PrintUniverses fopt -> str"Print Universes" ++ pr_opt str fopt + | PrintUniverses (b, fopt) -> Printf.ksprintf str "Print %sUniverses" (if b then "Sorted " else "") ++ pr_opt str fopt | PrintName qid -> str"Print" ++ spc() ++ pr_smart_global qid | PrintModuleType qid -> str"Print Module Type" ++ spc() ++ pr_reference qid | PrintModule qid -> str"Print Module" ++ spc() ++ pr_reference qid diff --git a/test-suite/success/PrintSortedUniverses.v b/test-suite/success/PrintSortedUniverses.v new file mode 100644 index 000000000..813265808 --- /dev/null +++ b/test-suite/success/PrintSortedUniverses.v @@ -0,0 +1,2 @@ +Require Reals. +Print Sorted Universes. diff --git a/toplevel/vernacentries.ml b/toplevel/vernacentries.ml index 367f0ef00..16817143d 100644 --- a/toplevel/vernacentries.ml +++ b/toplevel/vernacentries.ml @@ -202,7 +202,7 @@ let print_modtype r = with Not_found -> msgnl (str"Unknown Module Type " ++ pr_qualid qid) -let dump_universes s = +let dump_universes_gen g s = let output = open_out s in let output_constraint, close = if Filename.check_suffix s ".dot" || Filename.check_suffix s ".gv" then begin @@ -232,12 +232,17 @@ let dump_universes s = end in try - Univ.dump_universes output_constraint (Global.universes ()); + Univ.dump_universes output_constraint g; close (); msgnl (str ("Universes written to file \""^s^"\".")) with e -> close (); raise e +let dump_universes sorted s = + let g = Global.universes () in + let g = if sorted then Univ.sort_universes g else g in + dump_universes_gen g s + (*********************) (* "Locate" commands *) @@ -1112,8 +1117,11 @@ let vernac_print = function | PrintCoercionPaths (cls,clt) -> ppnl (Prettyp.print_path_between (cl_of_qualid cls) (cl_of_qualid clt)) | PrintCanonicalConversions -> ppnl (Prettyp.print_canonical_projections ()) - | PrintUniverses None -> pp (Univ.pr_universes (Global.universes ())) - | PrintUniverses (Some s) -> dump_universes s + | PrintUniverses (b, None) -> + let univ = Global.universes () in + let univ = if b then Univ.sort_universes univ else univ in + pp (Univ.pr_universes univ) + | PrintUniverses (b, Some s) -> dump_universes b s | PrintHint r -> Auto.print_hint_ref (smart_global r) | PrintHintGoal -> Auto.print_applicable_hint () | PrintHintDbName s -> Auto.print_hint_db_by_name s diff --git a/toplevel/vernacexpr.ml b/toplevel/vernacexpr.ml index 9af222461..72c9b1fe8 100644 --- a/toplevel/vernacexpr.ml +++ b/toplevel/vernacexpr.ml @@ -51,7 +51,7 @@ type printable = | PrintCoercions | PrintCoercionPaths of class_rawexpr * class_rawexpr | PrintCanonicalConversions - | PrintUniverses of string option + | PrintUniverses of bool * string option | PrintHint of reference or_by_notation | PrintHintGoal | PrintHintDbName of string |