Install Problem

This tutorial demonstrates how to solve the Install Problem, which involves determining whether a set of software packages can be installed on a system given a set of dependencies and conflicts. The install problem was introduced in the paper OPIUM: Optimal Package Install/Uninstall Manager.

Problem Description

A typical software distribution includes a collection of packages, each associated with metadata describing its requirements. This metadata contains:

  • Dependencies: Packages that must be installed for a given package to function.
  • Conflicts: Packages that cannot coexist with the given package.

The goal is to determine whether a valid installation profile can be constructed that satisfies all dependency and conflict constraints.

Example Metadata

For example, consider a distribution with the following packages and rules:

  • Package a depends on packages b, c, and z.
  • Package b depends on package d.
  • Package c depends on at least one of (d, e) and one of (f, g).
  • Package d conflicts with package e.

Constraints Representation

The above rules can be represented as logical constraints:

  • ¬a∨b¬a∨b
  • ¬a∨c¬a∨c
  • ¬a∨z¬a∨z
  • ¬b∨d¬b∨d
  • ¬c∨(d∨e)¬c∨(d∨e)
  • ¬c∨(f∨g)¬c∨(f∨g)
  • ¬d∨¬e¬d∨¬e

These logical constraints form the basis for solving the problem using satisfiability (SAT) solvers.

Dependency Graph and Conflicts

The metadata rules can also be visualized using a Dependency Graph and Conflict Relationships. To represent both dependencies and conflicts in a single graph:

        (a)
       / | \
     (b) (c) (z)
       |    | \
      (d)  (d) (e)
       |     |   |
       |    (f) (g)
       |_________|
Conflicts:
(d) --- (e)

Solving the Install Problem

Here’s the program that defines the metadata rules and solves the problem:

using Satisfiability
@satvariable(a, Bool)
@satvariable(b, Bool)
@satvariable(c, Bool)
@satvariable(d, Bool)
@satvariable(e, Bool)
@satvariable(f, Bool)
@satvariable(g, Bool)
@satvariable(z, Bool)

function DependsOn(pack, deps)
    if typeof(deps) <: AbstractExpr
        return implies(pack, deps)
    else
        return and([implies(pack, dep) for dep in deps]...)
    end
end

function Conflict(packs...)
    return or([not(pack) for pack in packs]...)
end

function install_check(constraints...)
    open(Z3()) do solver
        # Assert constraints
        for constraint in constraints
            assert!(solver, constraint)
        end
        status, model = sat!(solver)
        println(status)
        for (key, value) in model
            println("$key => $value")
        end
   end
end


# Example 1: Section 2 of https://ieeexplore.ieee.org/document/4222580
install_check(
    DependsOn(a, [b, c, z]),
    DependsOn(b, [d]),
    DependsOn(c, [or(d, e), or(f, g)]),
    Conflict(d, e),
    a,
    z
)