]> matita.cs.unibo.it Git - helm.git/blob - helm/software/components/tactics/closeCoercionGraph.ml
Trick: the refiner always subst-expands the outtype, so that the beta-redex
[helm.git] / helm / software / components / tactics / closeCoercionGraph.ml
1 (* Copyright (C) 2005, HELM Team.
2  * 
3  * This file is part of HELM, an Hypertextual, Electronic
4  * Library of Mathematics, developed at the Computer Science
5  * Department, University of Bologna, Italy.
6  * 
7  * HELM is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * 
12  * HELM is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with HELM; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
20  * MA  02111-1307, USA.
21  * 
22  * For details, see the HELM World-Wide-Web page,
23  * http://helm.cs.unibo.it/
24  *)
25
26 (* $Id: cicCoercion.ml 7077 2006-12-05 15:44:54Z fguidi $ *)
27
28 let debug = false 
29 let debug_print s = if debug then prerr_endline (Lazy.force s) else ()
30
31 (* given the new coercion uri from src to tgt returns the list 
32  * of new coercions to create. the list elements are
33  * (source, list of coercions to follow, target)
34  *)
35 let get_closure_coercions src tgt uri coercions =
36   let enrich (uri,sat,_) tgt =
37    let arity = match tgt with CoercDb.Fun n -> n | _ -> 0 in
38     uri,sat,arity
39   in
40   let uri = enrich uri tgt in
41   let eq_carr ?exact s t = 
42     debug_print(lazy(CoercDb.string_of_carr s^" VS "^CoercDb.string_of_carr t));
43     let rc = CoercDb.eq_carr ?exact s t in
44     debug_print(lazy(string_of_bool rc));
45     rc
46   in
47   match src,tgt with
48   | CoercDb.Uri _, CoercDb.Uri _ ->
49       debug_print (lazy ("Uri, Uri4"));
50       let c_from_tgt = 
51         List.filter 
52           (fun (f,t,_) -> 
53              debug_print (lazy ("Uri, Uri3"));
54              eq_carr f tgt) 
55           coercions 
56       in
57       let c_to_src = 
58         List.filter 
59           (fun (f,t,_) -> 
60              debug_print (lazy ("Uri, Uri2"));
61              eq_carr t src) 
62           coercions 
63       in
64         (HExtlib.flatten_map 
65           (fun (_,t,ul) -> 
66              if eq_carr ~exact:true src t then [] else
67              List.map (fun u -> src,[uri; enrich u t],t) ul) c_from_tgt) @
68         (HExtlib.flatten_map 
69           (fun (s,t,ul) -> 
70              if eq_carr ~exact:true s tgt then [] else
71              List.map (fun u -> s,[enrich u t; uri],tgt) ul) c_to_src) @
72         (HExtlib.flatten_map 
73           (fun (s,t1,u1l) ->
74             HExtlib.flatten_map 
75               (fun (_,t,u2l) ->
76                 HExtlib.flatten_map
77                   (fun u1 ->
78                   debug_print (lazy ("Uri, Uri1"));
79                     if  eq_carr ~exact:true s t
80                      || eq_carr ~exact:true s tgt
81                      || eq_carr ~exact:true src t
82                     then [] else
83                     List.map 
84                       (fun u2 -> (s,[enrich u1 t1;uri;enrich u2 t],t)) 
85                       u2l)
86                   u1l) 
87               c_from_tgt) 
88           c_to_src)
89   | _ -> [] (* do not close in case source or target is not an indty ?? *)
90 ;;
91
92 exception UnableToCompose
93
94 (* generate_composite (c2 (c1 s)) in the universe graph univ
95    both living in the same context and metasenv
96
97     c2 ?p2 (c1 ?p1 ?x ?s1) ?s2
98
99     where:
100      ?pn + 1 + ?sn = count_pi n - arity n
101 *)
102 let generate_composite' (c1,sat1,arity1) (c2,sat2,arity2) context metasenv univ=
103   let original_metasenv = metasenv in 
104   let c1_ty,univ = CicTypeChecker.type_of_aux' metasenv context c1 univ in
105   let c2_ty,univ = CicTypeChecker.type_of_aux' metasenv context c2 univ in
106   let rec mk_implicits = function
107     | 0 -> [] | n -> (Cic.Implicit None) :: mk_implicits (n-1)
108   in
109   let rec mk_lambda_spine c namer = function
110     | 0 -> c
111     | n -> 
112         Cic.Lambda 
113           (namer n,
114            (Cic.Implicit None), 
115            mk_lambda_spine (CicSubstitution.lift 1 c) namer (n-1))
116   in 
117   let count_pis t arity = 
118     let rec aux acc n = function
119       | Cic.Prod (name,src,tgt) -> aux (acc@[name]) (n+1) tgt
120       | _ -> n,acc
121     in
122     let len,names = aux [] 0 t in
123     let len = len - arity in
124     List.fold_left 
125       (fun (n,l) x -> if n < len then n+1,l@[x] else n,l) (0,[]) 
126       names
127   in
128   let compose c1 nc1 c2 nc2 =
129    Cic.Appl ((*CicSubstitution.lift 1*) c2 :: mk_implicits (nc2 - sat2 - 1) @
130      Cic.Appl ((*CicSubstitution.lift 1*) c1 :: mk_implicits nc1 ) ::
131      mk_implicits sat2)
132   in
133   let rec create_subst_from_metas_to_rels n = function 
134     | [] -> []
135     | (metano, ctx, ty)::tl -> 
136         (metano,(ctx,Cic.Rel n,ty)) ::
137           create_subst_from_metas_to_rels (n-1) tl
138   in
139   let split_metasenv metasenv n =
140     List.partition (fun (_,ctx,_) -> List.length ctx >= n) metasenv
141   in
142   let purge_unused_lambdas metasenv t =
143     let rec aux = function
144         | Cic.Lambda (_, Cic.Meta (i,_), t) when  
145           List.exists (fun (j,_,_) -> j = i) metasenv ->
146             aux (CicSubstitution.subst (Cic.Rel ~-100) t)
147         | Cic.Lambda (name, s, t) -> 
148             Cic.Lambda (name, s, aux t)
149         | t -> t
150     in
151     aux t
152   in
153   let order_body_menv term body_metasenv c1_pis c2_pis =
154     let rec purge_lambdas = function
155       | Cic.Lambda (_,_,t) -> purge_lambdas t
156       | t -> t
157     in
158     let skip_appl = function | Cic.Appl l -> List.tl l | _ -> assert false in
159     let rec metas_of_term_and_types t =
160       let metas = CicUtil.metas_of_term t in
161       let types = 
162        List.flatten       
163         (List.map 
164           (fun (i,_) -> try 
165             let _,_,ty = CicUtil.lookup_meta i body_metasenv in metas_of_term_and_types ty
166             with CicUtil.Meta_not_found _ -> []) 
167           metas)
168       in
169       metas @ types
170     in
171     let sorted_metas_of_term world t = 
172       let metas = metas_of_term_and_types t in
173       (* this check should be useless *)
174       let metas = List.filter (fun (i,_)->List.exists (fun (j,_,_) -> j=i) world) metas in
175       let order_metas metasenv metas = 
176         let module OT = struct type t = int let compare = Pervasives.compare end in
177         let module S = HTopoSort.Make (OT) in
178         let dep i = 
179           try 
180             let _,_,ty = List.find (fun (j,_,_) -> j=i) metasenv in
181             let metas = List.map fst (CicUtil.metas_of_term ty) in
182             HExtlib.list_uniq (List.sort Pervasives.compare metas)
183           with Not_found -> []
184         in
185           S.topological_sort (List.map (fun (i,_) -> i) metas) dep 
186       in 
187       order_metas world metas
188     in
189     let metas_that_saturate l =
190       List.fold_left 
191         (fun (acc,n) t ->
192           let metas = sorted_metas_of_term body_metasenv t in
193           let metas = 
194             List.filter (fun i -> List.for_all (fun (j,_) -> j<>i) acc) metas in
195           let metas = List.map (fun i -> i,n) metas in
196           metas @ acc, n+1)
197         ([],0) l
198     in
199     let l_c2 = skip_appl (purge_lambdas term) in
200     let l_c2_b,l_c2_a =
201      try
202       HExtlib.split_nth (c2_pis - sat2 - 1) l_c2
203      with
204       Failure _ -> assert false in
205     let l_c1,l_c2_a =
206      match l_c2_a with
207         Cic.Appl (_::l_c1)::tl -> l_c1,tl
208       | _ -> assert false in
209     let meta_to_be_coerced =
210      try
211       match List.nth l_c1 (c1_pis - sat1 - 1) with
212        | Cic.Meta (i,_) -> Some i
213        | t -> 
214           debug_print 
215             (lazy("meta_to_be_coerced: " ^ CicPp.ppterm t));
216           debug_print 
217             (lazy("c1_pis: " ^ string_of_int c1_pis ^ 
218              " sat1:" ^ string_of_int sat1));
219           None
220      with
221       Failure _ -> assert false
222     in
223     (* BIG HACK ORRIBLE:
224      * it should be (l_c2_b @ l_c1 @ l_c2_a), but in this case sym (eq_f) gets
225      *  \A,B,f,x,y,Exy and not \B,A,f,x,y,Exy
226      * as an orrible side effect, the other composites get a type lyke
227      *  \A,x,y,Exy,B,f with 2 saturations
228      *)
229     let meta2no = fst (metas_that_saturate (l_c1 @ l_c2_b @ l_c2_a)) in
230     let sorted =
231      List.sort 
232       (fun (i,ctx1,ty1) (j,ctx1,ty1) -> 
233           try List.assoc i meta2no -  List.assoc j meta2no 
234           with Not_found -> assert false) 
235       body_metasenv
236     in
237     let rec position_of n acc =
238      function
239         [] -> assert false
240       | (i,_,_)::_ when i = n -> acc
241       | _::tl -> position_of n (acc + 1) tl
242     in
243     let saturations_res, position_of_meta_to_be_coerced = 
244       match meta_to_be_coerced with
245       | None -> 0,0
246       | Some meta_to_be_coerced -> 
247           debug_print
248             (lazy ("META_TO_BE_COERCED: " ^ string_of_int meta_to_be_coerced));
249           let position_of_meta_to_be_coerced =
250             position_of meta_to_be_coerced 0 sorted in
251           debug_print (lazy ("POSITION_OF_META_TO_BE_COERCED: " ^
252             string_of_int position_of_meta_to_be_coerced));
253           List.length sorted - position_of_meta_to_be_coerced - 1,
254           position_of_meta_to_be_coerced
255     in
256     debug_print (lazy ("SATURATIONS: " ^ string_of_int saturations_res));
257     sorted, saturations_res, position_of_meta_to_be_coerced
258   in
259   let namer l n = 
260     let l = List.map (function Cic.Name s -> s | _ -> "A") l in
261     let l = List.fold_left
262       (fun acc s -> 
263         let rec add' s =
264           if List.exists ((=) s) acc then add' (s^"'") else s
265         in
266         acc@[add' s])
267       [] l
268     in
269     let l = List.rev l in 
270     Cic.Name (List.nth l (n-1))
271   in 
272   debug_print (lazy ("\nCOMPOSING"));
273   debug_print (lazy (" c1= "^CicPp.ppterm c1 ^"  :  "^ CicPp.ppterm c1_ty));
274   debug_print (lazy (" c2= "^CicPp.ppterm c2 ^"  :  "^ CicPp.ppterm c2_ty));
275   let c1_pis, names_c1 = count_pis c1_ty arity1 in 
276   let c2_pis, names_c2 = count_pis c2_ty arity2 in
277   let c = compose c1 c1_pis c2 c2_pis in
278   let spine_len = c1_pis + c2_pis in
279   let c = mk_lambda_spine c (namer (names_c1 @ names_c2)) spine_len in
280   debug_print (lazy ("COMPOSTA: " ^ CicPp.ppterm c));
281   let old_insert_coercions = !CicRefine.insert_coercions in
282   let old_pack_coercions = !CicRefine.pack_coercions in
283   let c, metasenv, univ, saturationsres, cpos =
284     try
285       CicRefine.insert_coercions := false;
286       CicRefine.pack_coercions := false;
287       let term, ty, metasenv, ugraph = 
288         CicRefine.type_of_aux' metasenv context c univ
289       in
290       debug_print(lazy("COMPOSED REFINED: "^CicPp.ppterm term));
291       debug_print(lazy("COMPOSED REFINED (pretty): "^
292         CicMetaSubst.ppterm_in_context [] ~metasenv term context));
293 (*       let metasenv = order_metasenv metasenv in *)
294 (*       debug_print(lazy("ORDERED MENV: "^CicMetaSubst.ppmetasenv [] metasenv)); *)
295       let body_metasenv, lambdas_metasenv = 
296         split_metasenv metasenv (spine_len + List.length context)
297       in
298       debug_print(lazy("B_MENV: "^CicMetaSubst.ppmetasenv [] body_metasenv));
299       debug_print(lazy("L_MENV: "^CicMetaSubst.ppmetasenv [] lambdas_metasenv));
300       let body_metasenv, saturationsres, cpos =
301        order_body_menv term body_metasenv c1_pis c2_pis
302       in
303       debug_print(lazy("ORDERED_B_MENV: "^CicMetaSubst.ppmetasenv [] body_metasenv));
304       let subst = create_subst_from_metas_to_rels spine_len body_metasenv in
305       debug_print (lazy("SUBST: "^CicMetaSubst.ppsubst body_metasenv subst));
306       let term = CicMetaSubst.apply_subst subst term in
307       let metasenv = CicMetaSubst.apply_subst_metasenv subst metasenv in
308       debug_print (lazy ("COMPOSED SUBSTITUTED: " ^ CicPp.ppterm term));
309       let term, ty, metasenv, ugraph = 
310         CicRefine.type_of_aux' metasenv context term ugraph
311       in
312       let body_metasenv, lambdas_metasenv = 
313         split_metasenv metasenv (spine_len + List.length context)
314       in
315       let lambdas_metasenv = 
316         List.filter 
317           (fun (i,_,_) -> 
318             List.for_all (fun (j,_,_) -> i <> j) original_metasenv)
319           lambdas_metasenv
320       in
321       let term = purge_unused_lambdas lambdas_metasenv term in
322       let metasenv = 
323         List.filter 
324           (fun (i,_,_) -> 
325             List.for_all 
326               (fun (j,_,_) ->
327                 i <> j || List.exists (fun (j,_,_) -> j=i) original_metasenv) 
328               lambdas_metasenv) 
329           metasenv 
330       in
331       debug_print (lazy ("####################"));
332       debug_print (lazy ("COMPOSED: " ^ CicPp.ppterm term));
333       debug_print (lazy ("SATURATIONS: " ^ string_of_int saturationsres));
334       debug_print (lazy ("MENV: "^CicMetaSubst.ppmetasenv [] metasenv));
335       debug_print (lazy ("####################"));
336       CicRefine.insert_coercions := old_insert_coercions;
337       CicRefine.pack_coercions := old_pack_coercions;
338       term, metasenv, ugraph, saturationsres, cpos
339     with
340     | CicRefine.RefineFailure s 
341     | CicRefine.Uncertain s -> debug_print s; 
342         CicRefine.insert_coercions := old_insert_coercions;
343         CicRefine.pack_coercions := old_pack_coercions;
344         raise UnableToCompose
345     | exn ->
346         CicRefine.insert_coercions := old_insert_coercions;
347         CicRefine.pack_coercions := old_pack_coercions;
348         raise exn
349   in
350   let c_ty, univ = 
351     CicTypeChecker.type_of_aux' ~subst:[] [] [] c univ
352   in
353   let real_composed = ref true in
354   let c = 
355     let rec is_id = function
356       | Cic.Lambda(_,_,t) -> is_id t
357       | Cic.Rel 1 -> true
358       | _ -> false
359     in
360     let is_id = function
361       | Cic.Const (u,_) -> 
362           (match CicEnvironment.get_obj CicUniv.empty_ugraph u with
363           | Cic.Constant (_,Some bo,_,_,_), _ ->  is_id bo
364           | _ -> false)
365       | _ -> false
366     in
367     let unvariant u =
368      match CicEnvironment.get_obj CicUniv.empty_ugraph u with
369      | Cic.Constant (_,Some (Cic.Const (u',_)),_,_,attrs), _
370        when List.exists ((=) (`Flavour `Variant)) attrs ->
371          u'
372      | _ -> u
373     in
374     let is_variant u =
375      match CicEnvironment.get_obj CicUniv.empty_ugraph u with
376      | Cic.Constant (_,Some (Cic.Const (u',_)),_,_,attrs), _
377        when List.exists ((=) (`Flavour `Variant)) attrs -> true
378      | _ -> false
379     in
380     let rec aux = function
381       | Cic.Lambda(n,s,t) -> Cic.Lambda(n,s,aux t)
382       | Cic.Appl (c::_) as t -> 
383           let t = 
384             if is_id c then
385               (real_composed := false ;
386                CicReduction.head_beta_reduce ~delta:true t)
387             else t
388           in 
389           (match t with
390           | Cic.Appl l -> Cic.Appl (List.map aux l)
391           | Cic.Const (u,[]) when is_variant u -> Cic.Const (unvariant u,[])
392           | t -> t)
393        | Cic.Const (u,[]) when is_variant u -> Cic.Const (unvariant u,[])
394        | t -> t
395     in
396     let simple_eta_c t = 
397       let incr = 
398         List.map (function Cic.Rel n -> Cic.Rel (n+1) | _ -> assert false)
399       in
400       let rec aux acc ctx = function
401         | Cic.Lambda (n,s,tgt) -> 
402             aux (incr acc @ [Cic.Rel 1]) (Some (n,Cic.Decl s) ::ctx) tgt
403         | Cic.Appl (t::tl) when tl = acc && 
404             CicTypeChecker.does_not_occur ctx 0 (List.length acc) t -> true, t
405         | t -> false, t
406       in
407       let b, newt = aux [] [] t in
408       if b then newt else t
409     in
410      simple_eta_c (aux c)
411   in
412   debug_print (lazy ("COMPOSED COMPRESSED: " ^ string_of_bool !real_composed ^" : " ^ CicPp.ppterm c));
413   c, c_ty, metasenv, univ, saturationsres, arity2, cpos, !real_composed
414 ;;
415
416 let build_obj c c_ty univ arity is_var =
417   let cleaned_ty =
418     FreshNamesGenerator.clean_dummy_dependent_types c_ty 
419   in
420   let obj = Cic.Constant ("xxxx",Some c,cleaned_ty,[],
421     [`Generated] @ if not is_var then [`Flavour `Variant] else [] )  in 
422
423     obj,univ
424 ;;
425
426 (* removes from l the coercions that are in !coercions *)
427 let filter_duplicates l coercions =
428   List.filter (
429    fun (src,l1,tgt) ->
430      not (List.exists (fun (s,t,l2) -> 
431        CoercDb.eq_carr s src && 
432        CoercDb.eq_carr t tgt &&
433        try 
434          List.for_all2 (fun (u1,_,_) (u2,_,_) -> UriManager.eq u1 u2) l1 l2
435        with
436        | Invalid_argument "List.for_all2" -> 
437            debug_print (lazy("XXX")); false)
438      coercions))
439   l
440 ;;
441
442 let mangle s t l = 
443   (*List.fold_left
444     (fun s x -> s ^ "_" ^ x)
445     (s ^ "_OF_" ^ t ^ "_BY" ^ string_of_int (List.length l)) l*)
446   s ^ "_OF_" ^ t
447 ;;
448
449 exception ManglingFailed of string 
450
451 let number_if_already_defined buri name l =
452   let err () =
453     raise 
454       (ManglingFailed 
455         ("Unable to give an altenative name to " ^ buri ^ "/" ^ name ^ ".con"))
456   in
457   let rec aux n =
458     let suffix = if n > 0 then ("__" ^ string_of_int n) else "" in
459     let suri = buri ^ "/" ^ name ^ suffix ^ ".con" in
460     let uri = UriManager.uri_of_string suri in
461     let retry () = if n < max_int then aux (n+1) else err () in
462     if List.exists (UriManager.eq uri) l then retry ()
463     else
464       try
465         let _  = Http_getter.resolve' ~local:true ~writable:true uri in
466         if Http_getter.exists' ~local:true uri then retry () else uri
467       with 
468       | Http_getter_types.Key_not_found _ -> uri
469       | Http_getter_types.Unresolvable_URI _ -> assert false
470   in
471   aux 0
472 ;;
473   
474 (* given a new coercion uri from src to tgt returns 
475  * a list of (new coercion uri, coercion obj, universe graph) 
476  *)
477 let close_coercion_graph src tgt uri saturations baseuri =
478   (* check if the coercion already exists *)
479   let coercions = CoercDb.to_list () in
480   let todo_list = get_closure_coercions src tgt (uri,saturations,0) coercions in
481   debug_print (lazy("composed " ^ string_of_int (List.length todo_list)));
482   let todo_list = filter_duplicates todo_list coercions in
483   try
484     let new_coercions = 
485       List.fold_left 
486         (fun acc (src, l , tgt) ->
487           try 
488             match l with
489             | [] -> assert false 
490             | (he,saturations1,arity1) :: tl ->
491                 let first_step = 
492                   Cic.Constant ("", Some (CicUtil.term_of_uri he),
493                   Cic.Sort Cic.Prop, [], [`Generated]),
494                   saturations1,
495                   arity1,0
496                 in
497                 let o,_ = 
498                   List.fold_left (fun (o,univ) (coer,saturations2,arity2) ->
499                     match o with 
500                     | Cic.Constant (_,Some u,_,[],_),saturations1,arity1,_ ->
501                         let t, t_ty, menv, univ, saturationsres, 
502                           arityres, cposres, is_var 
503                         = 
504                           generate_composite' (u,saturations1,arity1) 
505                             (CicUtil.term_of_uri coer,
506                              saturations2, arity2) [] [] univ
507                         in
508                         if (menv <> []) then
509                           HLog.warn "MENV non empty after composing coercions";
510                         let o,univ = build_obj t t_ty univ arityres is_var in
511                          (o,saturationsres,arityres,cposres),univ
512                     | _ -> assert false 
513                   ) (first_step, CicUniv.oblivion_ugraph) tl
514                 in
515                 let name_src = CoercDb.string_of_carr src in
516                 let name_tgt = CoercDb.string_of_carr tgt in
517                 let by = List.map (fun u,_,_ -> UriManager.name_of_uri u) l in
518                 let name = mangle name_tgt name_src by in
519                 let c_uri = 
520                   number_if_already_defined baseuri name 
521                     (List.map (fun (_,_,u,_,_,_,_) -> u) acc) 
522                 in
523                 let named_obj,saturations,arity,cpos = 
524                   match o with
525                   | Cic.Constant (_,bo,ty,vl,attrs),saturations,arity,cpos ->
526                       Cic.Constant (name,bo,ty,vl,attrs),saturations,arity,cpos
527                   | _ -> assert false 
528                 in
529                   (src,tgt,c_uri,saturations,named_obj,arity,cpos)::acc
530           with UnableToCompose -> acc
531       ) [] todo_list
532     in
533     new_coercions
534   with ManglingFailed s -> HLog.error s; []
535 ;;
536
537 CicCoercion.set_close_coercion_graph close_coercion_graph;;
538
539 (* generate_composite (c2 (c1 s)) in the universe graph univ
540  * both living in the same context and metasenv *)
541 let generate_composite c1 c2 context metasenv univ sat1 sat2 =
542  let a,_,b,c,_,_,_,_ =
543   generate_composite' (c1,sat1,0) (c2,sat2,0) context metasenv univ
544  in
545   a,b,c
546 ;;