Programs
writing
Programs

Motivation

once I have these types down

my program almost writes itself

How do you specify what the program should do?

What is the space of  programs to search?

How does the system search the space to find the program you specified

Specifying
Program
Behavior

How do you specify what the program should do?

  • A formal specification

  • Input-output examples

  • ​Reference implementation/program

  • Program with holes

Space of
Programs

To Search

What is the space of

programs to search?

  • Partial Program with holes?

  • Builtin or user defined?

Search the
Space of
Programs

How does the system search the space to find the program you specified

  • Brute force

  • Constraint Solving

  • Statistical search

Refinement Types

=

Types + Logical Predicates

type Name = Text

-- | Predicates of the refinement logic
data Predicate =
    BoolLit Bool
  | IntLit Int
  | Var Name
  | Unknown Name
  | Relation BinOp Predicate Predicate

  -- if p1 is true then p2 must also be true
  | Implies Predicate Predicate 
  | Not Predicate

data BinOp =
    Equal
  | And
  | Or
  | LEqual
  | Lesser
  | GEqual
  | Greater
-- | To Z3 AST
toZ3 :: Predicate -> Z3 Z3.AST
toZ3 predicate = case predicate of
  BoolLit True  -> Z3.mkTrue
  BoolLit False -> Z3.mkFalse
  IntLit i      -> Z3.mkIntNum i
  Var nm        -> do
    sort <- intSort
    s    <- Z3.mkStringSymbol $ toS nm
    Z3.mkConst s sort
  Not  p -> toZ3 p >>= Z3.mkNot

  Implies p1 p2 -> join $ Z3.mkImplies <$> toZ3 p1 <*> toZ3 p2

  Relation op p1 p2 -> join $ binOp <$> toZ3 p1 <*> toZ3 p2
   where
    binOp = case op of
      Equal   -> Z3.mkEq
      And     -> \x y -> Z3.mkAnd [x, y]
      Or      -> \x y -> Z3.mkOr [x, y]
      LEqual  -> Z3.mkLe
      GEqual  -> Z3.mkGe
      Greater -> Z3.mkGt
      Lesser  -> Z3.mkLt
-- | Search space for a single unknown
type PredicateSpace = [Predicate]
type PredicateMap = Map.Map Name PredicateSpace

-- | Valuation of a predicate unknown as a set of predicates
type Valuation = Set Predicate
-- | (Candidate) solutions for predicate unknowns
type Solution = Map.Map Name Valuation

-- | Top of the solutions 
-- (maps every unknown in unknowns to the empty set of qualifiers)
topSolution :: PredicateMap -> Solution
topSolution quals = 
  Map.fromSet (const Set.empty) (Map.keysSet quals)
-- | Substitute solutions from sol 
--   for all unknowns in predicate
substitute :: Solution -> Predicate -> Predicate
substitute sol predicate = case predicate of
  Unknown ident -> case Map.lookup ident sol of
    Just quals -> conjunction quals
    Nothing    -> predicate
  Not p             -> Not (substitute sol p)
  Implies f1 f2     -> 
    Implies (substitute sol f1) (substitute sol f2)
  Relation op f1 f2 -> 
    Relation op (substitute sol f1) (substitute sol f2)
  otherwise         -> predicate

conjunction predicates = if Set.null predicates
  then BoolLit True
  else foldr1 (Relation And) (Set.toList predicates)
solve :: [Solution] -> [Predicate] -> Z3 (Maybe Solution)
solve (sol:sols) predicates = do
  valid <- validSolution
  case valid of
    Just s  -> return $ Just s -- Solution found
    Nothing -> do
        new   <- newSolutions
        solve (new ++ sols) predicates
 where
  newSolutions = do
    modifiedCS <- modifiedConstraint
    strengthen quals modifiedCS sol

  validSolution = do
    new       <- newSolutions
    invalidCS <- invalidConstraint
    findM 
      (\s -> and <$> mapM (isValid . substitute s) 
      (delete invalidCS predicates)) new

  invalidConstraint, modifiedConstraint :: Z3 Predicate
  invalidConstraint = do
    fromJust <$> 
      findM
        (\predicate -> liftM not . isValid . substitute sol$  predicate) 
        predicates

  modifiedConstraint = do
    cs <- invalidConstraint
    case cs of
      Implies lhs rhs ->
        return $ Implies lhs (substitute sol rhs)
      _ -> panic $ toS $ "encountered ill-formed constraint " ++ (show cs)
solve :: [Solution] -> [Predicate] -> Z3 (Maybe Solution)
solve (sol:sols) predicates = do
  valid <- validSolution
  case valid of
    Just s  -> return $ Just s -- Solution found
    Nothing -> do
        new   <- newSolutions
        solve (new ++ sols) predicates
 where
  newSolutions = do
    modifiedCS <- modifiedConstraint
    strengthen quals modifiedCS sol

  validSolution = do
    new       <- newSolutions
    invalidCS <- invalidConstraint
    findM 
      (\s -> and <$> mapM (isValid . substitute s) 
      (delete invalidCS predicates)) new

  invalidConstraint, modifiedConstraint :: Z3 Predicate
  invalidConstraint = do
    fromJust <$> 
      findM
        (\predicate -> liftM not . isValid . substitute sol$  predicate) 
        predicates

  modifiedConstraint = do
    cs <- invalidConstraint
    case cs of
      Implies lhs rhs ->
        return $ Implies lhs (substitute sol rhs)
      _ -> panic $ toS $ "encountered ill-formed constraint " ++ (show cs)
-- | 'strengthen' @quals predicate sol@: all minimal strengthenings of 
-- @sol@ using predicates from @quals@ that make @predicate@ valid
strengthen :: PredicateMap -> Predicate -> Solution -> Z3 [Solution]
strengthen quals (Implies lhs rhs) sol = ...
condPredicates :: [Name] -> [Predicate]
condPredicates  vars = do
  lhs <- map Var vars
  op  <- [GEqual, Greater]
  rhs <- map Var vars
  guard $ lhs /= rhs
  return $ Relation op lhs rhs

varPredicates :: [Name] -> [Predicate]
varPredicates vars = do
  var <- map Var vars
  return $ result |=| var

vars = ["x", "y"]

space = Map.fromList
  [ ("condT",  (condPredicates vars))
  , ("condF", (condPredicates vars))
  , ("then" , (varPredicates vars) )
  , ("else" , (varPredicates vars) )
  ]

maxType = (Var "x" |<=| result) |&| (Var "y" |<=| result)

predicates =
  [ BoolLit True |==>| (Unknown "condT" ||| Unknown "condF")
  , (Unknown "condT" |&| Unknown "then") |==>| maxType
  , (Unknown "condF" |&| Unknown "else") |==>| maxType
  ]

Programs Writing Programs

By ..

Programs Writing Programs

  • 1,814