Skip to content

Commit 0ae5093

Browse files
committed
coupling rule: one-sided and two-sided
1 parent 23cf445 commit 0ae5093

File tree

9 files changed

+327
-26
lines changed

9 files changed

+327
-26
lines changed

src/ecHiTacticals.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ and process1_phl (_ : ttenv) (t : phltactic located) (tc : tcenv1) =
200200
| Psetmatch info -> EcPhlCodeTx.process_set_match info
201201
| Pweakmem info -> EcPhlCodeTx.process_weakmem info
202202
| Prnd (side, pos, info) -> EcPhlRnd.process_rnd side pos info
203+
| Pcoupling (side, f) -> EcPhlRnd.process_coupling side f
203204
| Prndsem (red, side, pos) -> EcPhlRnd.process_rndsem ~reduce:red side pos
204205
| Pconseq (opt, info) -> EcPhlConseq.process_conseq_opt opt info
205206
| Pconseqauto cm -> process_conseqauto cm

src/ecLexer.mll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@
145145
"cfold" , CFOLD ; (* KW: tactic *)
146146
"rnd" , RND ; (* KW: tactic *)
147147
"rndsem" , RNDSEM ; (* KW: tactic *)
148+
"coupling" , COUPLING ; (* KW: tactic *)
148149
"pr_bounded" , PRBOUNDED ; (* KW: tactic *)
149150
"bypr" , BYPR ; (* KW: tactic *)
150151
"byphoare" , BYPHOARE ; (* KW: tactic *)

src/ecParser.mly

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@
416416
%token CONSEQ
417417
%token CONST
418418
%token COQ
419+
%token COUPLING
419420
%token CHECK
420421
%token EDIT
421422
%token FIX
@@ -3000,9 +3001,13 @@ interleave_info:
30003001
| RND s=side? info=rnd_info c=prefix(COLON, semrndpos)?
30013002
{ Prnd (s, c, info) }
30023003

3004+
30033005
| RNDSEM red=boption(STAR) s=side? c=codepos1
30043006
{ Prndsem (red, s, c) }
30053007

3008+
| COUPLING s=side? f=sform
3009+
{ Pcoupling (s, f) }
3010+
30063011
| INLINE s=side? u=inlineopt? o=occurences?
30073012
{ Pinline (`ByName(s, u, ([], o))) }
30083013

src/ecParsetree.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ type phltactic =
748748
| Pasgncase of (oside * pcodepos)
749749
| Prnd of oside * psemrndpos option * rnd_tac_info_f
750750
| Prndsem of bool * oside * pcodepos1
751+
| Pcoupling of oside * pformula
751752
| Palias of (oside * pcodepos * osymbol_r)
752753
| Pweakmem of (oside * psymbol * fun_params)
753754
| Pset of (oside * pcodepos * bool * psymbol * pexpr)

src/phl/ecPhlPrRw.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,4 +339,4 @@ let t_pr_rewrite (s, f) tc =
339339
let hyps = LDecl.push_active_ss mp hyps in
340340
{m;inv=EcProofTyping.process_form hyps f ty}
341341
in
342-
t_pr_rewrite_low (s, omap to_env f) tc
342+
t_pr_rewrite_low (s, omap to_env f) tc

src/phl/ecPhlRnd.ml

Lines changed: 194 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,137 @@ module Core = struct
442442
| `Right -> f_equivS (snd es.es_ml) mt (es_pr es) es.es_sl s (es_po es) in
443443
FApi.xmutate1 tc (`RndSem pos) [concl]
444444

445+
(* -------------------------------------------------------------------- *)
446+
let t_equiv_coupling_r side g tc =
447+
(* process the following pRHL goal, where g is a coupling of g1 and g2 *)
448+
(* {phi} x <$ g1 ~ y <$ g2 {psi} *)
449+
let env = FApi.tc1_env tc in
450+
let es = tc1_as_equivS tc in
451+
let (lvL, muL), sl' = tc1_last_rnd tc es.es_sl in
452+
let (lvR, muR), sr' = tc1_last_rnd tc es.es_sr in
453+
let tyL = proj_distr_ty env (e_ty muL) in
454+
let tyR = proj_distr_ty env (e_ty muR) in
455+
let muL = EcFol.form_of_expr (EcMemory.memory es.es_ml) muL in
456+
let muR = EcFol.form_of_expr (EcMemory.memory es.es_mr) muR in
457+
458+
let goal =
459+
match side with
460+
| None ->
461+
(* Goal: iscoupling g muL muR => forall a b, (a, b) \in supp(g) => psi[x -> a, y -> b] *)
462+
(* Generate two free variables a and b and the pair (a, b) *)
463+
let a_id = EcIdent.create "a" in
464+
let b_id = EcIdent.create "b" in
465+
let a = f_local a_id tyL in
466+
let b = f_local b_id tyR in
467+
let ab = f_tuple [a; b] in
468+
469+
(* Generate the coupling distribution type: (tyL * tyR) distr *)
470+
let coupling_ty = ttuple [tyL; tyR] in
471+
let g_app = f_app_simpl g [] (tdistr coupling_ty) in
472+
473+
let iscoupling_op = EcPath.extend EcCoreLib.p_top ["Distr"; "iscoupling"] in
474+
let iscoupling_ty = tfun (tdistr tyL) (tfun (tdistr tyR) (tfun (tdistr coupling_ty) tbool)) in
475+
let iscoupling_pred = f_app (f_op iscoupling_op [tyL; tyR] iscoupling_ty)
476+
[muL; muR; g_app] tbool in
477+
478+
(* Substitute in the postcondition *)
479+
let post = es.es_po in
480+
let post_subst = subst_form_lv env (EcMemory.memory es.es_ml) lvL a post in
481+
let post_subst = subst_form_lv env (EcMemory.memory es.es_mr) lvR b post_subst in
482+
483+
let goal = f_imp (f_in_supp ab g_app) post_subst in
484+
let goal = f_forall_simpl [(a_id, GTty tyL); (b_id, GTty tyR)] goal in
485+
f_and iscoupling_pred goal
486+
| Some side ->
487+
(* Goal: dmap d1 g = d2 /\ forall a b, b = g(a) => psi[x -> a, y -> b] *)
488+
let dmap_op = EcPath.extend EcCoreLib.p_top ["Distr"; "dmap"] in
489+
let dmap_ty = tfun (tdistr tyL) (tfun (tfun tyL tyR) (tdistr tyR)) in
490+
let dmap_pred = f_eq (f_app (f_op dmap_op [tyL; tyR] dmap_ty) [muL; g] (tdistr tyR)) muR in
491+
492+
let a_id = EcIdent.create "a" in
493+
let b_id = EcIdent.create "b" in
494+
let a = f_local a_id tyL in
495+
let b = f_local b_id tyR in
496+
let post = es.es_po in
497+
let post_subst = subst_form_lv env (EcMemory.memory es.es_ml) lvL a post in
498+
let post_subst = subst_form_lv env (EcMemory.memory es.es_mr) lvR b post_subst in
499+
500+
let eq_condition =
501+
match side with
502+
| `Left -> f_eq (f_app_simpl g [a] tyR) b
503+
| `Right -> f_eq a (f_app_simpl g [b] tyL) in
504+
505+
let goal = f_imp eq_condition post_subst in
506+
let goal = f_forall_simpl [(a_id, GTty tyL); (b_id, GTty tyR)] goal in
507+
f_and dmap_pred goal
508+
in
509+
let goal = f_equivS_r { es with es_sl=sl'; es_sr=sr'; es_po=goal; } in
510+
511+
FApi.xmutate1 tc `Rnd [goal]
512+
513+
(* -------------------------------------------------------------------- *)
514+
let t_equiv_map_rnd_r f tc =
515+
let env = FApi.tc1_env tc in
516+
let es = tc1_as_equivS tc in
517+
let (lvL, muL), sl' = tc1_last_rnd tc es.es_sl in
518+
let (lvR, muR), sr' = tc1_last_rnd tc es.es_sr in
519+
let tyL = proj_distr_ty env (e_ty muL) in
520+
let tyR = proj_distr_ty env (e_ty muR) in
521+
let muL = EcFol.form_of_expr (EcMemory.memory es.es_ml) muL in
522+
let muR = EcFol.form_of_expr (EcMemory.memory es.es_mr) muR in
523+
524+
(* Generate free variables a and b *)
525+
let a_id = EcIdent.create "a" in
526+
let b_id = EcIdent.create "b" in
527+
let a = f_local a_id tyL in
528+
let b = f_local b_id tyR in
529+
530+
(* Apply function f to a *)
531+
let f_app = f_app_simpl f [a] tyR in
532+
533+
(* Substitute in the postcondition: psi[x <- a, y <- f(a)] *)
534+
let post = es.es_po in
535+
let post_subst = subst_form_lv env (EcMemory.memory es.es_ml) lvL a post in
536+
let post_subst = subst_form_lv env (EcMemory.memory es.es_mr) lvR f_app post_subst in
537+
538+
(* Forall a, psi[x <- a, y <- f(a)] *)
539+
let goal = f_forall_simpl [(a_id, GTty tyL)] post_subst in
540+
541+
(* Create the probability constraint: *)
542+
(* sum_{a' : tyL} mu1_g1[a'] * (if f(a') = b then 1 else 0) = mu1_g2[b] *)
543+
let a'_id = EcIdent.create "a'" in
544+
let a' = f_local a'_id tyL in
545+
546+
let f_app_sum = f_app_simpl f [a'] tyR in
547+
let eq_condition = f_eq f_app_sum b in
548+
let prob_muL = f_app_simpl muL [a'] treal in (* mu1 muL a' *)
549+
let b2r_op = EcPath.extend EcCoreLib.p_top ["Real"; "b2r"] in
550+
let b2r_ty = tfun tbool treal in
551+
let b2r_app = f_op b2r_op [] b2r_ty in
552+
let b2r_condition = f_app_simpl b2r_app [eq_condition] treal in
553+
554+
let summand = f_real_mul prob_muL b2r_condition in
555+
let sum_func = f_lambda [(a'_id, GTty tyL)] summand in
556+
557+
let bigreal_path = EcPath.extend EcCoreLib.p_top ["StdBigop"; "Bigreal"; "BRA"] in
558+
let big_op = EcPath.extend bigreal_path ["big"] in
559+
let predt_op = EcPath.extend EcCoreLib.p_top ["Logic"; "predT"] in
560+
let predt_ty = tfun tyL tbool in
561+
let predt_app = f_op predt_op [tyL] predt_ty in
562+
let big_ty = tfun (tfun tyL tbool) (tfun (tfun tyL treal) treal) in
563+
let big_app = f_op big_op [tyL] big_ty in
564+
let sum_expr = f_app_simpl big_app [predt_app; sum_func] treal in
565+
566+
(* The probability constraint: sum = mu1_g2[b] *)
567+
let prob_muR_b = f_app_simpl muR [b] treal in (* mu1 muR b *)
568+
let prob_constraint = f_eq sum_expr prob_muR_b in
569+
570+
(* forall b, constraint -> goal *)
571+
let goal = f_imp prob_constraint goal in
572+
let goal = f_forall_simpl [(b_id, GTty tyR)] goal in
573+
let goal = f_equivS_r { es with es_sl=sl'; es_sr=sr'; es_po=goal; } in
574+
575+
FApi.xmutate1 tc `Rnd [goal]
445576
end (* Core *)
446577

447578
(* -------------------------------------------------------------------- *)
@@ -666,32 +797,32 @@ let process_rnd side pos tac_info tc =
666797
t_bdhoare_rnd tac_info tc
667798

668799
| _, _, _ when is_equivS concl ->
669-
let process_form f ty1 ty2 =
670-
TTC.tc1_process_prhl_form tc (tfun ty1 ty2) f in
671-
672-
let bij_info =
673-
match tac_info with
674-
| PNoRndParams -> None, None
675-
| PSingleRndParam f -> Some (process_form f), None
676-
| PTwoRndParams (f, finv) -> Some (process_form f), Some (process_form finv)
677-
| _ -> tc_error !!tc "invalid arguments"
678-
in
800+
let process_form f ty1 ty2 =
801+
TTC.tc1_process_prhl_form tc (tfun ty1 ty2) f in
802+
803+
let bij_info =
804+
match tac_info with
805+
| PNoRndParams -> None, None
806+
| PSingleRndParam f -> Some (process_form f), None
807+
| PTwoRndParams (f, finv) -> Some (process_form f), Some (process_form finv)
808+
| _ -> tc_error !!tc "invalid arguments"
809+
in
679810

680-
let pos = pos |> Option.map (function
681-
| Single (b, p) ->
682-
let p =
683-
if Option.is_some side then
684-
EcProofTyping.tc1_process_codepos1 tc (side, p)
685-
else EcTyping.trans_codepos1 (FApi.tc1_env tc) p
686-
in Single (b, p)
687-
| Double ((b1, p1), (b2, p2)) ->
688-
let p1 = EcProofTyping.tc1_process_codepos1 tc (Some `Left , p1) in
689-
let p2 = EcProofTyping.tc1_process_codepos1 tc (Some `Right, p2) in
690-
Double ((b1, p1), (b2, p2))
691-
)
692-
in
693-
694-
t_equiv_rnd side ?pos bij_info tc
811+
let pos = pos |> Option.map (function
812+
| Single (b, p) ->
813+
let p =
814+
if Option.is_some side then
815+
EcProofTyping.tc1_process_codepos1 tc (side, p)
816+
else EcTyping.trans_codepos1 (FApi.tc1_env tc) p
817+
in Single (b, p)
818+
| Double ((b1, p1), (b2, p2)) ->
819+
let p1 = EcProofTyping.tc1_process_codepos1 tc (Some `Left , p1) in
820+
let p2 = EcProofTyping.tc1_process_codepos1 tc (Some `Right, p2) in
821+
Double ((b1, p1), (b2, p2))
822+
)
823+
in
824+
825+
t_equiv_rnd side ?pos bij_info tc
695826

696827
| _ -> tc_error !!tc "invalid arguments"
697828

@@ -713,3 +844,41 @@ let process_rndsem ~reduce side pos tc =
713844
| Some side when is_equivS concl ->
714845
t_equiv_rndsem reduce side pos tc
715846
| _ -> tc_error !!tc "invalid arguments"
847+
848+
(* -------------------------------------------------------------------- *)
849+
let process_rndp f tc =
850+
let concl = FApi.tc1_goal tc in
851+
852+
if not (is_equivS concl) then
853+
tc_error !!tc "rndp can only be used on pRHL goals"
854+
else
855+
let env = FApi.tc1_env tc in
856+
let es = tc1_as_equivS tc in
857+
let (_, muL), _ = tc1_last_rnd tc es.es_sl in
858+
let (_, muR), _ = tc1_last_rnd tc es.es_sr in
859+
let tyL = proj_distr_ty env (e_ty muL) in
860+
let tyR = proj_distr_ty env (e_ty muR) in
861+
let func_ty = tfun tyL tyR in
862+
let f_form = TTC.tc1_process_prhl_form tc func_ty f in
863+
Core.t_equiv_map_rnd_r f_form tc
864+
865+
let process_coupling side g tc =
866+
let concl = FApi.tc1_goal tc in
867+
868+
if not (is_equivS concl) then
869+
tc_error !!tc "coupling can only be used on pRHL goals"
870+
else
871+
let env = FApi.tc1_env tc in
872+
let es = tc1_as_equivS tc in
873+
let (_, muL), _ = tc1_last_rnd tc es.es_sl in
874+
let (_, muR), _ = tc1_last_rnd tc es.es_sr in
875+
let tyL = proj_distr_ty env (e_ty muL) in
876+
let tyR = proj_distr_ty env (e_ty muR) in
877+
878+
let coupling_ty =
879+
match side with
880+
| None -> tdistr (ttuple [tyL; tyR])
881+
| Some `Left -> tfun tyL tyR
882+
| Some `Right -> tfun tyR tyL in
883+
let g_form = TTC.tc1_process_prhl_form tc coupling_ty g in
884+
Core.t_equiv_coupling_r side g_form tc

src/phl/ecPhlRnd.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,8 @@ val t_equiv_rnd : ?pos:semrndpos -> oside -> (mkbij_t option) pair -> backward
2424
(* -------------------------------------------------------------------- *)
2525
val process_rnd : oside -> psemrndpos option -> rnd_infos_t -> backward
2626

27+
(* -------------------------------------------------------------------- *)
28+
val process_coupling : oside -> pformula -> backward
29+
2730
(* -------------------------------------------------------------------- *)
2831
val process_rndsem : reduce:bool -> oside -> pcodepos1 -> backward

0 commit comments

Comments
 (0)