Skip to content
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

✨ Ensure solver output is deterministic #159

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions internal/solver/lit_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ func (inconsistentLitMapping) Error() string {
// Solve (Constraints, Variables, etc.) and the variables that
// appear in the SAT formula.
type litMapping struct {
inorder []deppy.Variable
variables map[z.Lit]deppy.Variable
lits map[deppy.Identifier]z.Lit
constraints map[z.Lit]deppy.AppliedConstraint
c *logic.C
errs inconsistentLitMapping
inorder []deppy.Variable
variables map[z.Lit]deppy.Variable
lits map[deppy.Identifier]z.Lit
constraints map[z.Lit]deppy.AppliedConstraint
constraintsInOrder []z.Lit
c *logic.C
errs inconsistentLitMapping
}

// newLitMapping returns a new litMapping with its state initialized based on
Expand Down Expand Up @@ -72,6 +73,7 @@ func newLitMapping(variables []deppy.Variable) (*litMapping, error) {
Variable: variable,
Constraint: constraint,
}
d.constraintsInOrder = append(d.constraintsInOrder, m)
}
}

Expand Down Expand Up @@ -142,7 +144,7 @@ func (d *litMapping) AddConstraints(g inter.S) {
}

func (d *litMapping) AssumeConstraints(s inter.S) {
for m := range d.constraints {
for _, m := range d.constraintsInOrder {
s.Assume(m)
}
}
Expand Down
58 changes: 10 additions & 48 deletions internal/solver/solve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ package solver
import (
"bytes"
"context"
"errors"
"fmt"
"reflect"
"sort"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -117,11 +114,11 @@ func TestSolve(t *testing.T) {
Error: deppy.NotSatisfiable{
{
Variable: variable("a", constraint.Mandatory(), constraint.Prohibited()),
Constraint: constraint.Mandatory(),
Constraint: constraint.Prohibited(),
},
{
Variable: variable("a", constraint.Mandatory(), constraint.Prohibited()),
Constraint: constraint.Prohibited(),
Constraint: constraint.Mandatory(),
},
},
},
Expand Down Expand Up @@ -186,16 +183,16 @@ func TestSolve(t *testing.T) {
},
Error: deppy.NotSatisfiable{
{
Variable: variable("a", constraint.Mandatory()),
Constraint: constraint.Mandatory(),
Variable: variable("b", constraint.Mandatory(), constraint.Conflict("a")),
Constraint: constraint.Conflict("a"),
},
{
Variable: variable("b", constraint.Mandatory(), constraint.Conflict("a")),
Constraint: constraint.Mandatory(),
},
{
Variable: variable("b", constraint.Mandatory(), constraint.Conflict("a")),
Constraint: constraint.Conflict("a"),
Variable: variable("a", constraint.Mandatory()),
Constraint: constraint.Mandatory(),
},
},
},
Expand All @@ -218,16 +215,16 @@ func TestSolve(t *testing.T) {
},
Error: deppy.NotSatisfiable{
{
Variable: variable("a", constraint.Mandatory(), constraint.Dependency("x", "y"), constraint.AtMost(1, "x", "y")),
Constraint: constraint.AtMost(1, "x", "y"),
Variable: variable("y", constraint.Mandatory()),
Constraint: constraint.Mandatory(),
},
{
Variable: variable("x", constraint.Mandatory()),
Constraint: constraint.Mandatory(),
},
{
Variable: variable("y", constraint.Mandatory()),
Constraint: constraint.Mandatory(),
Variable: variable("a", constraint.Mandatory(), constraint.Dependency("x", "y"), constraint.AtMost(1, "x", "y")),
Constraint: constraint.AtMost(1, "x", "y"),
},
},
},
Expand Down Expand Up @@ -301,41 +298,6 @@ func TestSolve(t *testing.T) {

installed, err := s.Solve(context.TODO())

if installed != nil {
sort.SliceStable(installed, func(i, j int) bool {
return installed[i].Identifier() < installed[j].Identifier()
})
}
Comment on lines -304 to -308
Copy link
Member Author

Choose a reason for hiding this comment

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

This bit is not related to #142, but I think non-error output should also be deterministic. Hense removing the sorting of non-error output to avoid masking potential issues.


// Failed constraints are sorted in lexically
// increasing Order of the identifier of the
// constraint's variable, with ties broken
// in favor of the constraint that appears
// earliest in the variable's list of
// constraints.
var ns deppy.NotSatisfiable
if errors.As(err, &ns) {
sort.SliceStable(ns, func(i, j int) bool {
if ns[i].Variable.Identifier() != ns[j].Variable.Identifier() {
return ns[i].Variable.Identifier() < ns[j].Variable.Identifier()
}
var x, y int
for ii, c := range ns[i].Variable.Constraints() {
if reflect.DeepEqual(c, ns[i].Constraint) {
x = ii
break
}
}
for ij, c := range ns[j].Variable.Constraints() {
if reflect.DeepEqual(c, ns[j].Constraint) {
y = ij
break
}
}
return x < y
})
}

var ids []deppy.Identifier
for _, variable := range installed {
ids = append(ids, variable.Identifier())
Expand Down