Polynomial evaluation in Isabelle - definition

In the book Concrete Semantics, exercise 2.11 writes:
Define arithmetic expressions in one variable over integers
(type int) as a data type:
datatype exp = Var | Const int | Add exp exp | Mult exp exp
Define a function eval :: exp ⇒ int ⇒ int such that eval e x evaluates e at the value x. A polynomial can be represented as a list of coefficients, starting with the constant. For example, [4, 2, − 1, 3] represents the polynomial 4+2x−x 2 +3x 3 . Define a function evalp :: int list ⇒ int ⇒ int that evaluates a polynomial at the given value. Define a function coeffs :: exp ⇒ int list that transforms an
expression into a polynomial. This may require auxiliary functions. Prove that coeffs preserves the value of the expression: evalp (coeffs e) x = eval e x. Hint: consider the hint in Exercise 2.10.
As a first try, and because former exercises encouraged to write iterative versions of functions, I wrote the following:
datatype exp = Var | Const int | Add exp exp | Mult exp exp
fun eval :: "exp ⇒ int ⇒ int" where
"eval Var x = x"
| "eval (Const n) _ = n"
| "eval (Add e1 e2) x = (eval e1 x) + (eval e2 x)"
| "eval (Mult e1 e2) x = (eval e1 x) * (eval e2 x)"
fun evalp_it :: "int list ⇒ int ⇒ int ⇒ int ⇒ int" where
"evalp_it [] x xpwr acc = acc"
| "evalp_it (c # cs) x xpwr acc = evalp_it cs x (xpwr*x) (acc + c*xpwr)"
fun evalp :: "int list ⇒ int ⇒ int" where
"evalp coeffs x = evalp_it coeffs x 1 0"
fun add_coeffs :: "int list ⇒ int list ⇒ int list" where
"add_coeffs [] [] = []"
| "add_coeffs (a # as) (b# bs) = (a+b) # (add_coeffs as bs)"
| "add_coeffs as [] = as"
| "add_coeffs [] bs = bs"
(there might be some zip function to do this)
fun mult_coeffs_it :: "int list ⇒ int list ⇒ int list ⇒ int list ⇒ int list" where
"mult_coeffs_it [] bs accs zeros = accs"
| "mult_coeffs_it (a#as) bs accs zeros =
mult_coeffs_it as bs (add_coeffs accs zeros#bs) (0#zeros)"
fun mult_coeffs :: "int list ⇒ int list ⇒ int list" where
"mult_coeffs as bs = mult_coeffs_it as bs [] []"
fun coeffs :: "exp ⇒ int list" where
"coeffs (Var) = [0,1]"
| "coeffs (Const n) = [n]"
| "coeffs (Add e1 e2) = add_coeffs (coeffs e1) (coeffs e2)"
| "coeffs (Mult e1 e2) = mult_coeffs (coeffs e1) (coeffs e2)"
I tried to verify the sought theorem
lemma evalp_coeffs_eval: "evalp (coeffs e) x = eval e x"
but could not. Once I got an advice that writing good definitions is very important in theorem proving, though the adviser did not give details.
So, what is the problem with my definitions, conceptually? Please do not write the good definitions but point out the conceptual problems with my definitions.
UPDATE: upon advice I started to use
fun evalp2 :: "int list ⇒ int ⇒ int" where
"evalp2 [] v = 0"|
"evalp2 (p#ps) v = p + v * (evalp2 ps v) "
and looking into src/HOL/Algebra/Polynomials.thy I formulated
fun add_cffs :: "int list ⇒ int list ⇒ int list" where
"add_cffs as bs =
( if length as ≥ length bs
then map2 (+) as (replicate (length as - length bs) 0) # bs
else add_cffs bs as)"
but that did not help much, simp add: algebra_simps or arith did not solve the corresponding subgoal.

Some hints:
Try running quickcheck and nitpick on the Lemma until no more counter example is found. For the Lemma in your question I get the following counter example:
Quickcheck found a counterexample:
e = Mult Var Var
x = - 2
Evaluated terms:
evalp (coeffs e) x = - 10
eval e x = 4
Try to prove some useful Lemmas about your auxiliary functions first. For example:
lemma "evalp (mult_coeffs A B) x = evalp A x * evalp B x"
Read about calculating with polynomials (e.g. https://en.wikipedia.org/wiki/Polynomial_ring) and choose definitions close to what mathematicians do. Although this one might spoil the fun of coming up with a definition on your own.

Related

Compile-time invariant/property checking in Elm

I have some value constraints/properties that I want to check at compile-time. In this example, I want to track whether a vector is normalized or not. I think I have a solution, using type tags, but I need someone with some Elm/FP experience to tell me if I have missed something obvious. Thank you.
module TagExperiment exposing (..)
type Vec t = Vec Float Float Float
type Unit = Unit
type General = General
toGeneral : Vec t -> Vec General
toGeneral (Vec x y z) =
Vec x y z
scaleBy : Vec t -> Vec t -> Vec t
scaleBy (Vec bx by bz) (Vec ax ay az) =
{- Operate on two General's or two Unit's. If you have one of each,
then the Unit needs to be cast to a General.
-}
let
mag =
sqrt ((bx * bx) + (by * by) + (bz * bz))
in
Vec (ax * mag) (ay * mag) (az * mag)
-- These cases work as desired.
a : Vec Unit
a = Vec 0 0 1
b : Vec General
b = Vec 2 2 2
d : Vec Unit
d = scaleBy a a
e : Vec General
e = scaleBy b b
g : Vec General
g = scaleBy (toGeneral a) b
h : Vec General
h = scaleBy b (toGeneral a)
-- Here is where I have trouble.
c : Vec t -- unknown... uh-oh
c = Vec 3 3 3
f : Vec t -- still unknown... sure
f = scaleBy c c
i : Vec Unit -- wrong !!!
i = scaleBy a c
j : Vec Unit -- wrong !!!
j = scaleBy c a
k : Vec General -- lucky
k = scaleBy b c
l : Vec General -- lucky
l = scaleBy c b
{- The trouble is that I am not required to specify a tag for c. I guess the
solution is to write "constructors" and make the built-in Vec constructor
opaque?
-}
newUnitVec : Float -> Float -> Float -> (Vec Unit)
newUnitVec x y z =
-- add normalization
Vec x y z
newVec : Float -> Float -> Float -> (Vec General)
newVec x y z =
Vec x y z
Yes, without Dependant Types the most ergonomic way to ensure constraints on values at compile time is to use opaque types along with a "Parse" approach.
Perhaps something like:
module Vec exposing (UnitVector, Vector, vectorToUnit)
type UnitVector
= UnitVector Float Float Float
type Vector
= Vector Float Float Float
vectorToUnit : Vector -> Maybe UnitVector
vectorToUnit (Vector x y z) =
case ( x, y, z ) of
( 0, 0, 0 ) ->
Nothing
_ ->
normalize x y z
Then, with the only ways to get a UnitVector both defined inside this module and known to obey the constraints, then any time you see a UnitVector at compile-time it is correct to assume the constraints are met.
For vectors in particular, it may be worth having a look at ianmackenzie/elm-geometry for comparison?

Multivariate function composition

Let's consider the following:
f: Int -> Int -> Int -> Int
f a b c = a + b + c
g: Int -> Int
g x = x * 2
now, I'd like to create a composed function like
f(g(a), g(b), g(c))
how to use << and >> operators to achieve that? Is it even possible to compose multivariadic functions?
how to use << and >> operators to achieve that? Is it even possible to compose multivariadic functions?
Yes, easily:
h = flip flip g << ((<<) (<<)) << ((>>) g) << f << g
h is a <function> : number -> number -> number -> number, which is the same as h a b c = f (g a) (g b) (g c).
I think we don't need to dig deep in the correctness, since it's obvious that the version with arguments is much more readable.
Multivariadic does not really exist in Elm. Best I can suggest is
f : Int -> Int -> Int -> Int
f a b c =
[ a, b, c ] |> List.map ((<|) g) |> List.sum
g : Int -> Int
g x =
x * 2
Note that you had an error in the type signature for g

Simplifying a two-parameter anonymous function in Elm

In Elm, if I have an anonymous function
(\f x -> f x)
I can simplify it to
(<|)
Can the same be done for a two-parameter function where the parameters are arguments to another function?
(\x y -> f x y |> g)
I thought I could simply use
(f |> g)
but the compiler complains about the types.
Specifically, in one of the cases for my update function, I have something like this:
let
msgNames = [Foo, Bar]
values = ["f", "b"] // These values are actually derived
// by a more complicated operation
model_ = List.map2 (<|) msgNames values
|> List.foldl (\msg mod -> update msg mod |> Tuple.first)
model
in
( model_, Cmd.none )
I am trying to simplify the anonymous function argument to List.foldl to something like (update |> Tuple.first), but I get the following error from the compiler:
The right side of (|>) is causing a type mismatch.
159| update |> Tuple.first)
^^^^^^^^^^^
(|>) is expecting the right side to be a:
(Msg -> Model -> ( Model, Cmd Msg )) -> a
But the right side is:
(( Model, Cmd Msg )) -> Model
We can follow a few steps to simplify:
(\x y -> f x y |> g)
... can be written as
(\x y -> g (f x y))
... can be written as
(\x -> g << f x)
One more step and things get a little more confusing:
(((<<) g) << f)
This matches what you get from pointfree.io (which is Haskell where function composition is done using the . operator):
(g .) . f
If you are trying to improve readability, you might just want to make your own infix function:
infixr 9 <<<
(<<<) : (c -> d) -> (a -> b -> c) -> (a -> b -> d)
(<<<) g f x y =
g (f x y)
And now you can use it like this:
(g <<< f)

Hofstadter Female and Male sequences in SML

This is my first SML program. I am trying to write a function that returns the first number to the nth number of Hofstadter's Female or Male sequence in list form. What I have so far is:
val m = fn (n) => if n = 0 then 1 :: [] else m f (n - 1);
val f = fn (n) => if n = 0 then 0 :: [] else f m (n - 1);
You can learn about the sequence here:
https://en.wikipedia.org/wiki/Hofstadter_sequence#Hofstadter_Female_and_Male_sequences
The error that I am getting is:
[opening sequence.sml]
sequence.sml:1.49 Error: unbound variable or constructor: f
sequence.sml:1.47-1.58 Error: operator is not a function [tycon mismatch]
operator: int list
in expression:
(m <errorvar>) (n - 1)
val it = () : unit
How can I correct this?
I ended up taking this approach:
fun
m (n) = if n = 0 then 0 else n - (f (m (n - 1)))
and
f (n) = if n = 0 then 1 else n - (m (f (n - 1)));
val seq = fn n => List.tabulate((n), f);
It is quite slow. If anybody has a faster version, then I'd love to see it.
Although you have already fixed them, there were two problems with your original approach:
Function application is left-associative in SML so m f (n - 1) was being interpreted as (m f) (n - 1), not the desired m (f (n - 1)). You can fix this by explicitly specifying the bracketing m (f (n - 1)).
To be able to call f from m and m from f, you need to use the keyword fun instead of val on the first declaration (to make the function recursive), and the keyword and instead of fun or val on the second declaration (to make the function mutually recursive with the first function). This would look like
fun f n = ... (* I can call f or m from here! *)
and m n = ... (* I can call f or m from here! *)
To make it faster, you can memoize! The trick is to make f and m take as arguments memoized versions of themselves.
(* Convenience function: Update arr[i] to x, and return x. *)
fun updateAndReturn arr i x = (Array.update (arr, i, SOME x); x)
(*
* Look up result of f i in table; if it's not found, calculate f i and
* store in the table. The token is used so that deeper recursive calls
* to f can also try to store in the table.
*)
fun memo table f token i =
case Array.sub (table, i)
of NONE => updateAndReturn table i (f token i)
| SOME x => x
(*
* Given f, g, and n : int, returns a tuple (f', g') where f' and g' are memoized
* versions of f and g, respectively. f' and g' are defined only on the domain
* [0, n).
*)
fun memoizeMutual (f, g) n =
let
val fTable = Array.array (n, NONE)
val gTable = Array.array (n, NONE)
fun fMemo i = memo fTable f (fMemo, gMemo) i
and gMemo i = memo gTable g (gMemo, fMemo) i
in
(fMemo, gMemo)
end
fun female _ 0 = 1
| female (f, m) n = n - m (f (n - 1))
fun male _ 0 = 0
| male (m, f) n = n - f (m (n - 1))
fun hofstadter upTo =
let
val (male', female') = memoizeMutual (male, female) upTo
in
(List.tabulate (upTo, male'), List.tabulate (upTo, female'))
end
I renamed f and m to female and male. The memoized fMemo and gMemo are threaded through female and male by memoizeMutual. Interestingly, if we call male', then results for both male' and female' are memoized.
To confirm it's indeed faster, try evaluating hofstadter 10000. It's much faster than the forever that your version would take.
As a final note, the only recursive functions are fMemo and gMemo. Every other function I wrote could be written as an anonymous function (val memoizeMutual = fn ..., val female = fn ..., etc.), but I chose not to do so because the syntax for writing recursive functions is much more compact in SML.
To generalize this, you could replace the array version of memoizing with something like a hash table. Then we wouldn't have to specify the size of the memoization up front.

Proof in Coq that equality is reflexivity

The HOTT book writes on page 51:
... we can prove by path induction on p: x = y that
$(x, y, p) =_{ \sum_{(x,y:A)} (x=y)} (x, x, refl x)$ .
Can someone show me how to prove this in Coq?
Actually, it is possible to prove this result in Coq:
Notation "y ; z" := (existT _ y z) (at level 80, right associativity).
Definition hott51 T x y e :
(x; y; e) = (x; x; eq_refl) :> {x : T & {y : T & x = y} } :=
match e with
| eq_refl => eq_refl
end.
Here, I've used a semicolon tuple notation to express dependent pairs; in Coq, {x : T & T x} is the sigma type \sum_{x : T} T x. There is also a slightly easier-to-read variant, where we do not mention y:
Definition hott51' T x e : (x; e) = (x; eq_refl) :> {y : T & x = y} :=
match e with
| eq_refl => eq_refl
end.
If you're not used to writing proof terms by hand, this code might look a bit mysterious, but it is doing exactly what the HoTT book says: proceeding by path induction. There's one crucial bit of information that is missing here, which are the type annotations needed to do path induction. Coq is able to infer those, but we can ask it to tell us what they are explicitly by printing the term. For hott51', we get the following (after some rewriting):
hott51' =
fun (T : Type) (x : T) (e : x = x) =>
match e as e' in _ = y' return (y'; e') = (x; eq_refl) with
| eq_refl => eq_refl
end
: forall (T : Type) (x : T) (e : x = x),
(x; e) = (x; eq_refl)
The important detail there is that in the return type of the match, both x and e are generalized to y' and e'. The only reason this is possible is because we wrapped x in a pair. Consider what would happen if we tried proving UIP:
Fail Definition uip T (x : T) (e : x = x) : e = eq_refl :=
match e as e' in _ = y' return e' = eq_refl with
| eq_refl => eq_refl
end.
Here, Coq complains, saying:
The command has indeed failed with message:
In environment
T : Type
x : T
e : x = x
y' : T
e' : x = y'
The term "eq_refl" has type "x = x" while it is expected to have type
"x = y'" (cannot unify "x" and "y'").
What this error message is saying is that, in the return type of the match, the e' has type x = y', where y' is generalized. This means that the equality e' = eq_refl is ill-typed, because the right-hand side must have type x = x or y' = y'.
Simple answer: you can't. All proofs of x = y in Coq are not instances of eq_refl x. You will have to assume Uniqueness of Identity Proof to have such a result. This is a very nice axiom, but it's still an axiom in the Calculus of Inductive Constructions.