Skip to content

SVM optimizations #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 99 commits into
base: master
Choose a base branch
from
Open

SVM optimizations #130

wants to merge 99 commits into from

Conversation

mxprshn
Copy link
Member

@mxprshn mxprshn commented Jun 2, 2022

  • constraint independence optimization
  • condition evaluation on branching to avoid extra solver queries
  • SMT solver incrementality mode

@mxprshn mxprshn mentioned this pull request Jun 2, 2022
Copy link
Member

@MchKosticyn MchKosticyn left a comment

Choose a reason for hiding this comment

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

Отличный pull request, но нужно поправить пару синтаксических моментов

@@ -141,3 +142,29 @@ and
IStatedSymbolicConstantSource =
inherit ISymbolicConstantSource
abstract Compose : state -> term

module public State =
let makeEmpty modelState = {
Copy link
Member

Choose a reason for hiding this comment

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

Очень часто встречается State.makeEmpty None. Лучше сделать для этого отдельную функцию.

isFalse <- true

new() =
PathCondition(HashSet<term>(), false)
Copy link
Member

Choose a reason for hiding this comment

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

Лучше вещи, которые можно уместить в одной строчке, умещать на ней


override this.ToString() =
if (this :> IPathCondition).IsEmpty then
"true"
Copy link
Member

Choose a reason for hiding this comment

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

Аналогично тут и в других местах


member this.Map mapper =
let mapped = PathCondition() :> IPathCondition
Seq.iter (mapper >> mapped.Add) constraints
Copy link
Member

Choose a reason for hiding this comment

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

Возможно, лучше будет сделать конструктор от списка условий

Copy link
Member Author

Choose a reason for hiding this comment

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

В плане сделать PathCondition(HashSet(Seq.map mapper constraints), isFalse)? Тогда не произойдет упрощений, которые делаются внутри add (например, если маппер мапит все констрейнты в false, то условие пути не станет простым false)

else
constantsToAdd
|> Seq.pairwise
|> Seq.iteri (fun i (previous, next) ->
Copy link
Member

Choose a reason for hiding this comment

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

Лучше вместо больших лямбд определять функции (которые можно определить внутри текущей функции)

@@ -803,15 +806,19 @@ module internal Z3 =
match result with
| Status.SATISFIABLE ->
let z3Model = optCtx.Model
let model = builder.MkModel z3Model
let updatedModel = {q.currentModel with state = {q.currentModel.state with model = q.currentModel.state.model}}
Copy link
Member

Choose a reason for hiding this comment

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

Значение state лучше вынести в отдельную функцию

Copy link
Member Author

Choose a reason for hiding this comment

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

Просто для красоты? Или оно где-то еще используется?

seq {
yield! Seq.cast<BoolExpr> encoded.assumptions
yield encoded.expr :?> BoolExpr
}
Copy link
Member

Choose a reason for hiding this comment

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

Не хватает tab

let boolConsts = seq {
for expr in exprs do
let mutable name = ""
if (assumptions.TryGetValue(expr, &name)) then
Copy link
Member

Choose a reason for hiding this comment

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

Лишние скобки

yield boolConst
}
let names = boolConsts |> Seq.map (fun c -> c.ToString())
let amp = " & "
Copy link
Member

Choose a reason for hiding this comment

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

Лучше заинлайнить это значение

Comment on lines 95 to 104
{
commitHash = commitHash
dateTime = currentDateTime
caseName = caseName
tag = tag
timesCalled = m.timesCalled
totalTicks = m.stopwatch.ElapsedTicks
totalMs = m.stopwatch.ElapsedMilliseconds
testsGenerated = testsGenerated
}
Copy link
Member

Choose a reason for hiding this comment

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

Можно сделать создание этой структуры в отдельной подфункции (определить ее внутри этой)

member x.StateId
with get() = stateId
and set id =
stateId <- id

member x.Method with get() = m
member x.ThisArg
Copy link
Member

Choose a reason for hiding this comment

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

для чего обновилась версия зависимости runtime (см. ниже)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Упс, это что-то пошло не так

@@ -0,0 +1,39 @@
namespace VSharp
Copy link
Member

Choose a reason for hiding this comment

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

зачем разводить много логгеров в проекте? этот вообще сейчас используется?

Copy link
Member Author

Choose a reason for hiding this comment

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

Этот использовал, чтобы выводить логи для каждой ветки отдельно (заводил на каждую ветку тег). Наверное, можно его вместе со Stopwatch вынести в какую-нибудь другую ветку

@@ -0,0 +1,122 @@
namespace VSharp
Copy link
Member

Choose a reason for hiding this comment

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

этот класс точно нужен в мастере? может его вынести в отдельную ветку, а в мастере всё же профилироваться?

Copy link
Member Author

Choose a reason for hiding this comment

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

upd (чтобы не забыть): выводить время работы солвера в статистику

@@ -95,6 +96,8 @@ private class TestSvmCommand : DelegatingTestCommand
private TestResult Explore(TestExecutionContext context)
{
Core.API.ConfigureSolver(SolverPool.mkSolver());
Core.API.SetFeatureFlags(new featureFlags(false, false, false));
Copy link
Member

Choose a reason for hiding this comment

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

тут тоже выпилить фичу conditionEvaluation (пусть она работает всегда), а constraintIndependence включить по умолчанию.

@@ -0,0 +1,24 @@
using VSharp.Core;
Copy link
Member

Choose a reason for hiding this comment

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

этот файл надо смёржить с SILIOptions

@@ -6,6 +6,9 @@ open FSharpx.Collections
open VSharp

module API =
let SetFeatureFlags flags =
Copy link
Member

Choose a reason for hiding this comment

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

Предлагаю сделать более мелкозернистое управление фичами. Вместо создания FeatureFlags и установки их сюда, предлагаю сделать 1 метод в API -- setConstraintSlicingEnabled. Как писал выше, conditionEvaluation -- выпилить, а incrementality передавать в ConfigureSolver (см. следующий метод).

Copy link
Member Author

Choose a reason for hiding this comment

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

Предлагаешь переименовать independence в slicing, или опечатка?

@@ -0,0 +1,18 @@
namespace VSharp.Core
Copy link
Member

Choose a reason for hiding this comment

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

как писал в API.fs, предлагаю избавиться от этого файла

/// <summary>
/// Naive path condition implementation which maintains a single set of constraints
/// </summary>
type private PathCondition private (constraints : HashSet<term>, isFalse : bool) =
Copy link
Member

Choose a reason for hiding this comment

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

почему этот PathCondition стал мутабельным? форкать состояния будет же дороже теперь...

Copy link
Member Author

Choose a reason for hiding this comment

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

Правильно же понимаю, что из-за того, что copy будет очень накладным? А в персистентном множестве будет оставаться общая часть?

/// Find operation of the union-find structure. Returns not the representative element, but the element before it
/// (for convenience)
/// </summary>
let rec findPrevious constant =
Copy link
Member

Choose a reason for hiding this comment

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

почему find не перестраивает структуру данных? если этого не делать, дерево вытягивается в цепочку

match (nextNode onePrevious), (nextNode anotherPrevious) with
| Tail(oneRepresentative, oneConstraints), Tail(anotherRepresentative, anotherConstraints) when
oneRepresentative <> anotherRepresentative ->
let constraintsUnion = PersistentSet.union oneConstraints anotherConstraints
Copy link
Member

Choose a reason for hiding this comment

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

в DSU еще есть эвристика объединения по размеру, чтобы держать дерево более сбалансированным. ее я тоже тут не вижу

Copy link
Member Author

@mxprshn mxprshn Jul 15, 2022

Choose a reason for hiding this comment

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

Возникла сложность с тем, что нужны были не только операции union и find, но и операция, которая возвращает одно из независимых подмножеств. Сходу не очень понял, как это можно было бы реализовать эффективно в дереве --- пришлось бы постоянно для каждого элемента искать его root. Из-за этого запилил циклические списки, но с ними как раз проблема, что не добавить эвристики, которые работают с деревьями.

Хотя, с другой стороны, если с эвристиками поиск рута в дереве может занимать константу --- то видимо это не так уж и неэффективно, и какие-то велосипеды изобретать было не нужно

yield! (Seq.cast<_> assumptions)
yield query.expr
}
|> Seq.distinct |> Array.ofSeq
Copy link
Member Author

@mxprshn mxprshn Aug 3, 2022

Choose a reason for hiding this comment

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

Заметил, что могли возникать повторяющиеся assumptions (в т. ч. в мастере), пофиксил с помощью Seq.distinct

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

Successfully merging this pull request may close these issues.

3 participants