Skip to content
This repository has been archived by the owner on Oct 5, 2023. It is now read-only.

Use several SMT solvers in parallel #180

Draft
wants to merge 6 commits into
base: niols/just-be-bourrin
Choose a base branch
from

Conversation

qaristote
Copy link
Contributor

The purpose of this PR is to describe my implementation of parallelism inside Pirouette. It is not fully working, and is based on the niols/just-be-bourrin branch which implements checking for satisfiability at every node instead of every leaf of the symbolic execution tree. I'll comment my change in subsequent messages.

@qaristote qaristote marked this pull request as draft February 24, 2023 13:55
Copy link
Contributor Author

@qaristote qaristote left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @Niols pointed out, PureSMT is not so pure anymore, so we might want to change its name ...

Also note GitHub's code previewing doesn't necessarily encapsulate all the relevant code so I recommend reading this in the "Files changed" tab.

runActions Fail = return Fail
runActions (Yield x l) = Yield x <$> runActions l
runActions (Weight w l) = Weight w <$> runActions l
runActions (Action a) = a >>= runActions
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We switch from WeightedList === WeightedListT Identity to WeightedListT IO but the latter isn't an instance of the Traversable class required by mapConcurrently, hence we need a function to convert it back to a WeightedList (which is Traversable).


runSymEvalRaw ::
(SymEvalConstr lang) =>
SymEvalEnv lang ->
SymEvalSt lang ->
SymEval lang a ->
WeightedList (a, SymEvalSt lang)
WeightedListT IO (a, SymEvalSt lang)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A SymEval is now a function which returns a WeightedListT IO hence we change the type here accordingly.

@@ -174,20 +177,20 @@ runSymEvalWorker ::
PrtOrderedDefs lang ->
SymEvalSt lang ->
SymEval lang a ->
WeightedList (Path lang a)
IO (WeightedList (Path lang a))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we get a WeightedListT IO from running the computations inside the SymEval monad, we need an additional IO computation to get a WeightedList.

let solvers = SymEvalSolvers (sharedSolve . CheckPath) (sharedSolve . CheckProperty)
workers <- PureSMT.initAll (optsPureSMT opts) solverCtx
let asyncSolver :: forall t res. Traversable t => t (SolverProblem lang res) -> IO (t res)
asyncSolver = PureSMT.solve workers
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The split between PureSMT.initAll and PureSMT.solve is reflected here.

solvPair <- runSymEvalRaw (SymEvalEnv defs solvers opts) st' f
let paths = uncurry (path $ shouldStop opts) solvPair
return paths
solvPairs <- ListT.runActions $ runSymEvalRaw (SymEvalEnv defs solvers asyncSolver opts) st' f
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use runActions to convert from WeightedListT IO a to IO (WeightedList a).

IncorrectnessResult lang
runIncorrectnessLogic opts prog parms =
runIdentity $ execIncorrectnessLogic (proveAny opts isCounter) prog parms
IO (IncorrectnessResult lang)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment, it seems adding the IO wrapper here is enough for the code to compile.

solveOpts :: forall domain res. Options -> Solve domain => Ctx domain -> Problem domain res -> res
solveOpts opts ctx = unsafePerformIO $ do
-- we end up with a list of MVars, which we will protect in another MVar.
allProcs <- initAll @domain opts ctx >>= newMStack
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're propagating IO back into Pirouette's core, there's no need to call initAll when calling solveOpts, so I split these two functions.

r <- solveProblem @domain problem solver
return r
pushMStack ms allProcs
solve :: forall domain res t. Solve domain => MStack (MVar X.Solver) -> Traversable t => t (Problem domain res) -> IO (t res)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solve function now only takes as input the pool of worker and the problems to solve. The solver options are used when creating the pool of worker inside initAll.

where
nWorkers :: Int
nWorkers = maybe numCapabilities ensurePos (numWorkers opts)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

numCapabilties is only evaluated at compile time and the result may differ from what's actually available at run time, hence getNumCapabilities is preferred.

initAll :: forall domain. Options -> Solve domain => Ctx domain -> IO (MStack (MVar X.Solver))
initAll opts ctx =
newMStack =<< do
nWorkers <- ensurePos <$> maybe getNumCapabilities return (numWorkers opts)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that getNumCapabilities evaluates to 1 on my laptop, so I actually had to override numWorkers to get multiple workers. setNumCapabilities might also be need to tell Haskell we're running things in parallel.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant