Skip to content

Commit 425142c

Browse files
fpvandoornkim-em
andcommitted
refactor: improve the way simps traces messages (#789)
* Previously it was done with a configuration option, but it is much cleaner by using the option, so that we can use `trace[simps.verbose] ...` * Add `KVMap.updateBool` which simplifies some applications * Uncomment some tests of `simps` * Minor tweaks Co-authored-by: Scott Morrison <scott@tqft.net>
1 parent 69cf326 commit 425142c

File tree

4 files changed

+85
-81
lines changed

4 files changed

+85
-81
lines changed

Mathlib/Data/KVMap.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@ def eraseCore : List (Name × DataValue) → Name → List (Name × DataValue)
2121
def erase : KVMap → Name → KVMap
2222
| ⟨m⟩, k => ⟨eraseCore m k⟩
2323

24+
/-- update a Boolean entry based on its current value. -/
25+
def updateBool (m : KVMap) (k : Name) (f : Bool → Bool) : KVMap :=
26+
m.insert k <| DataValue.ofBool <| f <| m.getBool k
27+
2428
end Lean.KVMap

Mathlib/Tactic/Simps/Basic.lean

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ macro "initialize_simps_projections?" rest:simpsProj : command =>
338338
end Command
339339
end Lean.Parser
340340

341-
initialize registerTraceClass `Simps.verbose
341+
initialize registerTraceClass `simps.verbose
342342
initialize registerTraceClass `simps.debug
343343

344344
/-- Projection data for a single projection of a structure -/
@@ -503,14 +503,14 @@ def simpsApplyProjectionRules (str : Name) (rules : Array ProjectionRule) :
503503
/-- Auxilliary function for `simpsGetRawProjections`.
504504
Find custom projections declared by the user. -/
505505
def simpsFindCustomProjection (str : Name) (proj : ParsedProjectionData)
506-
(rawUnivs : List Level) (trc : Bool) : CoreM ParsedProjectionData := do
506+
(rawUnivs : List Level) : CoreM ParsedProjectionData := do
507507
let env ← getEnv
508508
let (rawExpr, nrs) ← MetaM.run' (getCompositeOfProjections str proj.origName.getString!)
509509
match env.find? (str ++ `Simps ++ proj.newName) with
510510
| some d@(.defnInfo _) =>
511511
let customProj := d.instantiateValueLevelParams! rawUnivs
512-
if trc then
513-
logInfo m!"[simps] > found custom projection for {proj.newName}:\n > {customProj}"
512+
trace[simps.verbose] "[simps] > found custom projection for {proj.newName}:\n > {
513+
customProj}"
514514
match (← MetaM.run' $ isDefEq customProj rawExpr) with
515515
| true => pure { proj with expr? := some customProj, projNrs := nrs, isChanged := true }
516516
| false =>
@@ -529,8 +529,9 @@ def simpsFindCustomProjection (str : Name) (proj : ParsedProjectionData)
529529

530530
/-- Auxilliary function for `simpsGetRawProjections`.
531531
Resolve a single notation class in `simpsFindAutomaticProjections`. -/
532+
-- currently unused
532533
def simpsResolveNotationClass (projs : Array ParsedProjectionData)
533-
(className : Name) (args : Array Expr) (eStr : Expr) (rawUnivs : List Level) (trc : Bool) :
534+
(className : Name) (args : Array Expr) (eStr : Expr) (rawUnivs : List Level) :
534535
MetaM (Array ParsedProjectionData) := do
535536
let env ← getEnv
536537
let classInfo := (getStructureInfo? env className).get!
@@ -551,38 +552,35 @@ def simpsResolveNotationClass (projs : Array ParsedProjectionData)
551552
trace[simps.debug] "info: ({relevantProj}, {rawExprLambda})"
552553
pure (relevantProj, rawExprLambda)
553554
let some pos := projs.findIdx? fun x ↦ some x.origName == relevantProj | do
554-
if trc then
555-
logInfo m!"[simps] > Warning: The structure has an instance for {className}, {""
555+
trace[simps.verbose] "[simps] > Warning: The structure has an instance for {className}, {""
556556
}but it is not definitionally equal to any projection."
557557
failure -- will be caught by `simpsFindAutomaticProjections`
558558
trace[simps.debug] "The raw projection is:\n {rawExprLambda}"
559559
projs.mapIdxM fun nr x ↦ do
560560
unless nr.1 = pos do return x
561561
if x.isChanged then
562-
if trc then
563-
logInfo m!"[simps] > Warning: Projection {relevantProj} is definitionally equal to\n {""
564-
}{rawExprLambda}\nHowever, this is not used since a custom simps projection is {""
565-
}specified by the user."
562+
trace[simps.verbose] "[simps] > Warning: Projection {relevantProj} is definitionally equal to{
563+
indentExpr rawExprLambda}\nHowever, this is not used since a custom simps projection is {
564+
""}specified by the user."
566565
return x
567-
if trc then
568-
logInfo m!"[simps] > Using notation from {className} for projection {relevantProj}."
566+
trace[simps.verbose] "[simps] > Using notation from {className} for projection {relevantProj}."
569567
return { x with expr? := some rawExprLambda }
570568

571569
/-- Auxilliary function for `simpsGetRawProjections`.
572570
Find custom projections, automatically found by simps.
573571
These come from algebraic notation classes, like `+`. -/
574-
-- if performance becomes a problem, possible heuristic: use the names of the projections to
575-
-- skip all classes that don't have the corresponding field.
572+
-- todo: just navigate all projections and check if there is one called "add"/"mul" etc.
573+
-- currently unused
576574
def simpsFindAutomaticProjections (str : Name) (projs : Array ParsedProjectionData)
577-
(strType : Expr) (rawUnivs : List Level) (trc : Bool) : CoreM (Array ParsedProjectionData) := do
575+
(strType : Expr) (rawUnivs : List Level) : CoreM (Array ParsedProjectionData) := do
578576
let env ← getEnv
579577
MetaM.run' <| forallTelescope strType fun args _ ↦ do
580578
let eStr := mkAppN (.const str rawUnivs) args
581579
let automaticProjs := notationClassAttr.getState env
582580
let mut projs := projs
583581
if args.size == 1 then -- can be wrong if additional type-class arguments??
584582
for (className, _) in automaticProjs do
585-
try projs ← simpsResolveNotationClass projs className args eStr rawUnivs trc
583+
try projs ← simpsResolveNotationClass projs className args eStr rawUnivs
586584
catch _ => pure ()
587585
return projs
588586

@@ -623,40 +621,38 @@ Optionally, this command accepts three optional arguments:
623621
has the attribute `@[simpsStructure]`.
624622
* The `rules` argument specifies whether projections should be added, renamed, used as prefix, and
625623
not used by default.
626-
* if `trc` is true, this tactic will trace information.
624+
* if `trc` is true, this tactic will trace information just as if
625+
`set_option trace.simps.verbose true` was set.
627626
-/
628627
def simpsGetRawProjections (str : Name) (traceIfExists : Bool := false)
629628
(rules : Array ProjectionRule := #[]) (trc := false) :
630629
CoreM (List Name × Array ProjectionData) := do
630+
withOptions (· |>.updateBool `trace.simps.verbose (trc || ·)) <| do
631631
let env ← getEnv
632-
let trc := trc || (← getOptions).getBool `trace.simps.verbose
633-
-- to do: double check tracing
634632
if let some data := (simpsStructure.getState env).find? str then
635633
-- We always print the projections when they already exists and are called by
636634
-- `initialize_simps_projections`.
637-
if traceIfExists || (← getOptions).getBool `trace.simps.verbose then
638-
logInfo <|
635+
withOptions (· |>.updateBool `trace.simps.verbose (traceIfExists || ·)) <| do
636+
trace[simps.verbose]
639637
projectionsInfo data.2.toList "Already found projection information for structure" str
640638
return data
641-
if trc then
642-
logInfo m!"[simps] > generating projection information for structure {str}."
639+
trace[simps.verbose] "[simps] > generating projection information for structure {str}."
643640
trace[simps.debug] "Applying the rules {rules}."
644641
let some strDecl := env.find? str
645642
| throwError "No such declaration {str}." -- maybe unreachable
646643
let rawLevels := strDecl.levelParams
647644
let rawUnivs := rawLevels.map Level.param
648645
let projs ← simpsApplyProjectionRules str rules
649-
let projs ← projs.mapM fun proj ↦ simpsFindCustomProjection str proj rawUnivs trc
646+
let projs ← projs.mapM fun proj ↦ simpsFindCustomProjection str proj rawUnivs
650647
-- the following will not work properly with Lean 4-style structure bundling
651-
-- let projs ← simpsFindAutomaticProjections str projs strDecl.type rawUnivs trc
648+
-- let projs ← simpsFindAutomaticProjections str projs strDecl.type rawUnivs
652649
let projs := projs.map (·.toProjectionData)
653-
-- make all proof non-default.
650+
-- make all proofs non-default.
654651
let projs ← projs.mapM fun proj ↦ do
655652
match (← MetaM.run' <| isProof proj.expr) with
656653
| true => pure { proj with isDefault := false }
657654
| false => pure proj
658-
if trc then
659-
logInfo <| projectionsInfo projs.toList "generated projections for" str
655+
trace[simps.verbose] projectionsInfo projs.toList "generated projections for" str
660656
simpsStructure.add str (rawLevels, projs)
661657
trace[simps.debug] "Generated raw projection data:\n{(rawLevels, projs)}"
662658
pure (rawLevels, projs)
@@ -716,8 +712,6 @@ structure Simps.Config where
716712
E.g. if we write `@[simps] def e : α × β ≃ β × α := ...` we will generate `e_apply` and not
717713
`e_apply_fst`. -/
718714
notRecursive := [`Prod, `PProd]
719-
/-- Output tracing messages. Can be set to `true` by writing `@[simps?]`. -/
720-
trace := false
721715
/-- Output debug messages. Not used much, use `set_option simps.debug true` instead. -/
722716
debug := false
723717
/-- [TODO] Add `@[to_additive]` to all generated lemmas. This can be set by marking the
@@ -780,7 +774,7 @@ def simpsGetProjectionExprs (tgt : Expr) (rhs : Expr) (cfg : Simps.Config) :
780774
let str := tgt.getAppFn.constName?.getD default
781775
-- the fields of the object
782776
let rhsArgs := rhs.getAppArgs.toList.drop params.size
783-
let (rawUnivs, projDeclata) ← simpsGetRawProjections str false #[] cfg.trace
777+
let (rawUnivs, projDeclata) ← simpsGetRawProjections str
784778
return projDeclata.map fun proj ↦
785779
(rhsArgs.getD (a₀ := default) proj.projNrs.head!,
786780
{ proj with
@@ -818,8 +812,7 @@ def simpsAddProjection (declName : Name) (type lhs rhs : Expr) (args : Array Exp
818812
let eqAp := mkApp3 (mkConst `Eq [lvl]) type lhs rhs
819813
let declType ← mkForallFVars args eqAp
820814
let declValue ← mkLambdaFVars args prf
821-
if cfg.trace then
822-
logInfo m!"[simps] > adding projection {declName}:\n > {declType}"
815+
trace[simps.verbose] "[simps] > adding projection {declName}:\n > {declType}"
823816
try
824817
addDecl <| .thmDecl {
825818
name := declName
@@ -832,7 +825,9 @@ def simpsAddProjection (declName : Name) (type lhs rhs : Expr) (args : Array Exp
832825
addSimpTheorem simpExtension declName true false .global <| eval_prio default
833826
-- cfg.attrs.mapM fun nm ↦ setAttribute nm declName tt -- todo: deal with attributes
834827
if let some tgt := cfg.addAdditive then
835-
ToAdditive.addToAdditiveAttr declName ⟨false, cfg.trace, tgt, none, true, ref⟩
828+
ToAdditive.addToAdditiveAttr declName
829+
-- tracing seems to fail
830+
false, (← getOptions) |>.getBool `trace.to_additive, tgt, none, true, ref⟩
836831

837832
/--
838833
Perform head-structure-eta-reduction on expression `e`. That is, if `e` is of the form
@@ -919,8 +914,7 @@ partial def simpsAddProjections (nm : Name) (type lhs rhs : Expr)
919914
if !rhsWhnf.getAppFn.isConstOf ctor then
920915
-- if I'm about to run into an error, try to set the transparency for `rhsMd` higher.
921916
if cfg.rhsMd == .reducible && (mustBeStr || !todoNext.isEmpty || !toApply.isEmpty) then
922-
if cfg.trace then
923-
logInfo m!"[simps] > The given definition is not a constructor {""
917+
trace[simps.verbose] "[simps] > The given definition is not a constructor {""
924918
}application:\n > {rhsWhnf}\n > Retrying with the options {""
925919
}\{rhsMd := semireducible, simpRhs := tt}."
926920
let nms ← simpsAddProjections nm type lhs rhs args mustBeStr
@@ -988,16 +982,15 @@ partial def simpsAddProjections (nm : Name) (type lhs rhs : Expr)
988982
If `shortNm` is true, the generated names will only use the last projection name.
989983
If `trc` is true, trace as if `trace.simps.verbose` is true. -/
990984
def simpsTac (ref : Syntax) (nm : Name) (cfg : Simps.Config := {}) (todo : List String := [])
991-
(trc := false) : AttrM (Array Name) := do
985+
(trc := false) : AttrM (Array Name) :=
986+
withOptions (· |>.updateBool `trace.simps.verbose (trc || ·)) <| do
992987
let env ← getEnv
993988
let some d := env.find? nm | throwError "Declaration {nm} doesn't exist."
994989
let lhs : Expr := mkConst d.name <| d.levelParams.map Level.param
995990
let todo := todo.eraseDup.map fun proj ↦ "_" ++ proj
996-
let mut cfg :=
997-
{ cfg with trace := cfg.trace || (← getOptions).getBool `trace.simps.verbose || trc }
991+
let mut cfg := cfg
998992
if let some addAdditive := ToAdditive.findTranslation? env nm then
999-
if cfg.trace then
1000-
logInfo m!"[simps] > @[to_additive] will be added to all generated lemmas."
993+
trace[simps.verbose] "[simps] > @[to_additive] will be added to all generated lemmas."
1001994
cfg := { cfg with addAdditive }
1002995
MetaM.run' <| simpsAddProjections ref d.levelParams
1003996
nm d.type lhs (d.value?.getD default) #[] (mustBeStr := true) cfg todo []

Mathlib/Tactic/ToAdditive.lean

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Authors: Mario Carneiro, Yury Kudryashov, Floris van Doorn, Jon Eugster
55
Ported by: E.W.Ayers
66
-/
77
import Mathlib.Data.String.Defs
8+
import Mathlib.Data.KVMap
89
import Mathlib.Lean.Expr.ReplaceRec
910
import Std.Lean.NameMapAttribute
1011
import Std.Data.Option.Basic
@@ -214,9 +215,9 @@ def findTranslation? (env : Environment) : Name → Option Name :=
214215
/-- Add a (multiplicative → additive) name translation to the translations map. -/
215216
def insertTranslation (src tgt : Name) : CoreM Unit := do
216217
if let some tgt' := findTranslation? (←getEnv) src then
217-
throwError "Already exists translation {src} ↦ {tgt'}"
218+
throwError "The translation {src} ↦ {tgt'} already exists"
218219
modifyEnv (ToAdditive.translations.addEntry · (src, tgt))
219-
trace[to_additive] "Added translation {src} ↦ {tgt}."
220+
trace[to_additive] "Added translation {src} ↦ {tgt}"
220221

221222
/-- Get whether or not the replace-all flag is set. If this is true, then the
222223
additiveTest heuristic is not used and all instances of multiplication are replaced.
@@ -460,14 +461,15 @@ def copySimpAttribute (src tgt : Name) : CoreM Unit := do
460461
-- [todo] other simp theorems
461462
let some ext ← getSimpExtension? `simp | return
462463
let thms ← ext.getTheorems
463-
if thms.isLemma (.decl src)) || thms.isLemma (.decl tgt) then
464+
if !thms.isLemma (.decl src) || thms.isLemma (.decl tgt) then
464465
return
466+
trace[to_additive_detail] "Adding @[simp] attribute to {tgt}."
465467
-- [todo] how to get prio data from SimpTheorems?
466468
Lean.Meta.addSimpTheorem ext tgt
467469
(post := true)
468470
(inv := false)
469-
(attrKind := AttributeKind.global)
470-
(prio := 1000) |>.run'
471+
(attrKind := .global)
472+
(prio := eval_prio default) |>.run'
471473

472474
/-- Copy the instance attribute in a `to_additive`
473475
@@ -476,6 +478,7 @@ def copyInstanceAttribute (src tgt : Name) : CoreM Unit := do
476478
if (← isInstance src) then
477479
let prio := (← getInstancePriority? src).elim 100 id
478480
let attr_kind := (← getInstanceAttrKind? src).elim AttributeKind.global id
481+
trace[to_additive_detail] "Making {tgt} an instance with priority {prio}."
479482
addInstance tgt attr_kind prio |>.run'
480483

481484
/-- [todo] add more attributes. -/
@@ -695,7 +698,7 @@ def targetName (src tgt : Name) (allowAutoName : Bool) : CoreM Name := do
695698
let pre := pre.mapPrefix <| findTranslation? (← getEnv)
696699
let (pre1, pre2) := pre.splitAt (depth - 1)
697700
if tgt == pre2.str tgt_auto && !allowAutoName then
698-
dbg_trace "to_additive correctly autogenerated target name for {src}. {"\n"
701+
logInfo m!"to_additive correctly autogenerated target name for {src}. {"\n"
699702
}You may remove the explicit argument {tgt}."
700703
let res := if tgt == .anonymous then pre.str tgt_auto else pre1 ++ tgt
701704
-- we allow translating to itself if `tgt == src`, which is occasionally useful for `additiveTest`
@@ -741,22 +744,18 @@ private def elabToAdditive : Syntax → CoreM ValueType
741744

742745
/-- `addToAdditiveAttr src val` adds a `@[to_additive]` attribute to `src` with configuration `val`.
743746
See the attribute implementation for more details. -/
744-
def addToAdditiveAttr (src : Name) (val : ValueType) : AttrM Unit := do
745-
let shouldTrace := val.trace || ((← getOptions) |>.getBool `trace.to_additive)
746-
withOptions
747-
(fun o ↦ o |>.setBool `to_additive.replaceAll val.replaceAll
748-
|>.setBool `trace.to_additive shouldTrace) <| do
747+
def addToAdditiveAttr (src : Name) (val : ValueType) : AttrM Unit :=
748+
withOptions (· |>.setBool `to_additive.replaceAll val.replaceAll
749+
|>.updateBool `trace.to_additive (val.trace || ·)) <| do
749750
let tgt ← targetName src val.tgt val.allowAutoName
750-
if let some tgt' := findTranslation? (← getEnv) src then
751-
throwError "{src} already has a to_additive translation {tgt'}."
752751
insertTranslation src tgt
753752
let firstMultArg ← MetaM.run' <| firstMultiplicativeArg src
754753
if firstMultArg != 0 then
755754
trace[to_additive_detail] "Setting relevant_arg for {src} to be {firstMultArg}."
756755
relevantArgAttr.add src firstMultArg
757756
if (← getEnv).contains tgt then
758-
-- since tgt already exists, we just need to add a translation src ↦ tgt
759-
-- and also src.𝑥 ↦ tgt.𝑥' for any subfields.
757+
-- since `tgt` already exists, we just need to add translations `src.x ↦ tgt.x'`
758+
-- for any subfields.
760759
proceedFields src tgt
761760
else
762761
-- tgt doesn't exist, so let's make it

test/Simps.lean

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -887,35 +887,42 @@ example (x : Bool) {z} (h : id x = z) : myRingHom x = z := by
887887

888888
/- check interaction with the `@[to_additive]` attribute -/
889889

890-
@[to_additive, simps]
890+
-- set_option trace.simps.debug true
891+
892+
@[to_additive instAddProd] -- todo: want to write simps here
891893
instance {M N} [Mul M] [Mul N] : Mul (M × N) := ⟨λ p q => ⟨p.1 * q.1, p.2 * q.2⟩⟩
892894

893-
-- todo: to_additive interaction
894-
-- run_cmd liftTermElabM <| do
895-
-- let env ← getEnv
896-
-- guard <| env.find? `prod.Mul_mul |>.isSome
897-
-- guard <| env.find? `prod.Add_add |>.isSome
898-
-- -- has_attribute `to_additive `prod.Mul
899-
-- -- has_attribute `to_additive `prod.Mul_mul
900-
-- guard <| hasSimpAttribute env `Prod.Mul_mul
901-
-- guard <| hasSimpAttribute env `Prod.Add_add
895+
attribute [simps] instMulProd
896+
897+
run_cmd liftTermElabM <| do
898+
let env ← getEnv
899+
guard <| env.find? `instMulProd_mul |>.isSome
900+
guard <| env.find? `instAddProd_add |>.isSome
901+
-- hasAttribute `to_additive `instMulProd
902+
-- hasAttribute `to_additive `instMulProd_mul
903+
guard <| hasSimpAttribute env `instMulProd_mul
904+
guard <| hasSimpAttribute env `instAddProd_add
902905

906+
-- todo: heterogenous notation_class
903907
-- example {M N} [Mul M] [Mul N] (p q : M × N) : p * q = ⟨p.1 * q.1, p.2 * q.2⟩ := by simp
904908
-- example {M N} [Add M] [Add N] (p q : M × N) : p + q = ⟨p.1 + q.1, p.2 + q.2⟩ := by simp
905909

906910
/- The names of the generated simp lemmas for the additive version are not great if the definition
907911
had a custom additive name -/
908-
@[to_additive my_add_instance, simps]
912+
@[to_additive my_add_instance] -- todo: want to write simps here
909913
instance my_instance {M N} [One M] [One N] : One (M × N) := ⟨(1, 1)⟩
914+
attribute [simps] my_instance
910915

911-
-- run_cmd liftTermElabM <| do
912-
-- get_decl `my_instance_one
913-
-- get_decl `my_add_instance_zero
914-
-- -- has_attribute `to_additive `my_instance -- todo
915-
-- -- has_attribute `to_additive `my_instance_one
916-
-- guard <| hasSimpAttribute `my_instance_one
917-
-- guard <| hasSimpAttribute `my_add_instance_zero
916+
run_cmd liftTermElabM <| do
917+
let env ← getEnv
918+
guard <| env.find? `my_instance_one |>.isSome
919+
guard <| env.find? `my_add_instance_zero |>.isSome
920+
-- hasAttribute `to_additive `my_instance -- todo
921+
-- hasAttribute `to_additive `my_instance_one
922+
guard <| hasSimpAttribute env `my_instance_one
923+
guard <| hasSimpAttribute env `my_add_instance_zero
918924

925+
-- todo: heterogenous notation_class
919926
-- example {M N} [One M] [One N] : (1 : M × N) = ⟨1, 1⟩ := by simp
920927
-- example {M N} [Zero M] [Zero N] : (0 : M × N) = ⟨0, 0⟩ := by simp
921928

@@ -947,7 +954,7 @@ structure MyType :=
947954
-- guard_hyp y : { x : Fin 3 // true }
948955
-- contradiction
949956

950-
-- todo: to_additive interaction
957+
-- this test might not apply to Lean 4
951958
-- /- Test that `to_additive` copies the `@[_rfl_lemma]` attribute correctly -/
952959
-- @[to_additive, simps]
953960
-- def monoid_hom.my_comp {M N P : Type _} [mul_one_class M] [mul_one_class N] [mul_one_class P]
@@ -969,13 +976,14 @@ structure MyType :=
969976
-- rfl
970977

971978
-- test that `to_additive` works with a custom name
972-
@[to_additive some_test2, simps]
979+
@[to_additive some_test2] -- todo: want to write simps here
973980
def some_test1 (M : Type _) [CommMonoid M] : Subtype (λ _ : M => True) := ⟨1, trivial⟩
974981

975-
-- todo: to_additive interaction
976-
-- run_cmd liftTermElabM <| do
977-
-- let env ← getEnv
978-
-- guard <| env.find? `some_test2_coe |>.isSome
982+
attribute [simps] some_test1
983+
984+
run_cmd liftTermElabM <| do
985+
let env ← getEnv
986+
guard <| env.find? `some_test2_val |>.isSome
979987

980988
end
981989

0 commit comments

Comments
 (0)