packages feed

liquidhaskell-0.8.0.2: tests/todo/Means.hs

{-# LANGUAGE TypeSynonymInstances #-}

module Means where

import Prelude hiding ((*))
import qualified Prelude as Pr
import Language.Haskell.Liquid.Prelude

type Scalar = Double

{-@ assume (*) :: x:Scalar -> y:Scalar -> {v:Scalar | v = (times x y)} @-}
(*) :: Scalar -> Scalar -> Scalar
(*) = (Pr.*)

{-@ assume inverse :: x:{v:Scalar | v != 0} -> {v:Scalar | v = (inverse x)} @-}
inverse :: Scalar -> Scalar
inverse x | x == zero = error "inverse a zero value"
          | otherwise = one / x

-- Double 1 does not take a singleton type
{-@ assume one :: {v:Scalar | v = 1} @-}
one :: Scalar
one = 1

{-@ assume two :: {v:Scalar | v = 2} @-}
two :: Scalar
two = 2

{-@ assume zero :: {v:Scalar | v = 0} @-}
zero :: Scalar
zero = 0


{-@
  measure times :: Scalar -> Scalar -> Scalar
  @-}

{-@
  measure inverse :: Scalar -> Scalar
  @-}

-- Properties of multiplication

{-@ mulNeutral :: x:Scalar -> {v:Bool | x = (times 1 x)} @-}
mulNeutral :: Scalar -> Bool
mulNeutral x | x == one * x = True
      --        | otherwise  = error "mulNeutral fails"

{-@ mulExpTwo :: x:Scalar -> {v:Bool | (times 2 x) = x + x } @-}
mulExpTwo :: Scalar -> Bool
mulExpTwo x  | two * x == x + x = True
      --        | otherwise  = error "mulAssoc fails"

{-@ mulTrans :: x:Scalar -> y:Scalar -> z:Scalar -> {v:Bool | (times (times x y) z) = (times x (times y z))} @-}
mulTrans :: Scalar -> Scalar -> Scalar -> Bool
mulTrans x y z | x * y * z == x * (y * z) = True
      --        | otherwise  = error "mulAssoc fails"

{-@ mulAssoc :: x:Scalar -> y:Scalar -> {v:Bool | (times x y) = (times y x)} @-}
mulAssoc :: Scalar -> Scalar -> Bool
mulAssoc x y | x * y == y * x = True
      --        | otherwise  = error "mulAssoc fails"

{-@ mulDivId :: x:{v:Scalar|v/= 0} -> {v:Bool | 1 = (times x (inverse x))} @-}
mulDivId :: Scalar -> Bool
mulDivId x | one == x * inverse x = True
      --   | otherwise  = error "mulDivId fails"

-- Class Specifications
class Vec a where
  norm :: a -> Scalar
  mean :: a -> a -> Scalar
  {-@ dist :: Vec a => x:a -> y:a -> {v:Scalar | v = (dist x y) } @-}
  dist :: a -> a -> Scalar

{-@ class measure dist :: forall a . a -> a -> Scalar @-}

-- Assumptions:
-- Triangle Inequality

{-@ triangleInequality :: Vec a => x:a -> y:a -> c:a ->
      {v:Bool | ((((dist x c) + (dist c y)) >= (dist x y)))} @-}
triangleInequality :: Vec a => a -> a -> a -> Bool
triangleInequality x y c 
  | (dist x c + dist c y) >= (dist x y) 
  = True
  | otherwise                           
  = error "Vec.dist does not satisfy triangleInequality"            



{-@ foo :: x:Scalar -> {v:Scalar |  v= x} @-}
foo :: Scalar -> Scalar
foo x = liquidAssume (mulNeutral x) $ one * x 


{-@ type Valid = {v:Bool | ((Prop v) <=> true)} @-}

{-@ prop1 :: Vec a => a -> a -> a -> Valid @-}
prop1 :: Vec a => a -> a -> a -> Bool
prop1 x y c = liquidAssume ( triangleInequality x y c 
                          && mulNeutral dxy 
                          && mulDivId two
                          && mulTrans two itwo dxy
                          && mulExpTwo itwo_dxy
                           ) $ 
              (dist x c) + (dist c y) >= (itwo_dxy + itwo_dxy')
  where c'  = mean x y
        dxy = dist x y
        itwo = inverse two
        itwo_dxy = itwo * dxy              -- = dist (y, mean x y)
        itwo_dxy' = inverse two * dist x y -- = dist (x, mean x y)


-- Q: how do we enforce linear arithmetic now? tests/pos/mul.hs
-- Step 1: encode everything as Real and use nlsat tactick
--         (check-sat-using qfnra-nlsat)
--         http://rise4fun.com/Z3/1EYC
-- Step 2: encode class theorems, like triangleInequality
--         at every environment with 3 Vecs enforce the theorem
-- Step 3: "prove" the theorems for class instances
--           testing?