Exemple #1
0
def test_minimal_unsatisfiable_subset():
    assert raises(ValueError, lambda: minimal_unsatisfiable_subset([[1]]))

    clauses = [[-10], [1], [5], [2, 3], [3, 4], [5, 2], [-7], [2], [3],
               [-2, -3, 5], [7, 8, 9, 10], [-8], [-9]]
    res = minimal_unsatisfiable_subset(clauses)
    assert sorted(res) == [[-10], [-9], [-8], [-7], [7, 8, 9, 10]]
    assert not sat(res)

    clauses = [[1, 3], [2, 3], [-1], [4], [3], [-3]]
    for perm in permutations(clauses):
        res = minimal_unsatisfiable_subset(clauses)
        assert sorted(res) == [[-3], [3]]
        assert not sat(res)

    clauses = [[1], [-1], [2], [-2], [3, 4], [4]]
    for perm in permutations(clauses):
        res = minimal_unsatisfiable_subset(perm)
        assert sorted(res) in [[[-1], [1]], [[-2], [2]]]
        assert not sat(res)
Exemple #2
0
def test_minimal_unsatisfiable_subset():
    assert raises(ValueError, lambda: minimal_unsatisfiable_subset([[1]]))

    clauses = [[-10], [1], [5], [2, 3], [3, 4], [5, 2], [-7], [2], [3], [-2,
        -3, 5], [7, 8, 9, 10], [-8], [-9]]
    res = minimal_unsatisfiable_subset(clauses)
    assert sorted(res) == [[-10], [-9], [-8], [-7], [7, 8, 9, 10]]
    assert not sat(res)


    clauses = [[1, 3], [2, 3], [-1], [4], [3], [-3]]
    for perm in permutations(clauses):
        res = minimal_unsatisfiable_subset(clauses)
        assert sorted(res) == [[-3], [3]]
        assert not sat(res)

    clauses = [[1], [-1], [2], [-2], [3, 4], [4]]
    for perm in permutations(clauses):
        res = minimal_unsatisfiable_subset(perm)
        assert sorted(res) in [[[-1], [1]], [[-2], [2]]]
        assert not sat(res)
Exemple #3
0
    def minimal_unsatisfiable_subset(self, clauses, v, w):
        while True:
            for i in combinations(clauses, len(clauses) - 1):
                if not sat(list(i)):
                    dotlog.debug('Finding minimal unsatisfiable subset')
                    clauses = i
                    break
            else:
                break

        import pprint
        return "The following set of clauses is unsatisfiable\n%s" % \
            pprint.pformat([[w[j] if j > 0 else 'not ' + w[-j] for j in k] for k in i])
Exemple #4
0
    def minimal_unsatisfiable_subset(self, clauses, v, w):
        while True:
            for i in combinations(clauses, len(clauses) - 1):
                if not sat(list(i)):
                    dotlog.debug('Finding minimal unsatisfiable subset')
                    clauses = i
                    break
            else:
                break

        import pprint
        return "The following set of clauses is unsatisfiable\n%s" % \
            pprint.pformat([[w[j] if j > 0 else 'not ' + w[-j] for j in k] for k in i])
Exemple #5
0
 def minimal_unsatisfiable_subset(self, clauses, v, w):
     while True:
         for i in combinations(clauses, len(clauses) - 1):
             if not sat(list(i)):
                 sys.stdout.write('.');sys.stdout.flush()
                 clauses = i
                 break
         else:
             break
     import pprint
     print()
     print("The following set of clauses is unsatisfiable")
     pprint.pprint([[w[j] if j > 0 else 'not ' + w[-j] for j in k] for k in i])
Exemple #6
0
 def minimal_unsatisfiable_subset(self, clauses, v, w):
     while True:
         for i in combinations(clauses, len(clauses) - 1):
             if not sat(list(i)):
                 sys.stdout.write('.')
                 sys.stdout.flush()
                 clauses = i
                 break
         else:
             break
     import pprint
     print()
     print("The following set of clauses is unsatisfiable")
     pprint.pprint([[w[j] if j > 0 else 'not ' + w[-j] for j in k]
                    for k in i])
Exemple #7
0
    def solve(self, specs, installed=[], update_deps=True, returnall=False,
              guess=True, minimal_hint=False):
        try:
            stdoutlog.info("Solving package specifications: ")
            res = self.explicit(specs)
            if res is not None:
                return res

            # If update_deps=True, set the target package in MatchSpec so that
            # the solver can minimize the version change. If update_deps=False,
            # fix the version and build so that no change is possible.
            len0 = len(specs)
            specs = list(map(MatchSpec, specs))
            snames = {s.name for s in specs}
            for pkg in installed:
                name, version, build = self.package_triple(pkg)
                if pkg not in self.index:
                    self.index[pkg] = {
                        'name':name, 'version':version,
                        'build':build, 'build_number':0
                    }
                    self.groups.setdefault(name,[]).append(pkg)
                if name in snames:
                    continue
                if update_deps:
                    spec = MatchSpec(name, target=pkg)
                else:
                    spec = MatchSpec('%s %s %s'%(name,version,build))
                specs.append(spec)
                snames.add(spec)
            dotlog.debug("Solving for %s" % specs)

            try:
                dists, specs = self.get_dists(specs)
            except NoPackagesFound:
                raise

            # Clear out our caches to reduce memory usage before the solve
            self.find_matches_.clear()
            self.ms_depends_.clear()

            # Check if satisfiable
            dotlog.debug('Checking satisfiability')
            groups = build_groups(dists)
            m, v, w = self.build_vw(groups)
            clauses = list(self.gen_clauses(v, groups, specs))
            solution = sat(clauses)
            if not solution:
                if guess:
                    if minimal_hint:
                        stderrlog.info('\nError: Unsatisfiable package '
                            'specifications.\nGenerating minimal hint: \n')
                        sys.exit(self.minimal_unsatisfiable_subset(clauses, v, w))
                    else:
                        stderrlog.info('\nError: Unsatisfiable package '
                            'specifications.\nGenerating hint: \n')
                        sys.exit(self.guess_bad_solve(specs))
                raise RuntimeError("Unsatisfiable package specifications")

            eq_version = self.generate_version_eq(v, groups, specs[:len0], majoronly=True)
            clauses, solution, obj1 = optimize(eq_version, clauses, solution)
            dotlog.debug('Requested version metric: %d'%obj1)

            eq_features, n0 = self.generate_feature_eq(v, groups, specs)
            clauses, solution, obj = optimize(eq_features, clauses, solution)
            dotlog.debug('Feature count metric: %d'%(obj+n0))

            eq_version2 = self.generate_version_eq(v, groups, specs, majoronly=False)
            clauses, solution, obj2 = optimize(eq_version2, clauses, solution)
            dotlog.debug('Total version metric: %d'%obj2)

            eq_version3 = self.generate_package_count(v, groups, specs)
            clauses, solution, obj3 = optimize(eq_version3, clauses, solution)
            dotlog.debug('Weak dependency metric: %d'%obj3)

            dotlog.debug('Final metrics: (%d,%d,%d,%d)'%(
                (n0+evaluate_eq(eq_features,solution),
                    evaluate_eq(eq_version,solution),
                    evaluate_eq(eq_version2,solution),
                    evaluate_eq(eq_version3,solution))))

            solution = [s for s in solution if 0 < s <= m]

            dotlog.debug('Looking for alternate solutions')
            solutions = [solution]
            nsol = 1
            while True:
                nclause = tuple(-q for q in solution if 0 < q <= m)
                clauses.append(nclause)
                solution = sat(clauses)
                if solution is None:
                    break
                solution = [s for s in solution if 0 < s <= m]
                nsol += 1
                if nsol > 10:
                    dotlog.debug('Too many solutions; terminating')
                    break
                solutions.append(solution)

            psolutions = [set(w[lit] for lit in sol if 0 < lit <= m and '@' not in w[lit]) for sol in solutions]
            if nsol > 1:
                stdoutlog.info(
                    '\nWarning: %s possible package resolutions (only showing differing packages):\n' %
                    ('>10' if nsol > 10 else nsol))
                common  = set.intersection(*psolutions)
                for sol in psolutions:
                    stdoutlog.info('\t%s,\n' % sorted(sol - common))

            if obj1 > 0 or obj2 > 0 or (obj3 > 0 and any(i>1 for i,_ in eq_version3)):
                log.debug("Older versions in the solution(s):")
                for sol in solutions:
                    v = ([(i, w[j]) for i, j in eq_version if j in sol] +
                         [(i, w[j]) for i, j in eq_version2 if j in sol] +
                         [(i, w[j]) for i, j in eq_version3 if i>1 and j in sol])
                    log.debug(v)
            stdoutlog.info('\n')
            return list(map(sorted, psolutions)) if returnall else sorted(psolutions[0])
        except:
            stdoutlog.info('\n')
            raise
Exemple #8
0
 def mysat(specs):
     dists = self.get_dists(specs)
     groups = build_groups(dists)
     m, v, w = self.build_vw(groups)
     clauses = set(self.gen_clauses(v, groups, specs))
     return sat(clauses)
Exemple #9
0
    def solve2(self,
               specs,
               features,
               guess=True,
               alg='BDD',
               returnall=False,
               minimal_hint=False,
               unsat_only=False):
        log.debug("Solving for %s" % str(specs))
        log.debug("Using alg %s" % alg)

        # First try doing it the "old way", i.e., just look at the most recent
        # version of each package from the specs. This doesn't handle the more
        # complicated cases that the pseudo-boolean solver does, but it's also
        # much faster when it does work.

        try:
            dists = self.get_dists(specs, max_only=True)
        except NoPackagesFound:
            # Handle packages that are not included because some dependencies
            # couldn't be found.
            pass
        else:
            v = {}  # map fn to variable number
            w = {}  # map variable number to fn
            i = -1  # in case the loop doesn't run
            for i, fn in enumerate(sorted(dists)):
                v[fn] = i + 1
                w[i + 1] = fn
            m = i + 1

            dotlog.debug("Solving using max dists only")
            clauses = set(self.gen_clauses(v, dists, specs, features))
            solutions = min_sat(clauses)

            if len(solutions) == 1:
                ret = [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
                if returnall:
                    return [ret]
                return ret

        dists = self.get_dists(specs)

        v = {}  # map fn to variable number
        w = {}  # map variable number to fn
        i = -1  # in case the loop doesn't run
        for i, fn in enumerate(sorted(dists)):
            v[fn] = i + 1
            w[i + 1] = fn
        m = i + 1

        clauses = set(self.gen_clauses(v, dists, specs, features))
        if not clauses:
            if returnall:
                return [[]]
            return []
        eq, max_rhs = self.generate_version_eq(v, dists)

        # Second common case, check if it's unsatisfiable
        dotlog.debug("Checking for unsatisfiability")
        solution = sat(clauses)

        if not solution:
            if guess:
                stderrlog.info('\nError: Unsatisfiable package '
                               'specifications.\nGenerating hint: ')
                if minimal_hint:
                    sys.exit(self.minimal_unsatisfiable_subset(clauses, v, w))
                else:
                    sys.exit(self.guess_bad_solve(specs, features))
            raise RuntimeError("Unsatisfiable package specifications")

        if unsat_only:
            return True

        def version_constraints(lo, hi):
            return set(generate_constraints(eq, m, [lo, hi], alg=alg))

        log.debug("Bisecting the version constraint")
        evaluate_func = partial(evaluate_eq, eq)
        constraints = bisect_constraints(0,
                                         max_rhs,
                                         clauses,
                                         version_constraints,
                                         evaluate_func=evaluate_func)

        # Only relevant for build_BDD
        if constraints and false in constraints:
            # XXX: This should *never* happen. build_BDD only returns false
            # when the linear constraint is unsatisfiable, but any linear
            # constraint can equal 0, by setting all the variables to 0.
            solution = []
        else:
            if constraints and true in constraints:
                constraints = set([])

        dotlog.debug("Finding the minimal solution")
        solutions = min_sat(clauses | constraints, N=m + 1)
        assert solutions, (specs, features)

        if len(solutions) > 1:
            stdoutlog.info('Warning: %s possible package resolutions:' %
                           len(solutions))
            for sol in solutions:
                stdoutlog.info('\t' +
                               str([w[lit] for lit in sol if 0 < lit <= m]))

        if returnall:
            return [[w[lit] for lit in sol if 0 < lit <= m]
                    for sol in solutions]
        return [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
Exemple #10
0
    def solve2(self,
               specs,
               features,
               installed=(),
               guess=True,
               alg='BDD',
               returnall=False,
               minimal_hint=False,
               unsat_only=False,
               update_deps=True,
               try_max_only=None):
        log.debug("Solving for %s" % str(specs))
        log.debug("Features: %s" % str(features))
        log.debug("Installed: %s" % str(installed))

        # This won't packages that aren't in the index, but there isn't much
        # we can do with such packages here anyway.
        installed_dists = {
            pkg: Package(pkg, self.index[pkg])
            for pkg in installed if pkg in self.index
        }

        if try_max_only is None:
            if unsat_only or update_deps:
                try_max_only = False
            else:
                try_max_only = True

        # XXX: Should try_max_only use the filtered list?
        if try_max_only:
            try:
                dists = self.get_dists(specs, max_only=True, filtered=False)
            except NoPackagesFound:
                # Handle packages that are not included because some dependencies
                # couldn't be found.
                pass
            else:
                v = {}  # map fn to variable number
                w = {}  # map variable number to fn
                i = -1  # in case the loop doesn't run
                for i, fn in enumerate(sorted(dists)):
                    v[fn] = i + 1
                    w[i + 1] = fn
                m = i + 1

                dotlog.debug("Solving using max dists only")
                clauses = set(self.gen_clauses(v, dists, specs, features))
                try:
                    solutions = min_sat(clauses,
                                        alg='iterate',
                                        raise_on_max_n=True)
                except MaximumIterationsError:
                    pass
                else:
                    if len(solutions) == 1:
                        ret = [
                            w[lit] for lit in solutions.pop(0) if 0 < lit <= m
                        ]
                        if returnall:
                            return [ret]
                        return ret

        dists = self.get_dists(specs, filtered=True)

        v = {}  # map fn to variable number
        w = {}  # map variable number to fn
        i = -1  # in case the loop doesn't run
        for i, fn in enumerate(sorted(dists)):
            v[fn] = i + 1
            w[i + 1] = fn
        m = i + 1

        clauses = set(self.gen_clauses(v, dists, specs, features))
        if not clauses:
            if returnall:
                return [[]]
            return []
        eq, max_rhs = self.generate_version_eq(v,
                                               dists,
                                               installed_dists,
                                               specs,
                                               update_deps=update_deps)

        # Second common case, check if it's unsatisfiable
        dotlog.debug("Checking for unsatisfiability")
        solution = sat(clauses)

        if not solution:
            if guess:
                if minimal_hint:
                    stderrlog.info(
                        '\nError: Unsatisfiable package '
                        'specifications.\nGenerating minimal hint: \n')
                    sys.exit(self.minimal_unsatisfiable_subset(clauses, v, w))
                else:
                    stderrlog.info('\nError: Unsatisfiable package '
                                   'specifications.\nGenerating hint: \n')
                    sys.exit(self.guess_bad_solve(specs, features))
            raise RuntimeError("Unsatisfiable package specifications")

        if unsat_only:
            return True

        log.debug("Using alg %s" % alg)

        def version_constraints(lo, hi):
            return set(generate_constraints(eq, m, [lo, hi], alg=alg))

        log.debug("Bisecting the version constraint")
        evaluate_func = partial(evaluate_eq, eq)
        constraints = bisect_constraints(0,
                                         max_rhs,
                                         clauses,
                                         version_constraints,
                                         evaluate_func=evaluate_func)

        # Only relevant for build_BDD
        if constraints and false in constraints:
            # XXX: This should *never* happen. build_BDD only returns false
            # when the linear constraint is unsatisfiable, but any linear
            # constraint can equal 0, by setting all the variables to 0.
            solution = []
        else:
            if constraints and true in constraints:
                constraints = set([])

        dotlog.debug("Finding the minimal solution")
        try:
            solutions = min_sat(clauses | constraints,
                                N=m + 1,
                                alg='iterate',
                                raise_on_max_n=True)
        except MaximumIterationsError:
            solutions = min_sat(clauses | constraints, N=m + 1, alg='sorter')
        assert solutions, (specs, features)

        if len(solutions) > 1:
            stdoutlog.info(
                '\nWarning: %s possible package resolutions (only showing differing packages):\n'
                % len(solutions))
            pretty_solutions = [{w[lit]
                                 for lit in sol if 0 < lit <= m}
                                for sol in solutions]
            common = set.intersection(*pretty_solutions)
            for sol in pretty_solutions:
                stdoutlog.info('\t%s,\n' % sorted(sol - common))

        log.debug("Older versions in the solution(s):")
        for sol in solutions:
            log.debug([(i, w[j]) for i, j in eq if j in sol])
        if returnall:
            return [[w[lit] for lit in sol if 0 < lit <= m]
                    for sol in solutions]
        return [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
Exemple #11
0
    def solve2(self, specs, features, guess=True, alg='sorter', returnall=False):
        log.debug("Solving for %s" % str(specs))

        # First try doing it the "old way", i.e., just look at the most recent
        # version of each package from the specs. This doesn't handle the more
        # complicated cases that the pseudo-boolean solver does, but it's also
        # much faster when it does work.

        try:
            dists = self.get_dists(specs, max_only=True)
        except NoPackagesFound:
            # Handle packages that are not included because some dependencies
            # couldn't be found.
            pass
        else:
            v = {} # map fn to variable number
            w = {} # map variable number to fn
            i = -1 # in case the loop doesn't run
            for i, fn in enumerate(sorted(dists)):
                v[fn] = i + 1
                w[i + 1] = fn
            m = i + 1

            dotlog.debug("Solving using max dists only")
            clauses = self.gen_clauses(v, dists, specs, features)
            solutions = min_sat(clauses)


            if len(solutions) == 1:
                ret = [w[lit] for lit in solutions.pop(0) if 0 < lit]
                if returnall:
                    return [ret]
                return ret

        dists = self.get_dists(specs)

        v = {} # map fn to variable number
        w = {} # map variable number to fn
        i = -1 # in case the loop doesn't run
        for i, fn in enumerate(sorted(dists)):
            v[fn] = i + 1
            w[i + 1] = fn
        m = i + 1

        clauses = list(self.gen_clauses(v, dists, specs, features))
        if not clauses:
            if returnall:
                return [[]]
            return []
        eq, max_rhs = self.generate_version_eq(v, dists)

        # Check the common case first
        dotlog.debug("Building the constraint with rhs: [0, 0]")
        constraints = list(generate_constraints(eq, m, [0, 0], alg=alg))

        # Only relevant for build_BDD
        if constraints and constraints[0] == [false]:
            # XXX: This should *never* happen. build_BDD only returns false
            # when the linear constraint is unsatisfiable, but any linear
            # constraint can equal 0, by setting all the variables to 0.
            solution = []
        else:
            if constraints and constraints[0] == [true]:
                constraints = []

            dotlog.debug("Checking for solutions with rhs:  [0, 0]")
            solution = sat(clauses + constraints)

        if not solution:
            # Second common case, check if it's unsatisfiable
            dotlog.debug("Checking for unsatisfiability")
            solution = sat(clauses)

            if not solution:
                if guess:
                    stderrlog.info('\nError: Unsatisfiable package '
                        'specifications.\nGenerating hint: ')

                    sys.exit(self.guess_bad_solve(specs, features))
                raise RuntimeError("Unsatisfiable package specifications")

            def version_constraints(lo, hi):
                return list(generate_constraints(eq, m, [lo, hi], alg=alg))

            log.debug("Bisecting the version constraint")
            constraints = bisect_constraints(0, max_rhs, clauses, version_constraints)

        dotlog.debug("Finding the minimal solution")
        solutions = min_sat(clauses + constraints, N=m+1)
        assert solutions, (specs, features)

        if len(solutions) > 1:
            print('Warning:', len(solutions), "possible package resolutions:")
            for sol in solutions:
                print('\t', [w[lit] for lit in sol if 0 < lit <= m])

        if returnall:
            return [[w[lit] for lit in sol if 0 < lit <= m] for sol in solutions]
        return [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
Exemple #12
0
def test_sat():
    assert sat([[1]]) == [1]
    assert sat([[1], [-1]]) == []
    assert sat([]) == []
Exemple #13
0
    def solve(self, specs, installed=[], update_deps=True, returnall=False,
              guess=True, minimal_hint=False):
        try:
            stdoutlog.info("Solving package specifications: ")
            res = self.explicit(specs)
            if res is not None:
                return res

            # If update_deps=True, set the target package in MatchSpec so that
            # the solver can minimize the version change. If update_deps=False,
            # fix the version and build so that no change is possible.
            len0 = len(specs)
            specs = list(map(MatchSpec, specs))
            snames = {s.name for s in specs}
            for pkg in installed:
                name, version, build = self.package_triple(pkg)
                if pkg not in self.index:
                    self.index[pkg] = {
                        'name':name, 'version':version,
                        'build':build, 'build_number':0
                    }
                    self.groups.setdefault(name,[]).append(pkg)
                if name in snames:
                    continue
                if update_deps:
                    spec = MatchSpec(name, target=pkg)
                else:
                    spec = MatchSpec('%s %s %s'%(name,version,build))
                specs.append(spec)
                snames.add(spec)
            dotlog.debug("Solving for %s" % specs)

            try:
                dists, specs = self.get_dists(specs)
            except NoPackagesFound:
                raise

            # Clear out our caches to reduce memory usage before the solve
            self.find_matches_.clear()
            self.ms_depends_.clear()

            # Check if satisfiable
            dotlog.debug('Checking satisfiability')
            groups = build_groups(dists)
            m, v, w = self.build_vw(groups)
            clauses = list(self.gen_clauses(v, groups, specs))
            solution = sat(clauses)
            if not solution:
                if guess:
                    if minimal_hint:
                        stderrlog.info('\nError: Unsatisfiable package '
                            'specifications.\nGenerating minimal hint: \n')
                        sys.exit(self.minimal_unsatisfiable_subset(clauses, v, w))
                    else:
                        stderrlog.info('\nError: Unsatisfiable package '
                            'specifications.\nGenerating hint: \n')
                        sys.exit(self.guess_bad_solve(specs))
                raise RuntimeError("Unsatisfiable package specifications")

            eq_version = self.generate_version_eq(v, groups, specs[:len0], majoronly=True)
            clauses, solution, obj1 = optimize(eq_version, clauses, solution)
            dotlog.debug('Requested version metric: %d'%obj1)

            eq_features, n0 = self.generate_feature_eq(v, groups, specs)
            clauses, solution, obj = optimize(eq_features, clauses, solution)
            dotlog.debug('Feature count metric: %d'%(obj+n0))

            eq_version2 = self.generate_version_eq(v, groups, specs, majoronly=False)
            clauses, solution, obj2 = optimize(eq_version2, clauses, solution)
            dotlog.debug('Total version metric: %d'%obj2)

            eq_version3 = self.generate_package_count(v, groups, specs)
            clauses, solution, obj3 = optimize(eq_version3, clauses, solution)
            dotlog.debug('Weak dependency metric: %d'%obj3)

            dotlog.debug('Final metrics: (%d,%d,%d,%d)'%(
                (n0+evaluate_eq(eq_features,solution),
                    evaluate_eq(eq_version,solution),
                    evaluate_eq(eq_version2,solution),
                    evaluate_eq(eq_version3,solution))))

            solution = [s for s in solution if 0 < s <= m]

            dotlog.debug('Looking for alternate solutions')
            solutions = [solution]
            nsol = 1
            while True:
                nclause = tuple(-q for q in solution if 0 < q <= m)
                clauses.append(nclause)
                solution = sat(clauses)
                if solution is None:
                    break
                solution = [s for s in solution if 0 < s <= m]
                nsol += 1
                if nsol > 10:
                    dotlog.debug('Too many solutions; terminating')
                    break
                solutions.append(solution)

            psolutions = [set(w[lit] for lit in sol if 0 < lit <= m and '@' not in w[lit]) for sol in solutions]
            if nsol > 1:
                stdoutlog.info(
                    '\nWarning: %s possible package resolutions (only showing differing packages):\n' %
                    ('>10' if nsol > 10 else nsol))
                common  = set.intersection(*psolutions)
                for sol in psolutions:
                    stdoutlog.info('\t%s,\n' % sorted(sol - common))

            if obj1 > 0 or obj2 > 0 or (obj3 > 0 and any(i>1 for i,_ in eq_version3)):
                log.debug("Older versions in the solution(s):")
                for sol in solutions:
                    v = ([(i, w[j]) for i, j in eq_version if j in sol] +
                         [(i, w[j]) for i, j in eq_version2 if j in sol] +
                         [(i, w[j]) for i, j in eq_version3 if i>1 and j in sol])
                    log.debug(v)
            stdoutlog.info('\n')
            return list(map(sorted, psolutions)) if returnall else sorted(psolutions[0])
        except:
            stdoutlog.info('\n')
            raise
Exemple #14
0
 def mysat(specs):
     dists = self.get_dists(specs)
     groups = build_groups(dists)
     m, v, w = self.build_vw(groups)
     clauses = set(self.gen_clauses(v, groups, specs))
     return sat(clauses)
Exemple #15
0
def test_sat():
    assert sat([[1]]) == [1]
    assert sat([[1], [-1]]) == []
    assert sat([]) == []
Exemple #16
0
    def solve2(self, specs, features, guess=True, alg='BDD',
        returnall=False, minimal_hint=False, unsat_only=False):

        log.debug("Solving for %s" % str(specs))

        # First try doing it the "old way", i.e., just look at the most recent
        # version of each package from the specs. This doesn't handle the more
        # complicated cases that the pseudo-boolean solver does, but it's also
        # much faster when it does work.

        try:
            dists = self.get_dists(specs, max_only=True)
        except NoPackagesFound:
            # Handle packages that are not included because some dependencies
            # couldn't be found.
            pass
        else:
            v = {}  # map fn to variable number
            w = {}  # map variable number to fn
            i = -1  # in case the loop doesn't run
            for i, fn in enumerate(sorted(dists)):
                v[fn] = i + 1
                w[i + 1] = fn
            m = i + 1

            dotlog.debug("Solving using max dists only")
            clauses = set(self.gen_clauses(v, dists, specs, features))
            try:
                solutions = min_sat(clauses, alg='iterate',
                    raise_on_max_n=True)
            except MaximumIterationsError:
                pass
            else:
                if len(solutions) == 1:
                    ret = [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
                    if returnall:
                        return [ret]
                    return ret

        dists = self.get_dists(specs)

        v = {}  # map fn to variable number
        w = {}  # map variable number to fn
        i = -1  # in case the loop doesn't run
        for i, fn in enumerate(sorted(dists)):
            v[fn] = i + 1
            w[i + 1] = fn
        m = i + 1

        clauses = set(self.gen_clauses(v, dists, specs, features))
        if not clauses:
            if returnall:
                return [[]]
            return []
        eq, max_rhs = self.generate_version_eq(v, dists)


        # Second common case, check if it's unsatisfiable
        dotlog.debug("Checking for unsatisfiability")
        solution = sat(clauses)

        if not solution:
            if guess:
                if minimal_hint:
                    stderrlog.info('\nError: Unsatisfiable package '
                        'specifications.\nGenerating minimal hint: \n')
                    sys.exit(self.minimal_unsatisfiable_subset(clauses, v,
            w))
                else:
                    stderrlog.info('\nError: Unsatisfiable package '
                        'specifications.\nGenerating hint: \n')
                    sys.exit(self.guess_bad_solve(specs, features))
            raise RuntimeError("Unsatisfiable package specifications")

        if unsat_only:
            return True

        log.debug("Using alg %s" % alg)

        def version_constraints(lo, hi):
            return set(generate_constraints(eq, m, [lo, hi], alg=alg))

        log.debug("Bisecting the version constraint")
        evaluate_func = partial(evaluate_eq, eq)
        constraints = bisect_constraints(0, max_rhs, clauses,
            version_constraints, evaluate_func=evaluate_func)

        # Only relevant for build_BDD
        if constraints and false in constraints:
            # XXX: This should *never* happen. build_BDD only returns false
            # when the linear constraint is unsatisfiable, but any linear
            # constraint can equal 0, by setting all the variables to 0.
            solution = []
        else:
            if constraints and true in constraints:
                constraints = set([])

        dotlog.debug("Finding the minimal solution")
        try:
            solutions = min_sat(clauses | constraints, N=m + 1, alg='iterate',
                raise_on_max_n=True)
        except MaximumIterationsError:
            solutions = min_sat(clauses | constraints, N=m + 1, alg='sorter')
        assert solutions, (specs, features)

        if len(solutions) > 1:
            stdoutlog.info('\nWarning: %s possible package resolutions (only showing differing packages):\n' % len(solutions))
            pretty_solutions = [{w[lit] for lit in sol if 0 < lit <= m} for
                sol in solutions]
            common  = set.intersection(*pretty_solutions)
            for sol in pretty_solutions:
                stdoutlog.info('\t%s,\n' % sorted(sol - common))

        if returnall:
            return [[w[lit] for lit in sol if 0 < lit <= m] for sol in solutions]
        return [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
Exemple #17
0
    def solve2(self, specs, features, installed=(), guess=True, alg='BDD',
        returnall=False, minimal_hint=False, unsat_only=False, update_deps=True,
        try_max_only=None):
        log.debug("Solving for %s" % str(specs))
        log.debug("Features: %s" % str(features))
        log.debug("Installed: %s" % str(installed))

        # This won't packages that aren't in the index, but there isn't much
        # we can do with such packages here anyway.
        installed_dists = {pkg: Package(pkg, self.index[pkg]) for pkg in
            installed if pkg in self.index}

        if try_max_only is None:
            if unsat_only or update_deps:
                try_max_only = False
            else:
                try_max_only = True

        if try_max_only:
            try:
                dists = self.get_dists(specs, max_only=True)
            except NoPackagesFound:
                # Handle packages that are not included because some dependencies
                # couldn't be found.
                pass
            else:
                v = {}  # map fn to variable number
                w = {}  # map variable number to fn
                i = -1  # in case the loop doesn't run
                for i, fn in enumerate(sorted(dists)):
                    v[fn] = i + 1
                    w[i + 1] = fn
                m = i + 1

                dotlog.debug("Solving using max dists only")
                clauses = set(self.gen_clauses(v, dists, specs, features))
                try:
                    solutions = min_sat(clauses, alg='iterate',
                        raise_on_max_n=True)
                except MaximumIterationsError:
                    pass
                else:
                    if len(solutions) == 1:
                        ret = [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]
                        if returnall:
                            return [ret]
                        return ret

        dists = self.get_dists(specs)

        v = {}  # map fn to variable number
        w = {}  # map variable number to fn
        i = -1  # in case the loop doesn't run
        for i, fn in enumerate(sorted(dists)):
            v[fn] = i + 1
            w[i + 1] = fn
        m = i + 1

        clauses = set(self.gen_clauses(v, dists, specs, features))
        if not clauses:
            if returnall:
                return [[]]
            return []
        eq, max_rhs = self.generate_version_eq(v, dists, installed_dists,
            specs, update_deps=update_deps)


        # Second common case, check if it's unsatisfiable
        dotlog.debug("Checking for unsatisfiability")
        solution = sat(clauses)

        if not solution:
            if guess:
                if minimal_hint:
                    stderrlog.info('\nError: Unsatisfiable package '
                        'specifications.\nGenerating minimal hint: \n')
                    sys.exit(self.minimal_unsatisfiable_subset(clauses, v,
            w))
                else:
                    stderrlog.info('\nError: Unsatisfiable package '
                        'specifications.\nGenerating hint: \n')
                    sys.exit(self.guess_bad_solve(specs, features))
            raise RuntimeError("Unsatisfiable package specifications")

        if unsat_only:
            return True

        log.debug("Using alg %s" % alg)

        def version_constraints(lo, hi):
            return set(generate_constraints(eq, m, [lo, hi], alg=alg))

        log.debug("Bisecting the version constraint")
        evaluate_func = partial(evaluate_eq, eq)
        constraints = bisect_constraints(0, max_rhs, clauses,
            version_constraints, evaluate_func=evaluate_func)

        # Only relevant for build_BDD
        if constraints and false in constraints:
            # XXX: This should *never* happen. build_BDD only returns false
            # when the linear constraint is unsatisfiable, but any linear
            # constraint can equal 0, by setting all the variables to 0.
            solution = []
        else:
            if constraints and true in constraints:
                constraints = set([])

        dotlog.debug("Finding the minimal solution")
        try:
            solutions = min_sat(clauses | constraints, N=m + 1, alg='iterate',
                raise_on_max_n=True)
        except MaximumIterationsError:
            solutions = min_sat(clauses | constraints, N=m + 1, alg='sorter')
        assert solutions, (specs, features)

        if len(solutions) > 1:
            stdoutlog.info('\nWarning: %s possible package resolutions (only showing differing packages):\n' % len(solutions))
            pretty_solutions = [{w[lit] for lit in sol if 0 < lit <= m} for
                sol in solutions]
            common  = set.intersection(*pretty_solutions)
            for sol in pretty_solutions:
                stdoutlog.info('\t%s,\n' % sorted(sol - common))

        log.debug("Older versions in the solution(s):")
        for sol in solutions:
            log.debug([(i, w[j]) for i, j in eq if j in sol])
        if returnall:
            return [[w[lit] for lit in sol if 0 < lit <= m] for sol in solutions]
        return [w[lit] for lit in solutions.pop(0) if 0 < lit <= m]