statebox / cql

CQL: Categorical Query Language implementation in Haskell
GNU Affero General Public License v3.0
162 stars 14 forks source link

Knuth-Bendix (KB) prover #31

Closed wisnesky closed 5 years ago

wisnesky commented 5 years ago

In Prover.hs there is a datatype

data ProverName = Free | Congruence | Orthogonal | KB | Auto

Free and Orthogonal are already implemented. The goal here is to implement KB (i.e., fill in all existing missing case statements for ProverName) using the Twee library:

http://hackage.haskell.org/package/twee

Also, the "KB" example or something similar from AQL-java should be added to the test suite.

I've been in contact with the author, Nick Smallbone. He had this advice:

Twee is intended to be usable as a library - I use it like that in QuickSpec (http://hackage.haskell.org/package/quickspec). But since I'm the only person using it like that, the API exposes somewhat too much of Twee's internals and is very underdocumented. But I'm happy to help you get your head round it :)

The main restriction at the moment is that the only term ordering supported is KBO. I've been meaning to add other term orderings so, if that's a problem, let me know and I'll see what I can do.

The main completion loop is the function Twee.complete in the package twee-lib. It has the type

complete :: (Function f, MonadIO m) => Output m f -> Config f -> State f -> m (State f)

(there is also a pure variant completePure), where:

complete runs until either the equations are completed, the goal (if any) is proved or a resource limit is reached. The function rules :: Function f => State f -> [Rule f] then gives you the generated rewrite rules. There isn't a built-in function for checking if completion succeeded (rather than reaching a resource limit) but I think the following code should work:

successful :: State f -> Bool successful = isNothing . fst . dequeue

The API for manipulating terms is in Twee.Term and uses a builder interface which might take a bit of getting used to. Here are a few examples:

-- Building the term f(X, c) t = build (app (fun f) [var x, con (fun c)])

-- Pattern matching on the above App (F f) (Cons (Var x) (Cons (App (F c) Empty) Empty)) = t

-- Instead of using Cons and Empty, you can use unpack to unpack an argument list App (F f) ts = t [Var x, App (F c) Empty] = unpack ts

Finally, you will need to define the type of function symbols. This can be whatever you want but needs to implement several typeclasses which can all be found in Twee.Base. You should make your function symbols have type 'Extended f' (Extended is also defined in Twee.Base) in order to get several of these instances for free. That is, the prover state will have type State (Extended MyFunctionSymbol). You will need to define the following instances:

wisnesky commented 5 years ago

I hooked this up, but I'm now waiting on a few questions from the author about how to infer symbol precedences and weights. For now this prover just loops forever almost always.

wisnesky commented 5 years ago

Apparently twee doesn't do completion of an equational theory T and yield a decision procedure; it decides T -> t by deciding !t /\ T -> true = false using the usual method of skolemizing t and axiomatizing Boolean logic. That will make things harder.

wisnesky commented 5 years ago

Re: above, twee seems to work if we re-complete with a new goal every time we're asked to decide an equality. Highly inefficient, although slightly more general that using completion straight up.