Apply simplifier to arbitrary term - isabelle

I have a term in mind, say "foo 1 2 a b", and I'd like to know if Isabelle can simplify it for me. I'd like to write something like
simplify "foo 1 2 a b"
and have the simplified term printed in the output buffer. Is this possible?
My current 'workaround' is:
lemma "foo 1 2 a b = blah"
apply simp
which works fine but looks a bit hacky.
What doesn't work (in my case) is:
value "foo 1 2 a b"
because a and b are unbound variables, and because my foo involves infinite sets and other fancy stuff, which the code generator chokes on.

There is no built-in feature AFAIK, but there are several ways to achieve this. You have already discovered one of them, namely state the term as a lemma and then invoke the simplifier. The drawback is that this cannot be used in all contexts, for example, not inside an apply proof script.
Alternatively, you can invoke the simplifier via the attribute [simplified]. This works in all contexts via the thm command and produces the output in the output buffer. First, the term must be injected into a theorem, then you can apply simplify to the theorem and display the result with thm. Here is the preparatory stuff that can go into your theory of miscellaneous stuff.
definition simp :: "'a ⇒ bool" where "simp _ = True"
notation (output) simp ("_")
lemma simp: "simp x" by(simp add: simp_def)
Then, you can write
thm simp[of "foo 1 2 a b", simplified]
and see the simplified term in the output window.
The evaluation mechanism is probably not what you want, because evaluation uses a different set of rewrite rules (namely the code equations) than the simplifier normally uses (the simpset). Therefore, this is likely to evaluate to a different term than by applying the simplifier. To see the difference, apply code_simp instead of simp in your approach with lemma "foo 1 2 a b = blah". The proof method code_simp uses the code equations just like value [simp] used to.

When using the value command, the evaluation of the argument is conducted by registered evaluators (see the Reference Manual of Isabelle2013-2).
It used to be possible to explicitly choose an evaluator in previous versions of Isabelle (e.g., Isabelle2013-2) by giving an extra argument to the value command. E.g.,
value [simp] "foo 1 2 a b"
It seems that in Isabelle2014 this parameter was dropped and according to the Reference Manual of Isabelle2014, the strategy is now fixed to first use ML code generation and in case this fails, normalization by evaluation.
From the NEWS file in the development version (e82c72f3b227) of Isabelle it seems as if this parameter will be enabled again in the upcoming Isabelle release.
UPDATE: As Andreas pointed out, value [simp] does not use the same set of simplification rules as apply simp. So even if available, the solution I described above will most likely not yield the result you want.

Related

Fragile rule application in Isabelle

I was playing around with an example from the Isabelle/HOL tutorial to get a better understanding on the correspondence between Isar and Tactics proofs.
This is a version which works:
lemma rtrancl_converseD: "(x,y) ∈ (r ^-1 )^* ⟹ (y,x) ∈ r^* "
proof (induct y rule: rtrancl_induct)
case base
then show ?case ..
next case (step y z)
then have "(z, y) ∈ r" using rtrancl_converseD by simp
with `(y,x)∈ r^*` show "(z,x) ∈ r^*" using [[unify_trace_failure]]
apply (subgoal_tac "1=(1::nat)")
apply (rule converse_rtrancl_into_rtrancl)
apply simp_all
done
qed
I want to instantiate converse_rtrancl_into_rtrancl which proofs (?a, ?b) ∈ ?r ⟹ (?b, ?c) ∈ ?r^* ⟹ (?a, ?c) ∈ ?r^* .
But without the seemingly nonsensical apply (subgoal_tac "1=(1::nat)") line this errors with
Clash: r =/= Transitive_Closure.rtrancl
Failed to apply proof method⌂:
using this:
(y, x) ∈ r^*
(z, y) ∈ r
goal (1 subgoal):
1. (z, x) ∈ r^*
If I fully instantiate the rule apply (rule converse_rtrancl_into_rtrancl[of z y r x]) this becomes Clash: z__ =/= ya__.
This leaves me with three questions: Why does this specific case break? How can I fix it? And how can I figure out what went wrong in these cases since I can't really understand what the unify_trace_failure message wants to tell me.
rule-tactics are usually sensitive to the order of premises. The order of premises in converse_rtrancl_into_rtrancl and in your proof state don't match. Switching the order of premises in the proof state using rotate_tac will make them match the rule, so that you can directly apply fact like this:
... show "(z,x) ∈ r^*"
apply (rotate_tac)
apply (fact converse_rtrancl_into_rtrancl)
done
Or, if you want to include some kind of rule tactic, this would look like this:
apply (rotate_tac)
apply (erule converse_rtrancl_into_rtrancl)
apply (assumption)
(I personally don't use apply scripts ever in my everyday work. So apply-style gurus might know more elegant ways of handling this kind of situation. ;) )
Regarding your 1=(1::nat) / simp_all fix:
The whole goal can directly be solved by simp_all. So, attempts with adding stuff like 1=1 probably did not really tell you a lot about how much the other methods contributed to solving the proof.
However, the additional assumption seems to actually help Isabelle match converse_rtrancl_into_rtrancl correctly. (Don't ask me why!) So, one could indeed circumvent the problem by adding this spurious assumption and then eliminating it with refl again like:
apply (subgoal_tac "1=(1::nat)")
apply (erule converse_rtrancl_into_rtrancl)
apply (assumption)
apply (rule refl)
This does not look particularly elegant, of course.
The [[unify_trace_failure]] probably only really helps if one is familiar with the internal workings of Nipkow's higher-order unification algorithm. (I'm not.) I think the hint for the future here would really be that one must look closely at the order of premises for some tactics (rather than at the unifier debug output).
I found an explanation in the Isar reference 6.4.3 .
The with b1..bn command is equivalent to from b1..bn and this, i.e. it enters the proof chaining mode which adds them as (structured) assumptions to proof methods.
Basic proof methods (such as rule) expect multiple facts to be given
in their proper order, corresponding to a prefix of the premises of
the rule involved. Note that positions may be easily skipped using
something like from _ and a and b, for example. This involves the
trivial rule PROP ψ =⇒ PROP ψ, which is bound in Isabelle/Pure as “_”
(underscore).
Automated methods (such as simp or auto) just insert any given facts
before their usual operation. Depending on the kind of procedure
involved, the order of facts is less significant here.
Given the information about the 'with' translation and that rule expects chained facts in order, we could try to flip the chained facts. And indeed this works:
from this and `(y,x)∈ r^*` show "(z,x) ∈ r^*"
by (rule converse_rtrancl_into_rtrancl)
I think "6.4.3 Fundamental methods and attributes" is also relevant because it describes how the basic methods interact with incoming facts. Notably, the '-' noop which is sometimes used when starting proofs turns forward chaining into assumptions on the goal.
with `(y,x)∈ r^*` show "(z,x) ∈ r^*"
apply -
apply (rule converse_rtrancl_into_rtrancl; assumption)
done
This works because the first apply consumes all chained facts so the second apply is pure backwards chaining. This is also why the subgoal_tac or rotate_tac worked, but only if they are in seperate apply commands.

Is it possible to "free" top-level universally quantified variables using tactics in Isabelle?

In short, I would like to go from this:
proof (prove)
goal (1 subgoal):
1. ⋀ myVar . somePredicate myVar
to this:
proof (prove)
goal (1 subgoal):
1. somePredicate myVar
by using tactics. The only solution I can find is to write a new lemma for example:
lemma myPredicateHolds_aux : "somePredicate myVar"
sorry
and then the original ⋀ myVar . somePredicate myVar usually can be solved by writing:
using myPredicateHolds_aux by blast
but I wonder whether there is a better way (using tactics), for convenience, and because, if the property is very intricate, blast may fail.
The proof (prove) suggests you're writing a proof script, in which case you can use subgoal for myVar. The "isar-ref" manual says a little more about it, I think (though it may be a bit dense).
You can also, and I believe this is usually the "preferred" way to do it, go into structured Isar proof mode and use fix:
proof -
fix myVar
show "somePredicate myVar"
proof ...

Isabelle return numbers instead of Suc(Suc( ... 0 ))

When I use value to find out some value of a function that returns natural numbers, I always obtain the answer in the form of iterated Successor functions of 0, i.e., Suc(Suc( ... 0 )) which can be hard to read sometimes.
Is there a way to output directly the number that Isabelle returns?
This is something I wanted to fix some time ago but apparently I forgot. Carcigenate's guess is incorrect; this is indeed the fully evaluated result. The problem is just that natural numbers are printed in this unwieldy way.
You can do the following things:
The easiest way is to convert the number to an integer, i.e. instead of value "foo x y z" (where foo x y z is the expression of type nat that you want to eavluate) you write value "int (foo x y z)".
You can add ~~/src/HOL/Library/Code_Target_Numeral to your imports. This makes Isabelle's code generator use arbitrary-precision integers of the target language (in case of value, that's ML and its GMP-based integers) instead of the inefficient successor notation. As a side effect, this also prints natural numbers in a nicer way.
You can add the following code to your theory, which changes the way the value command displays natural numbers:
Code:
lemma Suc_numeral_bit0: "Suc (numeral (Num.Bit0 n)) = numeral (Num.Bit1 n)"
by (subst Suc_numeral) simp
lemma Suc_numeral_bit1: "Suc (numeral (Num.Bit1 n)) = numeral (Num.Bit0 (n + Num.One))"
by (subst Suc_numeral) simp
lemmas [code_post] =
One_nat_def [symmetric] Suc_numeral_bit0 Suc_numeral_bit1 Num.Suc_1 Num.arith_simps
Note that the value command is a diagnostic command only. It is mainly used for quick plausibility tests and debugging of code generation setups, and getting it to work can sometimes be tricky.
By default, value relies on the code generator, i.e. Isabelle needs to know how to generate executable code for the expression, and if it cannot do that, value will probably fail. (It can sometimes also fall back to some other strategies, normalisation by evaluation or evaluation by simplification, but those usually don't provide useful output)
I'm just telling you this so that you know what to expect from the value command and don't get the impression that this is some integral part of Isabelle that people use all the time.

From a 'value' to a 'lemma'

I have a rather large term foo. When I type
value "foo"
then Isabelle evaluates foo to a value, say foo_value. I would now like to prove the following lemma.
lemma "foo = foo_value"
What proof method should I use? I tried try, but that timed out. I guess I could proceed manually by unfolding the various definitions that occur in foo, but surely I should be able to tap into whatever mechanism the value command is using, right?
There are three proof methods that correspond to the different evaluation mechanisms of value:
eval uses the code generator; it corresponds to value [code]. The proof succeeds if the generated ML code evaluates to True.
normalization compiles the statement to a symbolic normalisation engine in ML. It mimicks value [nbe].
code_simp uses Isabelle's simplifier as an evaluator. It corresponds to value [simp].
The tutorial on code generation describes these proof methods in more detail. eval and normalization act like oracles, i.e., they bypass Isabelle's kernel whereas every evaluation step of code_simp goes through the kernel. Usually, eval is faster than normalization and normalization is faster than code_simp.
I am not sure whether it works in all cases, but you could try:
lemma "foo = foo_value"
by eval
In many cases, by simp should also work and I guess eval is kind of an oracle (in the sense that it is not fully verified by the kernel; please somebody correct me if I am wrong).

Drop a variable in a goal in apply style

Having recently learnt how to drop an unwanted premise in an apply-style proof, I now wonder how to drop an unneeded variable. That is, suppose I have the goal
1. !!x y z. A ⟹ B ⟹ C
where y does not appear in A, B or C. How can I transform it to the following?
1. !!x z. A ⟹ B ⟹ C
triv_forall_equality is indeed the Pure rule to strip redundant parameters. There is also prune_params_tac to do that as ML tactic, it operates on all subgoals. Note that the latter is not exposed as Isar proof method, since it is hardly ever required in practice: tools like simp and auto already include it by default.
Note that the approach via (simp only: triv_forall_equality) works in many situations, but there is also a snag: the only modifier in Isabelle/HOL does a bit more than "only" using the given simp rules. It includes things like arithmetic solvers, which might cause surprise or confusion some situations.
To imitate prune_params_tac precisely within the Isar method language, you could use (unfold triv_forall_equality) although there is a tiny conceptual snag: its use of arbitrary rewriting instead of just infolding equations c = t is just a historical accident.
A simple:
apply simp
will do the trick. If you don't want to perform any other transformations on the goal state, you can try:
apply (simp only: triv_forall_equality)
which will remove the unnecessary meta-quantifiers, but otherwise not modify the goal state.

Resources