iu-parfunc / verified-instances

Verified instances for parallel programming.
BSD 3-Clause "New" or "Revised" License
8 stars 5 forks source link

Goal 0.1: insert + parallel map on a set #1

Open rrnewton opened 8 years ago

rrnewton commented 8 years ago

Parallel map on a set may not initially seem like it needs total Ord, but in LVish, it does. For instance, if you look at how the traverseSet function works, it must:

The assumption that inserts safely commute requires total Ord, as we've been discussing.

This goal is met when we have some kind of traverseSet operation that takes a proof of Ord totality (three proofs actually). This should in turn be based on an insert function that takes those same proofs.

[Note: for this prototype, because of problems with (1) type classes and (2) passing proofs in product types, we will pass proofs as separate function arguments.]

rrnewton commented 8 years ago

This should probably just be a branch of LVish with the fancier types. Please branch off of something that is passing unit tests.

vikraman commented 8 years ago

I'm wondering if turning on the LiquidHaskell --totality flag will be enough to ensure that compare :: a -> a -> Ordering is total, so we just need proofs of antisymmetry and transitivity.

vikraman commented 8 years ago

Ok, it's not enough, we still need the total order proof.

rrnewton commented 8 years ago

Could you post whatever counterexample convinced you?

As far as I can see, in a higher order case like this... I'm not sure how we can state that the totality check was applied to the function passed in. That is, can't I just put the function in another module, check it without --totality and then fool your function?

nikivazou commented 8 years ago

I too doubt that the totality check can be of any use to a polymorphic function compare :: a -> a -> Ordering. I cannot see how a polymorphic function can be partially defined, since you cannot do case analysis on its inputs.

rrnewton commented 8 years ago

I guess the idea was if you guarantee that all call sites for Set.insert are checked with liquid haskell, and that the code for the concrete (monorphic) compare functions are likewise checked.... then --totality would cover it?

I'm a bit confused about how we would assert totality in a refinement anyway. Wouldn't it be covered by whatever other refinements we have? I.e. any references to the compare operation in the refinements (e.g. antisymmetry/transitivity) mean it has been axiomatized and therefore is total?

vikraman commented 8 years ago

I was confusing the two notions of totality. For example, this is a total function, but it fails the totality requirement of a total order.

leqInt :: Int -> Int -> Bool
leqInt x y = False

So, instead of Ord a =>, we need to pass in:

   (leq : a -> a -> Bool) 
-> (x:a -> y:a -> { leq x y || leq y x })
-> (x:a -> y:a -> { leq x y && leq y x => x == y }) 
-> (x:a -> y:a -> z:a -> { leq x y && leq y z ==> leq x z })
rrnewton commented 8 years ago

I see... does that differ a bit with the a -> a -> Ordering signature? Wouldn't the latter mean that --totality is enough to force compare to meet the a <= b || b <= a requirement of the total order?

rrnewton commented 8 years ago

Oh, but I just realized that writing down that signature you pasted gets very very ugly using compare directly.

rrnewton commented 8 years ago

Here's an ugly and direct expansion to use compare:

   (cmpr : a -> a -> Ordering) 
-> (x:a -> y:a -> z:a -> { cmpr x y == LT && cmpr y z == LT ==> cmpr x z == LT})
-> (x:a -> y:a -> z:a -> { cmpr x y == LT && cmpr y z == EQ ==> cmpr x z == LT})
-> (x:a -> y:a -> z:a -> { cmpr x y == EQ && cmpr y z == LT ==> cmpr x z == LT})
-> (x:a -> y:a -> z:a -> { cmpr x y == EQ && cmpr y z == EQ ==> cmpr x z == EQ}) 
-> (x:a -> y:a -> { cmpr x y == LT ==> cmpr y x == GT })
-> (x:a -> y:a -> { cmpr x y == EQ ==> cmpr y x == EQ })
nikivazou commented 8 years ago

Well, you can use macros

type ComProp X Y Z = (x:a -> y:a -> z:a -> { cmpr x y == X && cmpr y z == Y ==> cmpr x z == Z})

type LTCom =  ComProp LT LT LT 

     (cmpr : a -> a -> Ordering) 
-> LTCom
-> (x:a -> y:a -> z:a -> { cmpr x y == LT && cmpr y z == EQ ==> cmpr x z == LT})
-> (x:a -> y:a -> z:a -> { cmpr x y == EQ && cmpr y z == LT ==> cmpr x z == LT})
-> (x:a -> y:a -> z:a -> { cmpr x y == EQ && cmpr y z == EQ ==> cmpr x z == EQ}) 
-> (x:a -> y:a -> { cmpr x y == LT ==> cmpr y x == GT })
-> (x:a -> y:a -> { cmpr x y == EQ ==> cmpr y x == EQ })
rrnewton commented 8 years ago

That's a macro at the type level; would it also be possible to just define leq as a utility function at the value level and then axiomatize?


-- Axiomatize and use as an abbreviation:
leq cmpr x y = cmpr x y == EQ || cmpr x y == LT 

insert :: x -> Set x ->  (c : a -> a -> Ordering) 
-> (x:a -> y:a -> { leq c x y || leq c y x })
-> (x:a -> y:a -> { leq c x y && leq c y x => x == y }) 
-> (x:a -> y:a -> z:a -> { leq c x y && leq c y z ==> leq c x z })
-> Par _ s ()

Anyway, I guess it comes down to whatever makes it easiest for the clients to do their proofs.

I think we should do a simple Set Int to kick off this 0.1 goal and make sure everything is hooked up and working. After that we were planning on checking off tuples and other simple examples.

rrnewton commented 8 years ago

By the way, just to document it:

The thing blocking us from putting these 3 proofs in a product type for convenience is this issue, I believe. @vikraman, have we confirmed that all available product types fail? I.e. a tuple of proofs doesn't work any better than a datatype of proofs?

nikivazou commented 8 years ago

It is possible @rrnewton! If you are an inline annotation {-@ inline leq @-}, leq will just be inlined in the logic

rrnewton commented 8 years ago

Ah, nice. I wasn't aware of that inline annotation.

(FYI, it turns out that I missed some cases, and totality on compare doesn't seem to help anything. Please ignore the compare-based spec above.)

Note that @vikraman's current approach actually gets around having a separate argument for each theorem. Instead he can create an abstract datatype for VerifiedOrd which does not store the proofs, and this works in the current Liquid Haskell.

This should support a smooth refactoring path to add the fields as proofs (when we can do refinements inside fields of a product type). That will be necessary for reuse when constructing proofs for compound values like pairs.

Actually, we can probably start mocking up a class-based version as well, again, not providing the proofs as methods, but rather a method that returns a VerifiedOrd value.