def get_best_var_to_flip(self, instance, best_solution, current_no_of_unsat_clauses): best_no_of_unsat_clauses = current_no_of_unsat_clauses best = best_solution.copy() utils = SatUtils() for i in range(1, len(best)): # checking if given variable is part of tabu list. If it is then next var is selected for the flip if self.is_var_tabu(i): continue tmp_solution = best_solution.copy() # working copy of the proposed solution var_to_flip = tmp_solution[i] # flipping a selected variable to opposing value tmp_solution[i] = SatUtils.flip_var(var_to_flip) _, no_of_unsat_clauses = SatUtils.solution_status( instance, tmp_solution) # if solution hasn't been found check if proposed temp solution is better than previous best if no_of_unsat_clauses < best_no_of_unsat_clauses: best_no_of_unsat_clauses = no_of_unsat_clauses # add current selection to tabu list self.add_to_tabu(i) # remember the best solution found so far for the next iteration best = tmp_solution.copy() return best
def main(self, max_restarts=10, max_iterations=1000, instance_path="./sat_data/test.cnf"): instance = SatUtils.read_instance(instance_path) var_count = len(instance[0]) start = time.time() end = None for restart in range(max_restarts): best_solution = SatUtils.initialize_variables(var_count) for iteration in range(max_iterations): solution_status, no_of_unsat_clauses = SatUtils.solution_status( instance, best_solution) # if solution has been found terminate the search if solution_status is True: end = time.time() print("Iteration,{0},Restart,{1},Duration,{2}".format( iteration, restart, end - start)) return best_solution = self.get_best_var_to_flip( instance, best_solution, no_of_unsat_clauses) # resetting tabu list in between the restarts GsatSolver.tabu = []
def execute_walk(self, formula, proposed_solution): solution_found, unsat_clause, unsat_clause_list = SatUtils.solution_status_with_unsat_clauses(formula, proposed_solution) random_unsat_clause = random.choice(unsat_clause_list) random_variable_in_clause = random.choice(random_unsat_clause) # as we're choosing a variable from the unsat clause we can come across negative numbers, where negative # sign would indicate variable is in 0 state, however, negative index are not present in the proposed solution # array, so it needs to be guaranteed that a positive number is passed as index, hence use of abs() proposed_solution[abs(random_variable_in_clause)] = SatUtils.flip_var(abs(random_variable_in_clause)) return proposed_solution
def test_read_instance(self): test_file_path = './sat_test_data/test.cnf' cnf = SatUtils.read_instance(test_file_path) self.assertEqual(len(cnf), 2) self.assertEqual(len(cnf[0]), 6) self.assertEqual(len(cnf[1]), 4)
def execute_search(self, proposed_solution, currently_flipped_variable, random_clause): """ Choose a variable to be flipped. If a randomly chosen variable is present in the randomly selected unsatisfied clause then return that variable. Otherwise find best and second best variable to be flipped. The choice between which variable to select, best or second best is made based on the probability provided. :param proposed_solution: :return: """ if currently_flipped_variable not in random_clause: return currently_flipped_variable else: # Chose best or second best variable to flip based on the probability provided tmp_solution = proposed_solution.copy() flip_scores = {} utils = SatUtils() # loop over the variables to see which flip generates the lowest number of unsatisfied clauses for i in range(len(self.variables)): tmp_solution[i] = SatUtils.flip_var(self.variables[i]) _, unsat_clauses = SatUtils.solution_status( self.instance, tmp_solution) # keep track of which variable flip generated what number of unsatisfied clauses flip_scores[self.variables[i]] = unsat_clauses # flip the selected variable tmp_solution[i] = SatUtils.flip_var(self.variables[i]) # Find best and second best - extremely efficient according to stack overflow two_smallest_keys = heapq.nsmallest(2, flip_scores, key=flip_scores.get) best = two_smallest_keys[0] second_best = two_smallest_keys[1] # generate noise value noise_value = random.uniform(0, 1) # compare noise value generated to the probability of second best variable being selected if noise_value < self.probability: return second_best else: return best
def test_basic_solution_status_with_true_state(self): formula = [[1, 2, 3, 4, 5, 6], [[1, -2, 3], [1, -2, -3], [1, 2, 4], [-4, -5, 6]]] solution = {1: 1, 2: 1, 3: 0, 4: 1, 5: 1, 6: 1} solution_found, unsat_result = SatUtils.solution_status( formula, solution) self.assertTrue(solution_found) self.assertEqual(unsat_result, 0)
def test_solution_status_with_false_state(self): formula = [[1, 2, 3, 4, 5, 6], [[1, -2, 3], [1, -2, -3], [1, 2, 4], [-4, -5, 6]]] solution = {1: 1, 2: 0, 3: 0, 4: 1, 5: 1, 6: 0} solution_found, unsat_result, unsat_list = SatUtils.solution_status_with_unsat_clauses( formula, solution) self.assertFalse(solution_found) self.assertEqual(unsat_result, 1) self.assertEqual(len(unsat_list), 1)
def main(self, wp, p, max_iterations, instance_path): cnf_contents = SatUtils.read_instance(instance_path) # instantiate required search and util objects novelty_search = NoveltySearch(cnf_contents, p, 1) variables = cnf_contents[0] walkSat = WalkSat() # initialize first solution proposal proposed_solution = SatUtils.initialize_variables(len(variables)) # start the timer start = time.time() end = None for i in range(max_iterations): # checking for timeout terminate condition if time.time() > start + 60: return solution_found, unsat_clause, unsat_clause_list = SatUtils.solution_status_with_unsat_clauses( cnf_contents, proposed_solution) # if a solution has been identified break out of the search loop and record it if solution_found is True: end = time.time() print("Iteration,{0},Duration,{1}".format(i, end - start)) return # pick algorithm to run the solution search based on probability if wp < random.uniform(0, 1): proposed_solution = walkSat.execute_walk( cnf_contents, proposed_solution) else: random_variable_to_flip = random.choice(variables) random_unsat_clause = random.choice(unsat_clause_list) best_flip = novelty_search.execute_search( proposed_solution, random_variable_to_flip, random_unsat_clause) proposed_solution[best_flip] = SatUtils.flip_var( proposed_solution[best_flip])
def main(self): proposed_solution = SatUtils.initialize_variables(self.variables) start = time.time() end = None for i in range(self.max_iterations): solution_found, unsat_clause, unsat_clause_list = SatUtils.solution_status_with_unsat_clauses( self.instance, proposed_solution) if solution_found is True: end = time.time() print("Iteration,{0},Duration,{1}".format(i, end - start)) return random_flip = random.choice(self.variables) random_clause = random.choice(unsat_clause_list) flip_this_index = self.execute_search(proposed_solution, random_flip, random_clause) proposed_solution[flip_this_index] = SatUtils.flip_var( proposed_solution[flip_this_index])
def test_initializing_variables(self): result = SatUtils.initialize_variables(10) print(result) self.assertEqual(len(result), 10)
def test_flip_var(self): result = SatUtils.flip_var(0) self.assertEqual(result, 1)
flip_scores, key=flip_scores.get) best = two_smallest_keys[0] second_best = two_smallest_keys[1] # generate noise value noise_value = random.uniform(0, 1) # compare noise value generated to the probability of second best variable being selected if noise_value < self.probability: return second_best else: return best if __name__ == '__main__': instance_path = None # check whether a path to input dataset has been provided or should the default path be used if len(sys.argv) > 1: instance_path = sys.argv[1] else: instance_path = "./sat_data/uf20-020.cnf" cnf_contents = SatUtils.read_instance(instance_path) solver = NoveltySearch(cnf_contents, 0.4, 100000) for i in range(1): solver.main()