Coverage for sparkle/solver/verifiers.py: 91%
57 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-07 15:22 +0000
1"""Classes for verifying solutions."""
2from __future__ import annotations
3from pathlib import Path
4import subprocess
6from sparkle.types import SolverStatus
9class SolutionVerifier:
10 """Solution verifier base class."""
12 def verify(self: SolutionVerifier,
13 instance: Path,
14 output: dict,
15 solver_call: list[str]) -> SolverStatus:
16 """Verify the solution."""
17 raise NotImplementedError
20class SATVerifier(SolutionVerifier):
21 """Class to handle the SAT verifier."""
22 sat_verifier_path =\
23 Path(__file__).parent.parent / "Components/Sparkle-SAT-verifier/SAT"
25 def __str__(self: SATVerifier) -> str:
26 """Return the name of the SAT verifier."""
27 return SATVerifier.__name__
29 @staticmethod
30 def verify(instance: Path, output: dict, solver_call: list[str]) -> SolverStatus:
31 """Run a SAT verifier and return its status."""
32 if SolverStatus(output["status"]) == SolverStatus.TIMEOUT:
33 return SolverStatus.TIMEOUT
34 raw_result = Path([s for s in solver_call if s.endswith(".rawres")][0])
35 return SATVerifier.call_sat_raw_result(instance, raw_result)
37 @staticmethod
38 def sat_verify_output(sat_output: str) -> SolverStatus:
39 """Return the status of the SAT verifier.
41 Four statuses are possible: "SAT", "UNSAT", "WRONG", "UNKNOWN"
42 """
43 return_code = int(sat_output.splitlines()[-1])
44 if return_code == 11: # SAT code
45 return SolverStatus.SAT
46 if return_code == 10: # UNSAT code
47 return SolverStatus.UNSAT
48 # Wrong code OR Unknown code
49 if return_code == 0 and "Wrong solution." in sat_output:
50 return SolverStatus.WRONG
51 # Should not occur
52 return SolverStatus.UNKNOWN
54 @staticmethod
55 def call_sat_raw_result(instance: Path,
56 raw_result: Path) -> SolverStatus:
57 """Run a SAT verifier to determine correctness of a result.
59 Args:
60 instance: path to the instance
61 raw_result: path to the result to verify
63 Returns:
64 The status of the solver on the instance
65 """
66 sat_verify = subprocess.run([SATVerifier.sat_verifier_path,
67 instance,
68 raw_result],
69 capture_output=True)
70 return SATVerifier.sat_verify_output(sat_verify.stdout.decode())
73class SolutionFileVerifier(SolutionVerifier):
74 """Class to handle the file verifier."""
76 def __init__(self: SolutionFileVerifier, csv_file: Path) -> None:
77 """Initialize the verifier by building dictionary from csv.
79 Args:
80 csv_file: path to the csv file. Requires lines to be of the form:
81 instance,objective,solution
82 """
83 self.csv_file = csv_file
84 lines = [line.split(",", maxsplit=2)
85 for line in self.csv_file.read_text().splitlines()]
86 self.solutions = {instance: (objective, solution)
87 for instance, objective, solution in lines}
89 def __str__(self: SATVerifier) -> str:
90 """Return the name of the SAT verifier."""
91 return SolutionFileVerifier.__name__
93 def verify(self: SolutionFileVerifier,
94 instance: Path,
95 solver_output: dict[str, object],
96 solver_call: list[str]) -> SolverStatus:
97 """Verify the solution.
99 Args:
100 instance: instance to verify, solution found by instance name as key
101 solver_output: outcome of the solver to verify
103 Returns:
104 The status of the solver on the instance
105 """
106 # If the solver ran out of time, we cannot verify a solution
107 if SolverStatus(solver_output["status"]) == SolverStatus.TIMEOUT:
108 return SolverStatus.TIMEOUT
109 instance = instance.name
110 if instance not in self.solutions:
111 return SolverStatus.UNKNOWN
113 objective, solution = self.solutions[instance]
114 if objective not in solver_output:
115 return SolverStatus.UNKNOWN
117 outcome = solver_output[objective]
118 if solution in SolverStatus._value2member_map_: # SAT type status
119 solution, outcome = SolverStatus(solution), SolverStatus(outcome)
120 if solution == SolverStatus.UNKNOWN or solution == outcome:
121 # Verify that the presented solution is correct
122 return SATVerifier.verify(instance, solver_output, solver_call)
123 else:
124 return SolverStatus.WRONG
126 if solution != outcome:
127 return SolverStatus.WRONG
128 return SolverStatus.SUCCESS
131# Define a mapping so we can translate between names and classes
132mapping = {SATVerifier.__name__: SATVerifier,
133 SolutionFileVerifier.__name__: SolutionFileVerifier}