-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from ImogenBits/instance_class
Change Problem class definition
- Loading branch information
Showing
21 changed files
with
482 additions
and
469 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,36 @@ | ||
"""The Biclique problem class.""" | ||
from typing import ClassVar | ||
|
||
from algobattle.problem import UndirectedGraph, SolutionModel, ValidationError | ||
from algobattle.util import u64 | ||
|
||
|
||
class Biclique(UndirectedGraph): | ||
"""The Biclique problem class.""" | ||
|
||
name: ClassVar[str] = "Bipartite Clique" | ||
min_size: ClassVar[int] = 5 | ||
|
||
class Solution(SolutionModel): | ||
"""A solution to a bipartite clique problem.""" | ||
|
||
direction: ClassVar = "maximize" | ||
|
||
s_1: set[u64] | ||
s_2: set[u64] | ||
|
||
def validate_solution(self, instance: "Biclique") -> None: | ||
edge_set = set(instance.edges) | set(edge[::-1] for edge in instance.edges) | ||
super().validate_solution(instance) | ||
if any(i >= instance.num_vertices for i in self.s_1 | self.s_2): | ||
raise ValidationError("Solution contains vertices that aren't in the instance.") | ||
if len(self.s_1.intersection(self.s_2)) != 0: | ||
raise ValidationError("Solution contains vertex sets that aren't disjoint.") | ||
if any((u, v) not in edge_set for u in self.s_1 for v in self.s_2): | ||
raise ValidationError("The instance graph is missing an edge between the solution vertex sets.") | ||
if any((u, v) in edge_set for u in self.s_1 for v in self.s_1) or any( | ||
(u, v) in edge_set for u in self.s_2 for v in self.s_2 | ||
): | ||
raise ValidationError("The solution is not a bipartite graph.") | ||
|
||
def score(self, instance: "Biclique") -> float: | ||
return len(self.s_1) + len(self.s_2) | ||
from algobattle.problem import Problem, UndirectedGraph, SolutionModel, ValidationError, maximize | ||
from algobattle.util import u64, Role | ||
|
||
|
||
class Solution(SolutionModel[UndirectedGraph]): | ||
"""A solution to a bipartite clique problem.""" | ||
|
||
s_1: set[u64] | ||
s_2: set[u64] | ||
|
||
def validate_solution(self, instance: UndirectedGraph, role: Role) -> None: | ||
edge_set = set(instance.edges) | set(edge[::-1] for edge in instance.edges) | ||
super().validate_solution(instance, role) | ||
if any(i >= instance.num_vertices for i in self.s_1 | self.s_2): | ||
raise ValidationError("Solution contains vertices that aren't in the instance.") | ||
if len(self.s_1.intersection(self.s_2)) != 0: | ||
raise ValidationError("Solution contains vertex sets that aren't disjoint.") | ||
if any((u, v) not in edge_set for u in self.s_1 for v in self.s_2): | ||
raise ValidationError("The instance graph is missing an edge between the solution vertex sets.") | ||
if any((u, v) in edge_set for u in self.s_1 for v in self.s_1) or any( | ||
(u, v) in edge_set for u in self.s_2 for v in self.s_2 | ||
): | ||
raise ValidationError("The solution is not a bipartite graph.") | ||
|
||
@maximize | ||
def score(self, instance: UndirectedGraph, role: Role) -> float: | ||
return len(self.s_1) + len(self.s_2) | ||
|
||
|
||
Biclique = Problem( | ||
name="Bipartite Clique", | ||
instance_cls=UndirectedGraph, | ||
solution_cls=Solution, | ||
min_size=5, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,66 @@ | ||
"""The C4subgraphiso problem class.""" | ||
from typing import ClassVar | ||
|
||
from algobattle.problem import UndirectedGraph, SolutionModel, ValidationError | ||
from algobattle.util import u64 | ||
from algobattle.problem import Problem, UndirectedGraph, SolutionModel, ValidationError, maximize | ||
from algobattle.util import u64, Role | ||
|
||
|
||
class C4subgraphiso(UndirectedGraph): | ||
"""The C4subgraphiso problem class.""" | ||
class Solution(SolutionModel[UndirectedGraph]): | ||
"""A solution to a Square Subgraph Isomorphism problem.""" | ||
|
||
name: ClassVar[str] = "Square Subgraph Isomorphism" | ||
min_size: ClassVar[int] = 4 | ||
squares: set[tuple[u64, u64, u64, u64]] | ||
|
||
class Solution(SolutionModel): | ||
"""A solution to a Square Subgraph Isomorphism problem.""" | ||
def validate_solution(self, instance: UndirectedGraph, role: Role) -> None: | ||
super().validate_solution(instance, role) | ||
self._all_entries_bounded_in_size(instance) | ||
self._all_squares_in_instance(instance) | ||
self._all_squares_node_disjoint() | ||
self._all_squares_induced(instance) | ||
|
||
direction: ClassVar = "maximize" | ||
def _all_entries_bounded_in_size(self, instance: UndirectedGraph) -> None: | ||
for square in self.squares: | ||
if any(node >= instance.num_vertices for node in square): | ||
raise ValidationError("An element of the solution doesn't index an instance vertex.") | ||
|
||
squares: set[tuple[u64, u64, u64, u64]] | ||
def _all_squares_node_disjoint(self) -> None: | ||
used_nodes = set() | ||
for square in self.squares: | ||
for node in square: | ||
if node in used_nodes: | ||
raise ValidationError("A square in the solution is not node-disjoint to at least one other square.") | ||
used_nodes.add(node) | ||
|
||
def validate_solution(self, instance: "C4subgraphiso") -> None: | ||
super().validate_solution(instance) | ||
self._all_entries_bounded_in_size(instance) | ||
self._all_squares_in_instance(instance) | ||
self._all_squares_node_disjoint() | ||
self._all_squares_induced(instance) | ||
def _all_squares_induced(self, instance: UndirectedGraph) -> None: | ||
edge_set = set(instance.edges) | ||
for square in self.squares: | ||
# Edges between opposing nodes of a square would mean the square is not induced by its nodes | ||
unwanted_edges = [ | ||
(square[0], square[2]), | ||
(square[2], square[0]), | ||
(square[1], square[3]), | ||
(square[3], square[1]), | ||
] | ||
if any(edge in edge_set for edge in unwanted_edges): | ||
raise ValidationError("A square in the solution is not induced in the instance.") | ||
|
||
def _all_entries_bounded_in_size(self, instance: "C4subgraphiso") -> None: | ||
for square in self.squares: | ||
if any(node >= instance.num_vertices for node in square): | ||
raise ValidationError("An element of the solution doesn't index an instance vertex.") | ||
def _all_squares_in_instance(self, instance: UndirectedGraph) -> None: | ||
edge_set = set(instance.edges) | ||
for square in self.squares: | ||
if ( | ||
not ((square[0], square[1]) in edge_set or (square[1], square[0]) in edge_set) | ||
or not ((square[1], square[2]) in edge_set or (square[2], square[1]) in edge_set) | ||
or not ((square[2], square[3]) in edge_set or (square[3], square[2]) in edge_set) | ||
or not ((square[3], square[0]) in edge_set or (square[0], square[3]) in edge_set) | ||
): | ||
raise ValidationError("A square is not part of the instance.") | ||
|
||
def _all_squares_node_disjoint(self) -> None: | ||
used_nodes = set() | ||
for square in self.squares: | ||
for node in square: | ||
if node in used_nodes: | ||
raise ValidationError( | ||
"A square in the solution is not node-disjoint to at least one other square." | ||
) | ||
used_nodes.add(node) | ||
@maximize | ||
def score(self, instance: UndirectedGraph, role: Role) -> float: | ||
return len(self.squares) | ||
|
||
def _all_squares_induced(self, instance: "C4subgraphiso") -> None: | ||
edge_set = set(instance.edges) | ||
for square in self.squares: | ||
# Edges between opposing nodes of a square would mean the square is not induced by its nodes | ||
unwanted_edges = [ | ||
(square[0], square[2]), | ||
(square[2], square[0]), | ||
(square[1], square[3]), | ||
(square[3], square[1]), | ||
] | ||
if any(edge in edge_set for edge in unwanted_edges): | ||
raise ValidationError("A square in the solution is not induced in the instance.") | ||
|
||
def _all_squares_in_instance(self, instance: "C4subgraphiso") -> None: | ||
edge_set = set(instance.edges) | ||
for square in self.squares: | ||
if ( | ||
not ((square[0], square[1]) in edge_set or (square[1], square[0]) in edge_set) | ||
or not ((square[1], square[2]) in edge_set or (square[2], square[1]) in edge_set) | ||
or not ((square[2], square[3]) in edge_set or (square[3], square[2]) in edge_set) | ||
or not ((square[3], square[0]) in edge_set or (square[0], square[3]) in edge_set) | ||
): | ||
raise ValidationError("A square is not part of the instance.") | ||
|
||
def score(self, instance: "C4subgraphiso") -> float: | ||
return len(self.squares) | ||
C4subgraphiso = Problem( | ||
name="Square Subgraph Isomorphism", | ||
instance_cls=UndirectedGraph, | ||
solution_cls=Solution, | ||
min_size=4, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,50 @@ | ||
"""The Clusterediting problem class.""" | ||
from collections import defaultdict | ||
from itertools import combinations | ||
from typing import ClassVar | ||
|
||
from algobattle.problem import UndirectedGraph, SolutionModel, ValidationError | ||
from algobattle.util import u64 | ||
from algobattle.problem import Problem, UndirectedGraph, SolutionModel, ValidationError, minimize | ||
from algobattle.util import u64, Role | ||
|
||
|
||
class Clusterediting(UndirectedGraph): | ||
"""The Clusterediting problem class.""" | ||
class Solution(SolutionModel[UndirectedGraph]): | ||
"""A solution to a Cluster Editing problem.""" | ||
|
||
name: ClassVar[str] = "Cluster Editing" | ||
min_size: ClassVar[int] = 4 | ||
add: set[tuple[u64, u64]] | ||
delete: set[tuple[u64, u64]] | ||
|
||
class Solution(SolutionModel): | ||
"""A solution to a Cluster Editing problem.""" | ||
def validate_solution(self, instance: UndirectedGraph, role: Role) -> None: | ||
edge_set = set(instance.edges) | ||
|
||
direction: ClassVar = "minimize" | ||
# Apply modifications to graph | ||
for edge in self.add: | ||
if edge[::-1] not in edge_set: | ||
edge_set.add(edge) | ||
for edge in self.delete: | ||
if edge in edge_set: | ||
edge_set.remove(edge) | ||
elif (edge[1], edge[0]) in edge_set: | ||
edge_set.remove((edge[1], edge[0])) | ||
else: | ||
raise ValidationError("Solution contains edge not found in instance.") | ||
|
||
add: set[tuple[u64, u64]] | ||
delete: set[tuple[u64, u64]] | ||
neighbors: defaultdict[int, set[int]] = defaultdict(set) | ||
for u, v in edge_set: | ||
neighbors[u].add(v) | ||
neighbors[v].add(u) | ||
|
||
def validate_solution(self, instance: "Clusterediting") -> None: | ||
edge_set = set(instance.edges) | ||
for u in range(instance.num_vertices): | ||
for v, w in combinations(neighbors[u], 2): | ||
if not (v, w) in edge_set and not (w, v) in edge_set: | ||
raise ValidationError("The solution does not transform the graph into a cluster.") | ||
|
||
# Apply modifications to graph | ||
for edge in self.add: | ||
if edge[::-1] not in edge_set: | ||
edge_set.add(edge) | ||
for edge in self.delete: | ||
if edge in edge_set: | ||
edge_set.remove(edge) | ||
elif (edge[1], edge[0]) in edge_set: | ||
edge_set.remove((edge[1], edge[0])) | ||
else: | ||
raise ValidationError("Solution contains edge not found in instance.") | ||
@minimize | ||
def score(self, instance: UndirectedGraph, role: Role) -> float: | ||
return len(self.add) + len(self.delete) | ||
|
||
neighbors: defaultdict[int, set[int]] = defaultdict(set) | ||
for u, v in edge_set: | ||
neighbors[u].add(v) | ||
neighbors[v].add(u) | ||
|
||
for u in range(instance.num_vertices): | ||
for v, w in combinations(neighbors[u], 2): | ||
if not (v, w) in edge_set and not (w, v) in edge_set: | ||
raise ValidationError("The solution does not transform the graph into a cluster.") | ||
|
||
def score(self, instance: "Clusterediting") -> float: | ||
return len(self.add) + len(self.delete) | ||
Clusterediting = Problem( | ||
name="Cluster Editing", | ||
min_size=4, | ||
instance_cls=UndirectedGraph, | ||
solution_cls=Solution, | ||
) |
Oops, something went wrong.