示例#1
0
class _BaseSolver(object):
    """
    define some basic function
    """

    def __init__(self, pysat_name="m22", bootstrap_with=None):
        """
        :param pysat_name :The name of  SAT's solver
        """
        self._pysat_sovlver = None
        self._compute_model_count = 0
        self._pysat_name = pysat_name
        self._formula = CNF()
        self._cpu_time = 0.0
        if bootstrap_with:
            self._formula.from_clauses(bootstrap_with)

    def print_status(self):
        used_mem = minimal_model.utils.get_used_memory()
        print("Memory used           : %.2f MB" % used_mem)
        print("CPU time              : %g s" % self.cpu_time)
        print("ComputeModelCount     : %d " % self.compute_model_count)

    @property
    def compute_model_count(self) -> int:
        return self._compute_model_count

    @property
    def cpu_time(self) -> float:
        """
        get the cpu time of solving minimal model
        """
        return self._cpu_time

    @property
    def formula(self) -> CNF:
        """
        get the original CNF formula
        """
        return self._formula

    def add_clause(self, clause):
        """
        add clause to formula
        """
        self._formula.append(clause)

    def append_formula(self, formula):
        """
        append formula to formula
        """
        for clause in formula.clauses:
            self._formula.append(clause)

    def compute_minimal_model(self) -> Tuple[bool, list]:
        """
        This method is used to  a minimal model,it will return a `tuple`.
        The first value means whether a CNF formula given to the solver is satisfiability
        The second value is a minimal model only if the formula is satisfiability, otherwise the value is None\n
        :return:  (satisfiability,minimal model)
        """
        pass
 def test_condition_one_clauses(self):
     for index, g in enumerate(self.games):
         cond = g.condition_one_clauses()
         self.assertLessEqual(len(cond), 8 * g.capacity)
         solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond)))
         self.assertTrue(solver.solve())
 def test_as_DIMACS_CNF(self):
     for index, g in enumerate(self.games):
         cond = g.get_cnf_solution()
         solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond)))
         self.assertTrue(solver.solve())
示例#4
0
def negarLinha(valor: int, lin: int, col: int, matriz: list[list],
               formula: CNF):
    for col in range(col, len(matriz[lin])):
        formula.append([-valor, -matriz[lin][col]])
示例#5
0
    def _encode(cls, lits, weights=None, bound=1, top_id=None, vpool=None,
            encoding=EncType.best, comparator='<'):
        """
            This is the method that wraps the encoder of PyPBLib. Although the
            method can be invoked directly, a user is expected to call one of
            the following methods instead: :meth:`atmost`, :meth:`atleast`, or
            :meth:`equals`.

            The list of literals can contain either integers or pairs ``(l,
            w)``, where ``l`` is an integer literal and ``w`` is an integer
            weight. The latter can be done only if no ``weights`` are
            specified separately.

            :param lits: a list of literals in the sum.
            :param weights: a list of weights
            :param bound: the value of bound :math:`k`.
            :param top_id: top variable identifier used so far.
            :param vpool: variable pool for counting the number of variables.
            :param encoding: identifier of the encoding to use.
            :param comparator: identifier of the comparison operator

            :type lits: iterable(int)
            :type weights: iterable(int)
            :type bound: int
            :type top_id: integer or None
            :type vpool: :class:`.IDPool`
            :type encoding: integer
            :type comparator: str

            :rtype: :class:`pysat.formula.CNF`
        """

        assert pblib_present, 'Package \'pypblib\' is unavailable. Check your installation.'

        if encoding < 0 or encoding > 5:
            raise(NoSuchEncodingError(encoding))

        assert lits, 'No literals are provided.'

        assert not top_id or not vpool, \
                'Use either a top id or a pool of variables but not both.'

        # preparing weighted literals
        if weights:
            assert len(lits) == len(weights), 'Same number of literals and weights is expected.'
            wlits = [pblib.WeightedLit(l, w) for l, w in zip(lits, weights)]
        else:
            if all(map(lambda lw: (type(lw) in (list, tuple)) and len(lw) == 2, lits)):
                # literals are already weighted
                wlits = [pblib.WeightedLit(*wl) for wl in lits]
                lits = zip(*lits)[0]  # unweighted literals for getting top_id
            elif all(map(lambda l: type(l) is int, lits)):
                # no weights are provided => all weights are units
                wlits = [pblib.WeightedLit(l, 1) for l in lits]
            else:
                assert 0, 'Incorrect literals given.'

        # obtaining the top id from the variable pool
        if vpool:
            top_id = vpool.top

        if not top_id:
            top_id = max(map(lambda x: abs(x), lits))

        # pseudo-Boolean constraint and variable manager
        constr = pblib.PBConstraint(wlits, EncType._to_pbcmp[comparator], bound)
        varmgr = pblib.AuxVarManager(top_id + 1)

        # encoder configuration
        config = pblib.PBConfig()
        config.set_PB_Encoder(EncType._to_pbenc[encoding])

        # encoding
        result = pblib.VectorClauseDatabase(config)
        pb2cnf = pblib.Pb2cnf(config)
        pb2cnf.encode(constr, result, varmgr)

        # extracting clauses
        ret = CNF(from_clauses=result.get_clauses())

        # updating vpool if necessary
        if vpool:
            if vpool._occupied and vpool.top <= vpool._occupied[0][0] <= ret.nv:
                cls._update_vids(ret, vpool)
            else:
                vpool.top = ret.nv - 1
                vpool._next()

        return ret
示例#6
0
		
	k=int(len(S)/2)
	S1=S[0:k]
	S2=S[k:len(S)]
	A1=diag(S2,S1,Diff(AC,S2))
	A2=diag(A1,S2,Diff(AC,A1))
	return A1 + A2

def Diff(li1, li2): 
    li_dif = [i for i in li1 + li2 if i not in li1 or i not in li2] 
    return li_dif 

#model=sys.argv[1]
#requirements=sys.argv[2]
#outFile=sys.argv[3]

#model="./cnf/aircraft_fm.xml.cnf"
#requirements="./cnf/aircraft_fm.xml.cnf_conf/1"
requirements="../QX-Benchmark/cnf/betty/5000_30_0/16-50-4.prod"
model="../QX-Benchmark/cnf/betty/5000_30_0.cnf"
outFile="./out.txt"

modelCNF = CNF(from_file=model)
requirementsCNF = CNF(from_file=requirements)

result= fmdiag(requirementsCNF.clauses, modelCNF.clauses + requirementsCNF.clauses)
resCNF= CNF()
for c in result:
	resCNF.append(c)
resCNF.to_file(outFile)
print(count)
示例#7
0
def consistencyCheck(AC, solver, difficulty):
    if solver == "Sat4j":
        f = tempfile.NamedTemporaryFile()
        cnf = CNF()
        for clause in AC:  #AC es conjunto de conjuntos
            cnf.append(clause[1])  #añadimos la constraint
        cnf.to_file(f.name)
        starttime = time.time()
        out = os.popen("java -jar org.sat4j.core.jar " + f.name).read()
        f.close()
        reqtime = time.time() - starttime
        time.sleep(reqtime * difficulty)
        if "UNSATISFIABLE" in out:
            return False
        else:
            return True

    elif solver == "Glucose3":
        g = Glucose3()
        for clause in AC:  #AC es conjunto de conjuntos
            g.add_clause(clause[1])  #añadimos la constraint
        starttime = time.time()
        sol = g.solve()
        reqtime = time.time() - starttime
        time.sleep(reqtime * difficulty)
        return sol
    elif solver == "Choco4":
        f = tempfile.NamedTemporaryFile()
        cnf = CNF()
        for clause in AC:  #AC es conjunto de conjuntos
            cnf.append(clause[1])  #añadimos la constraint
        cnf.to_file(f.name)
        starttime = time.time()
        out = os.popen("java -jar choco4solver.jar " + f.name).read()
        f.close()
        reqtime = time.time() - starttime
        time.sleep(reqtime * difficulty)
        if "UNSATISFIABLE" in out:
            return False
        else:
            return True
    else:
        raise ValueError("Solver not defined")
示例#8
0
class NaiveQBF():
    """
    This class work as a skeleton in order to define qbf solver based on SAT.
    """
    def __init__(self):
        """
        Constructor of the class. Always create an empty solver
        """
        self.formula = CNF()
        self.quantifiers = []
        self.propagate = []

    def append_formula(self, formula):
        """
        Append a formula (seen as a list of clauses) to the solver.
        """
        for clause in formula:
            self.formula.append(clause)

    def propagate_literal(self, variable):
        """
        Add a literal to be propagated.
        """
        self.propagate.append(variable)
        if variable in self.quantifiers:
            self.quantifiers.remove(variable)

    def add_clause(self, clause):
        """
        Add a clause to the formula
        """

        self.formula.append(clause)

    def add_quantifiers(self, quantifier):
        """
        Add a quantifier to the quantifiers list.
        Quantifiers are represented as integers. A positive
        quantifiers is interpreted as an exists, as well as
        a negative quantifiers is interpreted as a for all.
        """

        if quantifier in self.quantifiers:
            raise ValueError('A variable can not be quantified twice')

        self.quantifiers.append(quantifier)

    def negate(self):
        """
        negate the formula along with the quantifiers list.
        """
        self.formula = self.formula.negate()
        self.quantifiers = [-quant for quant in self.quantifiers]

    def __solve(quantifiers, formula, propagate):
        """
        Private method that implements the recursión that solve the problem.
        """
        if quantifiers:
            new_quant = copy.deepcopy(quantifiers)
            quantifier = new_quant.pop()

            if quantifier < 0:
                return NaiveQBF.__solve(
                    new_quant, formula,
                    propagate + [quantifier]) and NaiveQBF.__solve(
                        new_quant, formula, propagate + [-quantifier])
            if NaiveQBF.__solve(new_quant, formula, propagate + [quantifier]):
                return True
            return NaiveQBF.__solve(new_quant, formula,
                                    propagate + [quantifier])
        else:
            solver = Solver(name='cd')
            solver.append_formula(formula)
            print(formula.clauses)
            print(propagate)
            return solver.solve(assumptions=propagate)

    def solve(self):
        """
        Solved the associated formula.
        """
        return NaiveQBF.__solve(self.quantifiers, self.formula, self.propagate)
# very simple script that outputs #vars & #clauses for a given .cnf
# useful to check if the instances correspond to their descriptions in a paper

from pysat.formula import CNF
import sys

f = CNF(sys.argv[1])
print(f'{sys.argv[1]} has {f.nv} vars and {len(f.clauses)} clauses')
def test_simplify_CNF():
    clauses = [[1, 2], [4, 5], [-1, 2, 3]]
    fmla = CNF(from_clauses=clauses)
    simplify_CNF(fmla, [1, 4]).to_fp(sys.stdout)
    fmla.to_fp(sys.stdout)
示例#11
0
def solve_problem(problem):
    p_atoms, last = enumerate_states(problem, 1)
    a_atoms, last = enumerate_agent_actions(last, problem)
    spa_atoms, last = enumerate_spread_actions(problem, last)
    agea_atoms, last = enumerate_states(problem, last)
    noopa_atoms, last = enumerate_states(problem, last)
    observation_clauses = gen_observation_clauses(p_atoms,
                                                  problem['observations'])
    action_preconditions_clauses = gen_precondition_clauses(
        p_atoms, a_atoms, spa_atoms, noopa_atoms, agea_atoms)
    fact_acheivement_clauses = gen_fact_acheivement_clauses(
        p_atoms, a_atoms, spa_atoms, noopa_atoms, agea_atoms)
    action_interefernce_clauses = gen_action_interefernce_clauses(
        a_atoms, spa_atoms, noopa_atoms, agea_atoms)
    must_spread_clauses = gen_must_spread_clauses(p_atoms, a_atoms, spa_atoms)
    must_age_clauses = gen_must_age_clauses(p_atoms, a_atoms, agea_atoms)
    S_representation_clauses = gen_S_representation_clauses(p_atoms)
    must_use_teams_clauses = gen_must_use_teams_clauses(
        p_atoms, a_atoms, problem['medics'], problem['police'])
    limited_teams_clauses = gen_limited_teams_clauses(p_atoms, a_atoms,
                                                      problem['medics'],
                                                      problem['police'])
    all_clauses = observation_clauses + action_preconditions_clauses + fact_acheivement_clauses + action_interefernce_clauses + must_spread_clauses + must_age_clauses + S_representation_clauses + must_use_teams_clauses + limited_teams_clauses
    answer = {}
    non_starter_list = ['I', 'Q']
    for query in problem['queries']:
        row = query[0][0]
        col = query[0][1]
        step = query[1]
        status = query[2]
        if step == 0 and status in non_starter_list:
            answer[
                query] = 'F'  # a query about a state that can't be in step 0 returns 'F' instantly
        else:
            if status == 'S':
                query_clause = [
                    p_atoms['S3'][step][row][col],
                    p_atoms['S2'][step][row][col],
                    p_atoms['S1'][step][row][col]
                ]
            elif status == 'Q':
                if step == 0:  # can't have Q in initial conditions
                    query_clause = [False]
                else:
                    query_clause = [
                        p_atoms['Q2'][step][row][col],
                        p_atoms['Q1'][step][row][col]
                    ]
            elif status == 'I':
                if step == 0:  # can't have I in initial conditions
                    query_clause = [False]
                else:
                    query_clause = [p_atoms['I'][step][row][col]]
            else:
                query_clause = [p_atoms[status][step][row][col]]
            cnf_formula1 = CNF()
            sat_solver = Solver()
            cnf_formula1.clauses = all_clauses + [query_clause]
            #cnf_formula.clauses = all_clauses + [[21, 29, 37]]
            sat_solver.append_formula(cnf_formula1)
            res1 = sat_solver.solve()
            #res2 = sat_solver.get_model()
            sat_solver.delete()
            res1_other = False
            if res1 == True:
                # try to solve again- with any otehr possible status instead of status
                # If there is a solution- then it means there could be another status
                # It this case the result can;t be conclusive
                sat_solver = Solver()
                negative_query_clauses = []
                for q in query_clause:
                    negative_query_clauses.append([-q])
                cnf_formula2 = CNF()
                cnf_formula2.clauses = all_clauses + negative_query_clauses

                sat_solver.append_formula(cnf_formula2)
                res1_other = sat_solver.solve()
                model = sat_solver.get_model()
                sat_solver.delete()

            if res1 and not res1_other:  # The query status is possible and no other status is possible
                answer[query] = 'T'
            elif res1 and res1_other:  # The query status is possible but also at least one more status can be
                answer[query] = '?'
            else:
                answer[query] = 'F'  # The query status is not possible
            sat_solver.delete()

    return answer
def test_get_unsat_core():
    with open("cnf/unsat_fmla.cnf", "r") as f:
        fmla = CNF(from_fp=f)
    result, bad_asms = get_unsat_core_pysat(fmla, None)
    validate_get_unsat_core_pysat(fmla, result, bad_asms)
示例#13
0
def SATProblemToCNF(problem):
  return CNF(from_clauses=Clauses_to_clauses(problem.clauses()))
示例#14
0
  def __init__(self, name, lookahead_rewards=["march_cu"]):
      self.name = name
      self.lookahead_rewards = lookahead_rewards

  def cube(self, s# , assumptions
  ):
      lr = self.lookahead_rewards[0]
      status, lits = s.cube(# assumptions=assumptions,
                            lookahead_reward=lr, lookahead_delta_fraction=1.0)
      if status != Z3Status.unknown: return None
      else: return lits[0].var()

class Z3VarSelector:
  def __init__(self, name="bob"):
    self.name = name

  def __call__(self, fmla):
    problem = SATProblemFromCNF(fmla)
    S = Z3Solver(problem, opts(max_conflicts=0))
    status, lits = S.cube(lookahead_reward="march_cu", lookahead_delta_fraction=1.0)
    if not status == Z3Status.unknown:
      return None
    else:
      return lits[0].var().idx()

if __name__ == "__main__":
    test_1_dimacs = CNF(from_clauses=[[1,2,3,4],[-1,-2,-3],[5,6],[1,3,5],[-4,-2,-1],[-5],[4,-2,5]])
    test_1_problem = SATProblemFromCNF(test_1_dimacs)
    s = Z3Solver(test_1_problem, opts(max_conflicts=100))
    assert s.check() ==  Z3Status.sat
示例#15
0
    def encoding_Learn_SSAT(self,
                            classifier,
                            attributes,
                            sensitive_attributes,
                            auxiliary_variables,
                            probs,
                            filename,
                            dependency_constraints=[],
                            ignore_sensitive_attribute=None,
                            verbose=True,
                            find_maximization=True,
                            negate_dependency_CNF=False):
        """  
        Maximization-minimization algorithm
        """

        self.instance = "Learn"

        # TODO here we call twice: 1) get the sensitive feature with maximum favor, 2) get the sensitive feature with minimum favor
        # classifier is assumed to be a boolean formula. It is a 2D list
        # attributes, sensitive_attributes and auxiliary_variables is a list of variables
        # probs is the list of i.i.d. probabilities of attributes that are not sensitive

        self.num_variables = np.array(
            attributes +
            [abs(_var) for _group in sensitive_attributes
             for _var in _group] + auxiliary_variables).max()
        self.formula = ""

        # the sensitive attribute is exist quantified
        for group in sensitive_attributes:
            if (ignore_sensitive_attribute == None
                    or group != ignore_sensitive_attribute):
                for var in group:
                    self.formula += self._apply_exist_quantification(var)

        # random quantification over non-sensitive attributes
        if (len(attributes) > 0):
            for attribute in attributes:
                self.formula += self._apply_random_quantification(
                    attribute, probs[attribute])
        else:
            # dummy random variables
            self.formula += self._apply_random_quantification(
                self.num_variables, 0.5)
            self.num_variables += 1

        if (ignore_sensitive_attribute != None):
            for var in ignore_sensitive_attribute:
                self.formula += self._apply_exist_quantification(var)

        # Negate the classifier
        if (not find_maximization):
            _classifier = CNF(from_clauses=classifier)
            _negated_classifier = _classifier.negate(topv=self.num_variables)
            classifier = _negated_classifier.clauses
            # print(auxiliary_variables, self.num_variables, _negated_classifier.auxvars)
            auxiliary_variables += [
                i for i in range(self.num_variables +
                                 1, _negated_classifier.nv + 1)
            ]
            # print(auxiliary_variables)
            self.num_variables = max(_negated_classifier.nv,
                                     self.num_variables)
        """
        The following should not work as negation of dependency constraints does not make sense
        """
        # # When dependency CNF is provided, we also need to negate it to learn the least favored group
        # if(negate_dependency_CNF and len(dependency_constraints) > 0):
        #     """
        #     (not a) And b is provided where a = classifier and b = dependency constraints.
        #     Additionally, (not a) is in CNF.
        #     Our goal is to construct (not (a And b)) <-> (not a) Or (not b) <-> (x Or y) And (x -> not a) And (y -> not b).
        #     In this encoding, x and y are two introduced variables.
        #     """

        #     _dependency_CNF = CNF(from_clauses=dependency_constraints)
        #     _negated_dependency_CNF = _dependency_CNF.negate(topv=self.num_variables)

        #     # redefine
        #     auxiliary_variables += [i for i in range(self.num_variables + 1, _negated_dependency_CNF.nv + 3)]
        #     self.num_variables = max(_negated_dependency_CNF.nv, self.num_variables)
        #     dependency_constraints = [clause + [-1 * (self.num_variables + 1)] for clause in _negated_dependency_CNF.clauses]
        #     dependency_constraints += [[self.num_variables + 1, self.num_variables + 2]]
        #     classifier = [clause + [-1 * (self.num_variables + 2)] for clause in classifier]
        #     self.num_variables += 2 # two additional variables are introduced

        # for each sensitive-attribute (one hot vector), at least one group must be True
        self._formula_for_equal_one_constraints = ""
        for _group in sensitive_attributes:
            if (len(_group) > 1):  # when categorical attribute is not Boolean
                equal_one_constraint = PBEnc.equals(
                    lits=_group,
                    weights=[1 for _ in _group],
                    bound=1,
                    top_id=self.num_variables)
                for clause in equal_one_constraint.clauses:
                    self._formula_for_equal_one_constraints += self._construct_clause(
                        clause)
                auxiliary_variables += [
                    i for i in range(self.num_variables +
                                     1, equal_one_constraint.nv + 1)
                ]
                self.num_variables = max(equal_one_constraint.nv,
                                         self.num_variables)

        # other variables (auxiliary) are exist quantified
        for var in auxiliary_variables:
            self.formula += self._apply_exist_quantification(var)

        # clauses for the classifier
        for clause in classifier:
            self.formula += self._construct_clause(clause)

        # clauses for the dependency constraints
        for clause in dependency_constraints:
            self.formula += self._construct_clause(clause)

        self.formula += self._formula_for_equal_one_constraints

        # store in a file
        self.formula = self._construct_header() + self.formula[:-1]
        file = open(filename, "w")
        file.write(self.formula)
        file.close()
示例#16
0
def solve_sudoku_SAT(sudoku: List[List[int]], k: int) -> Union[List[List[int]], None]:
    """Solve a k-by-k block Sudoku puzzle by encoding the problem as a propositional CNF formula
       and using a satisfiability (SAT) solver

    :param sudoku: A Sudoku puzzle represented as a list of lists. Values to be filled in are set to zero (0)
    :type sudoku: List[List[int]]
    :param k: Size of the Sudoku puzzle (a grid of k x k blocks)
    :type k: int
    :return: The solved sudoku puzzles as a list of lists or None if no solution is found.
    :rtype: Union[List[List[int]], None]
    """
    # Record a (row, column, value) triple as a Variable ID in a PySAT ID Pool
    def triplet_to_id(row_index: int, column_index: int, val: int) -> int:
        # indexes need to start at 1 in the id pool
        return id_pool.id(tuple([row_index + 1, column_index + 1, val]))

    # Extract the (row, column, value) triple from the PySAT Variable ID
    def id_to_triplet(vid: int) -> Tuple[int, int, int]:
        row, column, val = id_pool.obj(vid)
        # convert back to indexes
        return row - 1, column - 1, val

    solved_sudoku = copy(sudoku)

    grid_size = k**2
    cell_indices = list(get_cell_indices(k))

    formula = CNF()
    id_pool = IDPool()

    # Build the CNF Formula using the definition of Kwon and Jain (2006)
    # (https://www.researchgate.net/publication/228566840_Optimized_CNF_encoding_for_sudoku_puzzles)
    # Extended Encoding = cell definedness AND cell uniqueness AND row definedness AND row uniqueness
    #   AND column definedness AND column uniqueness AND block definedness
    #   AND block uniqueness AND assigned cells
    # definedness: each (cell, row, column, block) has at least one number from 1 to grid_size+1
    # uniqueness each (cell, row, column, block) has at most one number from 1 to grid_size+1

    # Row/column/cell level
    # You can swap indices to declare uniqueness and definedness in a single loop
    for row_index, column_index in cell_indices:
        # Cell definedness
        formula.append([triplet_to_id(row_index, column_index, v) for v in range(grid_size)])

        # Row definedness
        formula.append([triplet_to_id(row_index, v, column_index) for v in range(grid_size)])

        # Column definedness
        formula.append([triplet_to_id(v, column_index, row_index) for v in range(grid_size)])

        # Assigned cells
        val = sudoku[row_index][column_index]
        if val != 0:
            formula.append([triplet_to_id(row_index, column_index, val - 1)])

        for val_index_i, val_index_j in itertools.combinations(range(grid_size), 2):
            # Cell uniqueness
            formula.append([
                -triplet_to_id(row_index, column_index, val_index_i),
                -triplet_to_id(row_index, column_index, val_index_j)])

            # Row uniqueness
            formula.append([
                -triplet_to_id(row_index, val_index_i, column_index),
                -triplet_to_id(row_index, val_index_j, column_index)])

            # Column uniqueness
            formula.append([
                -triplet_to_id(val_index_i, row_index, column_index),
                -triplet_to_id(val_index_j, row_index, column_index)])

    # Block level
    for val in range(grid_size):
        for block_indices in get_cell_indices_by_block(k):
            # Block definedness
            formula.append([triplet_to_id(r, c, val) for r, c in block_indices])

            # Block uniqueness
            for block_index_tuple in itertools.combinations(block_indices, 2):
                (row_1, column_1), (row_2, column_2) = block_index_tuple
                formula.append([
                    -triplet_to_id(row_1, column_1, val),
                    -triplet_to_id(row_2, column_2, val)])

    # Solve
    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()

    if not answer:
        return None

    # Fill in solution
    model = solver.get_model()
    for vid in model:
        if vid > 0:
            row_index, column_index, val = id_to_triplet(vid)
            solved_sudoku[row_index][column_index] = val + 1

    return solved_sudoku
示例#17
0
文件: lbx.py 项目: sschnug/pysat

#
#==============================================================================
if __name__ == '__main__':
    dcalls, to_enum, solver, verbose, files = parse_options()

    if type(to_enum) == str:
        to_enum = 0

    if files:
        if files[0].endswith('cnf'):
            if files[0].endswith('.wcnf'):
                formula = WCNF(from_file=files[0])
            else:  # expecting '*.cnf'
                formula = CNF(from_file=files[0]).weighted()

            with LBX(formula, use_cld=dcalls, solver_name=solver, use_timer=True) as mcsls:
                for i, mcs in enumerate(mcsls.enumerate()):
                    if verbose:
                        print('c MCS:', ' '.join([str(cl_id) for cl_id in mcs]), '0')

                        if verbose > 1:
                            cost = sum([formula.wght[cl_id - 1] for cl_id in mcs])
                            print('c cost:', cost)

                    if to_enum and i + 1 == to_enum:
                        break

                    mcsls.block(mcs)
示例#18
0
    adapt, blo, cmode, to_enum, exhaust, incr, minz, solver, trim, verbose, \
            files = parse_options()

    if files:
        # parsing the input formula
        if files[0].endswith('.gz'):
            fp = gzip.open(files[0], 'rt')
            ftype = 'WCNF' if files[0].endswith('.wcnf.gz') else 'CNF'
        else:
            fp = open(files[0], 'r')
            ftype = 'WCNF' if files[0].endswith('.wcnf') else 'CNF'

        if ftype == 'WCNF':
            formula = WCNF(from_fp=fp)
        else:  # expecting '*.cnf'
            formula = CNF(from_fp=fp).weighted()

        fp.close()

        # enabling the competition mode
        if cmode:
            assert cmode in ('a',
                             'b'), 'Wrong MSE18 mode chosen: {0}'.format(cmode)
            adapt, blo, exhaust, solver, verbose = True, True, True, 'g3', 3

            if cmode == 'a':
                trim = 5 if max(formula.wght) > min(formula.wght) else 0
                minz = False
            else:
                trim, minz = 0, True
示例#19
0
 def set_desired_formula(self, clauses, output_file):
     self.formula = CNF(from_clauses=clauses)
     self.output_file = output_file
    def extract_datapoint(self,
                          task,
                          produce_derived=False,
                          num_subproblems=0,
                          num_units=0):
        try:
            dump_dir = tempfile.TemporaryDirectory(dir=task.tmpdir)
        except:
            dump_dir = tempfile.TemporaryDirectory()

        with dump_dir as dump_dir_name:
            TOO_BIG_FLAG = False

            cnf = CNF()
            try:
                cnf.from_file(task.cnf_path, compressed_with="use_ext")
            except:
                try:
                    new_cnf_path = simplify_cnf_path(task.cnf_path,
                                                     CLAUSE_LIMIT)
                    cnf.from_file(
                        new_cnf_path, compressed_with="use_ext"
                    )  # circumvent pysat DIMACS parser complaining about invalid DIMACS files
                except:  # shrug
                    return 1, []

            if len(cnf.clauses) == 0 or cnf.nv == 0:
                return 1, []
            try:
                if len(cnf.clauses) > CLAUSE_LIMIT:
                    print(f"PROBLEM WITH {len(cnf.clauses)} CLAUSES TOO BIG")
                    status = -1
                    res = None
                    TOO_BIG_FLAG = True
                else:
                    res = gen_lbdp(tempfile.TemporaryDirectory(),
                                   cnf,
                                   dump_dir=(dump_dir_name + "/"),
                                   dumpfreq=self.dumpfreq,
                                   timeout=self.timeout,
                                   clause_limit=CLAUSE_LIMIT)
                    glue_cutoff = 50
                    if np.sum(res.glue_counts) <= glue_cutoff:
                        print(
                            f"[WARNING] PROBLEM HAS FEWER THAN {glue_cutoff} GLUE COUNTS, DISCARDING"
                        )
                        return 1, []
                    status = 0
            except FileNotFoundError as e:
                print("[WARNING]: FILE NOT FOUND", e)
                print("TASK TYPE: ", task.task_type)
                print("TASK ORIGINAL CNF ", task.original_cnf_path)
                status = 1
            except Exception as e:
                print("[WARNING]: EXITING GEN_LBDP DUE TO EXCEPTION", e)
                status = 1

            if status == 1:
                print(
                    f"FORMULA {task.cnf_path} SATISFIABLE OR ERROR RAISED, DISCARDING"
                )
                return status, []
            elif status == -1:  # UNKNOWN i.e. timed out, so split the problem
                child_paths = []
                print(f"SPLITTING CNF WITH {cnf.nv} AND {len(cnf.clauses)}")
                subproblem_random_units = 3
                if TOO_BIG_FLAG:
                    subproblem_random_units += math.ceil(
                        math.log((len(cnf.clauses) - CLAUSE_LIMIT), 2))
                num_tries = 0
                while True:
                    try:
                        print("SPLITTING WITH RANDOM UNITS: ",
                              subproblem_random_units)
                        if num_tries > 30 or subproblem_random_units >= cnf.nv:
                            print(
                                "NUM TRIES OR RANDOM UNITS EXCEEDED LIMIT, STOPPING"
                            )
                            break
                        for subproblem in Subproblems(
                                task.cnf_path,
                                num_subproblems=num_subproblems,
                                random_units=subproblem_random_units):
                            print("ENTERING SUBPROBLEM LOOP")
                            subproblem_path = os.path.join(
                                task.tmpdir, (str(uuid.uuid4()) + ".cnf.gz"))
                            subproblem.to_file(subproblem_path,
                                               compress_with="gzip")
                            try:
                                new_path = simplify_cnf_path(
                                    subproblem_path, CLAUSE_LIMIT)
                                with open(new_path, "r") as f:
                                    header = f.readline().split()[2:]
                                print("HEADER", header)
                                n_clauses = int(header[1])
                                if n_clauses > CLAUSE_LIMIT:
                                    raise IndexError(
                                        f"CLAUSE LIMIT {CLAUSE_LIMIT} EXCEEDED BY N_CLAUSES {n_clauses}"
                                    )
                                if n_clauses == 0:
                                    continue
                                child_paths.append(new_path)
                            except IndexError as e:
                                print("SIMPLIFY CNF FOUND NO SIMPLIFIED CNF")
                                print(e)
                                raise IndexError
                            except Exception as e:
                                print(
                                    "SIMPLIFY CNF PATH FAILED FOR SOME OTHER REASON"
                                )
                                print(e)
                                print("BAD PROBLEM: ", task.original_cnf_path)
                    except IndexError:
                        print("CAUGHT INDEXERROR, INCREMENTING RANDOM UNITS")
                        num_tries += 1
                        subproblem_random_units += (num_tries**2)
                        continue
                    break

                print(
                    f"SPLIT FORMULA INTO {len(child_paths)} SUBPROBLEMS: {child_paths}"
                )

                return status, child_paths
            else:
                self.writer_handle.write.remote(res)

                child_paths = []

                if produce_derived:
                    dumps = recursively_get_files(dump_dir_name,
                                                  forbidden=["bz2", "xz"],
                                                  exts=["cnf", "gz"])
                    print("GOT DUMPS: ", dumps)
                    for cnf_path in dumps:
                        try:
                            moved = shutil.move(
                                cnf_path,
                                os.path.join(task.tmpdir,
                                             str(uuid.uuid4()) + ".cnf"))
                            print(f"APPENDING DUMPED CNF: {moved}")
                            child_paths.append(moved)
                        except:
                            print("[WARNING] SOMETHING WENT TERRIBLY WRONG")

                if num_subproblems > 0 and task.task_type == 1:
                    subproblem_random_units = 3
                    while True:
                        try:
                            if subproblem_random_units > 15:
                                break
                            for subproblem in Subproblems(
                                    task.cnf_path,
                                    num_subproblems=num_subproblems,
                                    random_units=subproblem_random_units):
                                subproblem_path = os.path.join(
                                    task.tmpdir,
                                    (str(uuid.uuid4()) + ".cnf.gz"))
                                subproblem.to_file(subproblem_path,
                                                   compress_with="gzip")
                                try:
                                    new_path = simplify_cnf_path(
                                        subproblem_path, CLAUSE_LIMIT)
                                    child_paths.append(new_path)
                                except IndexError as e:
                                    print(
                                        "SIMPLIFY CNF FOUND NO SIMPLIFIED CNF")
                                    print(e)
                                    raise IndexError
                                except Exception as e:
                                    print(
                                        "SIMPLIFY CNF PATH FAILED FOR SOME OTHER REASON"
                                    )
                                    print(e)
                                    print("BAD PROBLEM: ",
                                          task.original_cnf_path)
                        except IndexError:
                            print(
                                "CAUGHT INDEXERROR, INCREMENTING RANDOM UNITS")
                            subproblem_random_units += 1
                            continue
                        break

            print("RETURNING CHILD PATHS", child_paths)
        return status, child_paths
示例#21
0
    -5: "c2",
    -6: "c3",
    -7: "~c4",
    1: "x1",
    2: "x2",
    3: "x3",
    -1: "~x1",
    -2: "~x2",
    -3: "~x3"
}

f = cost_puzzle(user_vars, I, weights)

config_params = COusParams()

ocus_expl_computer = OCUSExplain(C=CNF(from_clauses=all_constraints),
                                 params=config_params,
                                 matching_table=matching_table,
                                 verbose=True)

## Computing the Whole explanation sequence
expl_sequence = ocus_expl_computer.explain(U=user_vars, f=f, I0=I)
print("WHole sequence:\n", expl_sequence)

## Computing next best explanation given current interpretation
expl_1_step = ocus_expl_computer.explain_1_step(U=user_vars, f=f, I0=I)
print("Explain 1 step:\n", expl_1_step)

## Computing best explanation for given literal
expl_1_lit = ocus_expl_computer.explain_1_lit(f=f, lit=2, I0=I)
print("Explain 1 literal:\n", expl_1_lit)
示例#22
0
def solve_sudoku_SAT(sudoku, k):
    """
    Function that solves given sudoku list with SAT encoding. Edges correspond
    to undirected edge between cells in same row, column or k*k block.
    """

    formula = CNF()

    num_vertices = (k * k) * (k * k)  # 81 for k = 3
    # Nd array representation of sudoku
    graph = np.array(sudoku).reshape(num_vertices, 1)
    # Create 2d np array of vertice numbers of 0 until num_vertices-1 for edges
    vertices = np.arange(num_vertices).reshape(k * k, k * k)
    edges = get_edges(vertices, k)
    vertices = vertices.reshape(num_vertices, 1)
    # Number of possible values in sudoku which we refer to as numbers (nums/n)
    num_nums = k * k

    # Return propositional variable for each vertex and num combination
    def var_v_n(v, n):
        return (v * num_nums) + n

    # Each cell contains a value between 1 and k*k
    for v in range(num_vertices):

        # Clause to ensure at least one num can be assigned to one vertex
        clause = [var_v_n(v, n) for n in range(1, num_nums + 1)]
        formula.append(clause)

        for n1 in range(1, num_nums + 1):
            for n2 in range(n1 + 1, num_nums + 1):
                # Clause ensures at most one num can be assigned to one vertex
                clause = [-1 * var_v_n(v, n1), -1 * var_v_n(v, n2)]
                formula.append(clause)

        # If a cell contains u in input, cell in solution must contain u
        if graph[v] != 0:
            cell = var_v_n(vertices[v].item(), graph[v].item())
            formula.append([cell])

    # Each two different cells in same row,col,block must contain diff values
    for (v1, v2) in edges:
        for n in range(1, num_nums + 1):
            # Num assigned to vertex 1 of edge should be true, OR num assigned
            # to other vertex of edge should be true
            clause = [-var_v_n(v1.item(), n), -var_v_n(v2.item(), n)]
            formula.append(clause)

    solver = MinisatGH()
    solver.append_formula(formula)

    answer = solver.solve()
    if answer:
        model = solver.get_model()
        # Fill in sudoku graph with answer
        for v in range(num_vertices):
            for n in range(1, num_nums + 1):
                if var_v_n(v, n) in model:
                    graph[v] = n
        graph = graph.reshape(k * k, k * k).tolist()
    else:
        graph = None

    return graph
示例#23
0
def cnf(c):
    """
    Converts circuit to CNF using the Tseitin transformation

    Parameters
    ----------
    c : Circuit
            circuit to transform

    Returns
    -------
    variables : pysat.IDPool
            formula variable mapping
    formula : pysat.CNF
            CNF formula
    """
    variables = IDPool()
    formula = CNF()

    for n in c.nodes():
        variables.id(n)
        if c.type(n) == "and":
            for f in c.fanin(n):
                formula.append([-variables.id(n), variables.id(f)])
            formula.append([variables.id(n)] +
                           [-variables.id(f) for f in c.fanin(n)])
        elif c.type(n) == "nand":
            for f in c.fanin(n):
                formula.append([variables.id(n), variables.id(f)])
            formula.append([-variables.id(n)] +
                           [-variables.id(f) for f in c.fanin(n)])
        elif c.type(n) == "or":
            for f in c.fanin(n):
                formula.append([variables.id(n), -variables.id(f)])
            formula.append([-variables.id(n)] +
                           [variables.id(f) for f in c.fanin(n)])
        elif c.type(n) == "nor":
            for f in c.fanin(n):
                formula.append([-variables.id(n), -variables.id(f)])
            formula.append([variables.id(n)] +
                           [variables.id(f) for f in c.fanin(n)])
        elif c.type(n) == "not":
            if c.fanin(n):
                f = c.fanin(n).pop()
                formula.append([variables.id(n), variables.id(f)])
                formula.append([-variables.id(n), -variables.id(f)])
        elif c.type(n) == "buf":
            if c.fanin(n):
                f = c.fanin(n).pop()
                formula.append([variables.id(n), -variables.id(f)])
                formula.append([-variables.id(n), variables.id(f)])
        elif c.type(n) in ["xor", "xnor"]:
            # break into heirarchical xors
            nets = list(c.fanin(n))

            # xor gen
            def xorClauses(a, b, c):
                formula.append(
                    [-variables.id(c), -variables.id(b), -variables.id(a)])
                formula.append(
                    [-variables.id(c),
                     variables.id(b),
                     variables.id(a)])
                formula.append(
                    [variables.id(c), -variables.id(b),
                     variables.id(a)])
                formula.append(
                    [variables.id(c),
                     variables.id(b), -variables.id(a)])

            while len(nets) > 2:
                # create new net
                new_net = "xor_" + nets[-2] + "_" + nets[-1]
                variables.id(new_net)

                # add sub xors
                xorClauses(nets[-2], nets[-1], new_net)

                # remove last 2 nets
                nets = nets[:-2]

                # insert before out
                nets.insert(0, new_net)

            # add final xor
            if c.type(n) == "xor":
                xorClauses(nets[-2], nets[-1], n)
            else:
                # invert xor
                variables.id(f"xor_inv_{n}")
                xorClauses(nets[-2], nets[-1], f"xor_inv_{n}")
                formula.append([variables.id(n), variables.id(f"xor_inv_{n}")])
                formula.append(
                    [-variables.id(n), -variables.id(f"xor_inv_{n}")])
        elif c.type(n) == "0":
            formula.append([-variables.id(n)])
        elif c.type(n) == "1":
            formula.append([variables.id(n)])
        elif c.type(n) in ["ff", "lat", "input"]:
            formula.append([variables.id(n), -variables.id(n)])
        else:
            print(f"unknown gate type: {c.type(n)}")
            code.interact(local=dict(globals(), **locals()))

    return formula, variables
    count=0
    genhash=""
    if len(sys.argv) > 1:
        model=sys.argv[1]
        requirements=sys.argv[2]
        lmax=int(sys.argv[3])
        solver=sys.argv[4]
        difficulty=sys.argv[5]
        difficulty=int(sys.argv[5])
    else:
        lmax=int(6)
#        requirements="./cnf/paperB/p1.prod"
#        model="./cnf/paperB/fm.cnf"
        solver="Sat4j"
        difficulty=int(0)
        requirements="./cnf/bench/prod-16-0.prod"
        model="./cnf/bench/model_16.cnf"
        model="./cnf/bench/model_16.cnf"
    
    modelCNF = CNF(from_file=model)
    requirementsCNF = CNF(from_file=requirements)
    #Vamos a almacenar el id con la C
    AC=sorted(enumerate(modelCNF.clauses,0), key=lambda x: x[0])
    S=sorted(enumerate(requirementsCNF.clauses,len(modelCNF.clauses)), key=lambda x: x[0])
    pool = mp.Pool(mp.cpu_count())
    starttime = time.time()
    result= fastDiag(S,AC+S)
    reqtime = time.time() - starttime
    print(model+"|"+requirements+"|"+str(reqtime)+"|"+str(count+1)+"|"+str(len(cache))+"|"+str(lmax)+"|pFMDiag|"+solver+"|"+str(difficulty)+"|"+str(result))
    pool.close()
    pool.terminate()
示例#25
0
def create_clauses(epeg):

    class_to_var, var_to_class, \
        class_pair_to_var, var_to_class_pair = create_vars_mappings(epeg)

    cnf = CNF(from_clauses=[])

    top_id = max(class_pair_to_var.values())

    # constraint (1) - at most one node from each class
    for class_name, class_nodes in epeg.classes.items():

        vars = [class_to_var[class_name]] + list(
            set([node.id for node in class_nodes]))
        weights = [1] + [
            -1 for _ in list(set([node.id for node in class_nodes]))
        ]

        constraint_formula = PBEnc.equals(lits=vars,
                                          weights=weights,
                                          bound=0,
                                          top_id=top_id)
        top_id = max([top_id] + [
            abs(var) for clause in constraint_formula.clauses for var in clause
        ])

        cnf.extend(constraint_formula.clauses)

    # constraint (2) - return class
    ret_class_var = class_to_var[epeg.root_class]
    return_constraint = PBEnc.equals(lits=[ret_class_var],
                                     weights=[1],
                                     bound=1,
                                     top_id=top_id)
    top_id = max(
        [top_id] +
        [abs(var) for clause in return_constraint.clauses for var in clause])
    cnf.extend(return_constraint.clauses)

    # constraint (3) - taking node implies taking each of its children eq-classes
    for _id, node in epeg.nodes.items():
        # node => child_eq_class ---> ~node \/ child_eq_class
        cnf.extend([[-_id, class_to_var[child.eq_class]]
                    for child in node.children])

    # constraint (4) - taking node implies taking pair of its class and each child class
    # (except for theta nodes' second child)
    for _id, node in epeg.nodes.items():
        for i in range(len(node.children)):
            if not (isinstance(node, THETANode) and i == 1):
                node_child_pair_var = class_pair_to_var[(
                    node.eq_class, node.children[i].eq_class)]
                clause = [-_id, node_child_pair_var]
                cnf.append(clause)

    # constraint (5) - if there is pair of same class, there must be at least one theta node from that class taken
    for class_name, nodes in epeg.classes.items():
        pair_var = class_pair_to_var[(class_name, class_name)]
        clause = [-pair_var] + [
            node.id for node in nodes if isinstance(node, THETANode)
        ]
        cnf.append(clause)

    # constraint (6) - transitivity
    for class_1 in epeg.classes.keys():
        for class_2 in epeg.classes.keys():
            for class_3 in epeg.classes.keys():
                if len(set([class_1, class_2, class_3])) == 3:
                    pair_1 = class_pair_to_var[(class_1, class_2)]
                    pair_2 = class_pair_to_var[(class_2, class_3)]
                    pair_3 = class_pair_to_var[(class_1, class_3)]
                    clause = [-pair_1, -pair_2, pair_3]
                    cnf.append(clause)

    return cnf, top_id
示例#26
0
文件: musx.py 项目: sschnug/pysat
        """

    print('Usage:', os.path.basename(sys.argv[0]), '[options] dimacs-file')
    print('Options:')
    print('        -h, --help')
    print('        -s, --solver     SAT solver to use')
    print(
        '                         Available values: g3, lgl, m22, mc, mgh (default: m22)'
    )
    print('        -v, --verbose    Be verbose')


#
#==============================================================================
if __name__ == '__main__':
    solver, verbose, files = parse_options()

    if files:
        formula = CNF(from_file=files[0])

        with MUSX(formula, solver=solver, verbosity=verbose) as musx:
            mus = musx.compute()

            if mus:
                if verbose:
                    print('c CNF size: {0}'.format(len(formula.clauses)))
                    print('c MUS size: {0}'.format(len(mus)))

                print('v', ' '.join([str(clid) for clid in mus]), '0')
                print('c oracle time: {0:.4f}'.format(musx.oracle_time()))
示例#27
0
 def test_condition_three_clauses(self):
     for index, g in enumerate(self.games):
         cond = g.condition_three_clauses()
         solver = Cadical(CNF(from_string=as_DIMACS_CNF(cond)))
         self.assertTrue(solver.solve())
示例#28
0
def solve_sudoku_SAT(sudoku, k):

    # give each possible value for cell (i, j) a (boolean) variable, resulting in in total k^6 variables
    variables = [[[v + k * k * (k * k * i + j) for v in range(1, k * k + 1)]
                  for j in range(k * k)] for i in range(k * k)]

    formula = CNF()

    # iterate over rows and columns
    for i in range(k * k):

        # keep track of which variables belong to the row and column and to which values v
        row_vars = [[] for v in range(k * k)]
        col_vars = [[] for v in range(k * k)]

        for j in range(k * k):
            if sudoku[i][j] > 0:
                # values we are certain of are added as unit clause
                formula.append([variables[i][j][sudoku[i][j] - 1]])

            # each cell must contain a value
            formula.append(
                variables[i][j]
            )  # this says: one of the variables belonging to (i,j) must be true
            for v1 in range(k * k):
                row_vars[v1].append(
                    variables[i][j][v1]
                )  # save the variable belonging to the row for each value
                col_vars[v1].append(
                    variables[j][i]
                    [v1])  # same for the column: note the swapped i and j

                for v2 in range(v1 + 1, k * k):
                    # prevent cells from having multiple values
                    # in cell (i,j) any two variables v1, v2 (v1 != v2) cannot both be true
                    formula.append(
                        [-variables[i][j][v1], -variables[i][j][v2]])

        for v in range(k * k):
            # each row and column must contain each possible value
            formula.append(
                row_vars[v]
            )  # for each possible value v, one corresponding variable must be true in row i
            formula.append(col_vars[v])  # same for the column

            for v1 in range(k * k):
                for v2 in range(v1 + 1, k * k):
                    # and values in rows and columns cannot appear more than once
                    # even though this is already implied, adding these lines speeds up the search process
                    formula.append([-row_vars[v][v1], -row_vars[v][v2]])
                    formula.append([-col_vars[v][v1], -col_vars[v][v2]])

    # iterate over blocks
    for b1 in range(k):  # block vertical index
        for b2 in range(k):  # block horizontal index
            # keep track of which variables belong to the block and to which values v
            block_vars = [[] for v in range(k * k)]
            for i in range(k * b1, k + k * b1):  # row
                for j in range(k * b2, k + k * b2):  # column
                    for v in range(k * k):  # value
                        block_vars[v].append(variables[i][j][v])
            for v in range(k * k):
                # the block must contain every possible value once
                formula.append(block_vars[v])
                for v1 in range(k * k):
                    for v2 in range(v1 + 1, k * k):
                        # and two values in the block cannot be the same
                        formula.append(
                            [-block_vars[v][v1], -block_vars[v][v2]])

    solver = MinisatGH()
    solver.append_formula(formula)

    answer = solver.solve()

    if answer:
        # if a solution is found
        # we convert the variables back to indices (i,j) and a value v
        solution = [[0 for j in range(k * k)]
                    for i in range(k * k)]  # empty k*k x k*k solution grid
        for l in solver.get_model():  # for each variable l in the model
            if l > 0:  # variable l is true
                idx = l - 1  # this makes indexing easier
                i = int(idx / k**4)  # row index
                j = int(idx / (k * k) % (k * k))  # column index
                v = idx % (k * k) + 1  # value
                solution[i][j] = v
        return solution
    return None  # if no solution is found
示例#29
0
文件: g2cnf.py 项目: vsirin/diplom
class ColSAT:
    def __init__(self, g=nx.Graph(), ncolors=10):

        self.ncolors = ncolors
        self.g = g.copy()
        self.cmap = ColMap(g, ncolors)

    def check_coloring(self):
        for n1, n2 in self.g.edges():
            if 'c' not in self.g.node[n1] or 'c' not in self.g.node[n2]:
                return False
            if self.g.node[n1]['c'] == self.g.node[n2]['c']:
                return False
        return True

    def apply_model(self):

        check = set()
        for var in self.model[self.model > 0]:
            node, color = self.cmap.dec(var)
            self.g.node[node]['c'] = color
            if (node, color) in check:
                raise Exception("Two colors for one node???")
            else:
                check.add((node, color))

        self.colored = self.check_coloring()

        if self.colored != self.solved:
            raise Exception("Something went wrong!")

        return self.colored

    def build_cnf(self):

        self.formula = CNF()
        colors = list(range(1, self.ncolors + 1))

        for n1, n2 in self.g.edges():
            for c in colors:
                self.formula.append(
                    [-self.cmap.enc(n1, c), -self.cmap.enc(n2, c)])

        #specials = [28, 194, 242, 355, 387, 397, 468]
        #ii = 1
        #for n in specials:
        #   self.formula.append([self.cmap.enc(n, ii)])
        #  ii += 1

        for n in self.g.nodes():
            #if not n in specials:
            self.formula.append([self.cmap.enc(n, c) for c in colors])
            for c1 in colors:
                for c2 in colors:
                    if c1 < c2:
                        self.formula.append(
                            [-self.cmap.enc(n, c1), -self.cmap.enc(n, c2)])

        return self.formula

    def solve_cnf(self, solver=''):

        triangle = find_triangle(self.g)
        assumptions = []
        if len(triangle) > 0:
            assumptions = [
                self.cmap.enc(triangle[0], 1),
                self.cmap.enc(triangle[1], 2),
                self.cmap.enc(triangle[2], 3)
            ]

        #Glucose3, Glucose4, Lingeling, MapleChrono, MapleCM, Maplesat, Minisat22, MinisatGH
        #with Glucose4(bootstrap_with=self.formula.clauses, with_proof=True) as ms:
        with Lingeling(bootstrap_with=self.formula.clauses) as ms:
            self.solved = ms.solve(assumptions=assumptions)
            if self.solved:
                self.model = np.array(ms.get_model())
                self.apply_model()
            else:
                self.proof = []  #ms.get_proof()
                self.colored = False

        return self.solved
def solve_sudoku_SAT(sudoku, k):
    '''
        Function that solves a sudoku using a SAT solver.
        Creates the formula and feeds it to the MinisatGH solver.

        Returns: the filled in sudoku if it was solved, None otherwise.
    '''
    kk = k * k
    formula = CNF()

    # functions to be used within this solver
    def var_number(i, j, n):
        ''' Determines a unique index for a sudoku cell based in its row, column and possible value. '''
        return i * kk * kk + j * kk + n

    # create literals for values that are already filled in, these must always be satisfied
    for i in range(kk):
        for j in range(kk):
            if sudoku[i][j] != 0:
                clause = [var_number(i, j, sudoku[i][j])]
                formula.append(clause)

    # ensure all cells have at least one of the specified values
    for i in range(kk):
        for j in range(kk):
            clause = []
            # iterate over all possible values, determine the unique index
            for n in range(1, kk + 1):
                clause.append(var_number(i, j, n))
            # ensure at least one is satisfied
            formula.append(clause)

    # iterate over rows (needed for multiple checks)
    for i in range(kk):
        for j in range(kk):
            # ensure at most one value is picked
            for n1 in range(1, kk + 1):
                for n2 in range(n1 + 1, kk + 1):
                    # only one of the two values can be true
                    clause = [
                        -1 * var_number(i, j, n1), -1 * var_number(i, j, n2)
                    ]
                    formula.append(clause)

        # ensure proper row and column (no double values)
        for n in range(1, kk + 1):
            for j1 in range(kk):
                for j2 in range(j1 + 1, kk):
                    # ensure no column contains same value twice
                    clause = [
                        -1 * var_number(i, j1, n), -1 * var_number(i, j2, n)
                    ]
                    formula.append(clause)
                    # ensure no row contains same value twice
                    clause = [
                        -1 * var_number(j1, i, n), -1 * var_number(j2, i, n)
                    ]
                    formula.append(clause)

    # create list with indices of blocks
    indices = create_indices(k)

    # iterate over block indices
    for ind in indices:
        # iterate over possible values
        for n in range(1, kk + 1):
            # create every combination of block indices possible
            for i1 in range(len(ind)):
                for i2 in range(i1 + 1, len(ind)):
                    # ensure no value appears more than once
                    clause = [
                        -1 * var_number(ind[i1][0], ind[i1][1], n),
                        -1 * var_number(ind[i2][0], ind[i2][1], n)
                    ]
                    formula.append(clause)

    # initialise the solver and give it the formula
    solver = MinisatGH()
    solver.append_formula(formula)

    # attempt to solve formula
    answer = solver.solve()

    # fill the sudoku when an answer has been found
    if answer:
        # get model from solver
        model = solver.get_model()
        for i in range(kk):
            for j in range(kk):
                for n in range(1, kk + 1):
                    if var_number(i, j, n) in model:
                        sudoku[i][j] = n
        return sudoku

    # return None when no solution was found
    return None