From 06c9c66bdb556235f47fe61f21fab802f8211326 Mon Sep 17 00:00:00 2001 From: Kacper Date: Thu, 12 Jun 2025 10:58:20 +0200 Subject: [PATCH 1/4] Removed default version and region in Advantage --- .../solvers/quantum_annealing/dwave/advantage.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/QHyper/solvers/quantum_annealing/dwave/advantage.py b/QHyper/solvers/quantum_annealing/dwave/advantage.py index c93099c..51301fa 100644 --- a/QHyper/solvers/quantum_annealing/dwave/advantage.py +++ b/QHyper/solvers/quantum_annealing/dwave/advantage.py @@ -46,8 +46,8 @@ class Advantage(Solver): problem: Problem penalty_weights: list[float] | None = None - version: str = "Advantage_system5.4" - region: str = "eu-central-1" + version: str | None = None + region: str | None = None num_reads: int = 1 chain_strength: float | None = None token: str | None = None @@ -55,8 +55,8 @@ class Advantage(Solver): def __init__(self, problem: Problem, penalty_weights: list[float] | None = None, - version: str = "Advantage_system5.4", - region: str = "eu-central-1", + version: str | None = None, + region: str | None = None, num_reads: int = 1, chain_strength: float | None = None, use_clique_embedding: bool = False, @@ -68,9 +68,14 @@ def __init__(self, self.num_reads = num_reads self.chain_strength = chain_strength self.use_clique_embedding = use_clique_embedding - self.sampler = DWaveSampler( + if (self.version and not self.region) or (self.region and not self.version): + raise ValueError("Both 'version' and 'region' must be specified together.") + if self.version and self.region: + self.sampler = DWaveSampler( solver=self.version, region=self.region, token=token or DWAVE_API_TOKEN) + else: + self.sampler = DWaveSampler(token=token or DWAVE_API_TOKEN) self.token = token if use_clique_embedding: From 65ee6a4d0526890e3d229e4b3f6fcdefce01bcc9 Mon Sep 17 00:00:00 2001 From: Kacper Date: Thu, 12 Jun 2025 12:31:29 +0200 Subject: [PATCH 2/4] Fixed bug with 0 --- QHyper/polynomial.py | 7 +++++++ QHyper/problems/community_detection.py | 4 ++++ QHyper/solvers/quantum_annealing/dwave/advantage.py | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/QHyper/polynomial.py b/QHyper/polynomial.py index 60befba..f0980a6 100644 --- a/QHyper/polynomial.py +++ b/QHyper/polynomial.py @@ -64,11 +64,16 @@ def __init__(self, terms: dict[tuple[str, ...], float] | float | int self.terms = defaultdict(float) + non_zero_found = False for term, coefficient in terms.items(): if coefficient == 0: continue + non_zero_found = True self.terms[tuple(sorted(term))] += coefficient + if not non_zero_found: + self.terms[tuple()] = 0.0 + @overload def __add__(self, other: float | int) -> 'Polynomial': ... @@ -204,6 +209,8 @@ def degree(self) -> int: int The degree of the polynomial. """ + if not self.terms: + return 0 return max(len(term) for term in self.terms) def get_variables(self) -> set[str]: diff --git a/QHyper/problems/community_detection.py b/QHyper/problems/community_detection.py index 29e5e4c..ed59485 100644 --- a/QHyper/problems/community_detection.py +++ b/QHyper/problems/community_detection.py @@ -164,6 +164,10 @@ def _set_objective_function(self) -> None: equation = {key: -1 * val for key, val in equation.items()} + nonzero_terms = sum(1 for v in equation.values() if not np.isclose(v, 0.0)) + if nonzero_terms == 0: + raise ValueError(f"QUBO is empty — all terms in modularity matrix are 0 or have negative values. Try lower resolution (current: {self.resolution})") + self.objective_function = Polynomial(equation) def _encode_discrete_to_one_hot( diff --git a/QHyper/solvers/quantum_annealing/dwave/advantage.py b/QHyper/solvers/quantum_annealing/dwave/advantage.py index 51301fa..183696a 100644 --- a/QHyper/solvers/quantum_annealing/dwave/advantage.py +++ b/QHyper/solvers/quantum_annealing/dwave/advantage.py @@ -79,7 +79,7 @@ def __init__(self, self.token = token if use_clique_embedding: - args = self.weigths if self.weigths else [] + args = self.penalty_weights if self.penalty_weights else [] qubo = Converter.create_qubo(self.problem, args) qubo_terms, offset = convert_qubo_keys(qubo) bqm = BinaryQuadraticModel.from_qubo(qubo_terms, offset=offset) From 02e340305cfaeb4be041ba8e80f2a36177ee50dd Mon Sep 17 00:00:00 2001 From: Kacper Date: Tue, 15 Jul 2025 09:19:04 +0200 Subject: [PATCH 3/4] Full modularity matrix will calculate only once --- QHyper/problems/community_detection.py | 25 +++++++++++-------------- requirements/prod.txt | 1 + 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/QHyper/problems/community_detection.py b/QHyper/problems/community_detection.py index ed59485..327e014 100644 --- a/QHyper/problems/community_detection.py +++ b/QHyper/problems/community_detection.py @@ -22,34 +22,31 @@ class Network: resolution: float = 1.0 weight: str | None = "weight" community: list | None = None - full_modularity_matrix: np.ndarray = field(init=False) + full_modularity_matrix: np.ndarray | None = None generalized_modularity_matrix: np.ndarray = field(init=False) def __post_init__(self) -> None: if not self.community: self.community = [*range(self.graph.number_of_nodes())] - ( - self.full_modularity_matrix, - self.generalized_modularity_matrix, - ) = self.calculate_modularity_matrix() + if self.full_modularity_matrix is None: + self.full_modularity_matrix = self.calculate_full_modularity_matrix() + self.generalized_modularity_matrix = self.calculate_generalized_modularity_matrix() + - def calculate_modularity_matrix(self) -> np.ndarray: + def calculate_full_modularity_matrix(self) -> np.ndarray: adj_matrix: np.ndarray = nx.to_numpy_array(self.graph, weight=self.weight) in_degree_matrix: np.ndarray = adj_matrix.sum(axis=1) out_degree_matrix: np.ndarray = adj_matrix.sum(axis=0) m: int = np.sum(adj_matrix) - full_modularity_matrix = ( - adj_matrix - - self.resolution * np.outer(in_degree_matrix, out_degree_matrix) / m - ) - - B_bis = full_modularity_matrix[self.community, :] + return adj_matrix - self.resolution * np.outer(in_degree_matrix, out_degree_matrix) / m + + def calculate_generalized_modularity_matrix(self) -> np.ndarray: + B_bis = self.full_modularity_matrix[self.community, :] B_community = B_bis[:, self.community] B_i = np.sum(B_community, axis=1) B_j = np.sum(B_community.T, axis=1) delta = np.eye(len(self.community), dtype=np.int32) - B_g = 0.5*( B_community + B_community.T ) - 0.5 * delta * (B_i + B_j) - return full_modularity_matrix, B_g + return 0.5 * (B_community + B_community.T) - 0.5 * delta * (B_i + B_j) class KarateClubNetwork(Network): diff --git a/requirements/prod.txt b/requirements/prod.txt index 8feee23..ad58b1b 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -6,4 +6,5 @@ dwave-system gurobipy types-tqdm pandas +autoray==0.6.11 git+https://github.com/wfcommons/wfcommons.git@main From dc91e36c4be6f41f797d1e78575451a1ae1037cb Mon Sep 17 00:00:00 2001 From: Kacper Date: Sun, 2 Nov 2025 20:30:19 +0100 Subject: [PATCH 4/4] Changed error value and applied Black formatter --- QHyper/problems/community_detection.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/QHyper/problems/community_detection.py b/QHyper/problems/community_detection.py index 327e014..9247d94 100644 --- a/QHyper/problems/community_detection.py +++ b/QHyper/problems/community_detection.py @@ -30,16 +30,20 @@ def __post_init__(self) -> None: self.community = [*range(self.graph.number_of_nodes())] if self.full_modularity_matrix is None: self.full_modularity_matrix = self.calculate_full_modularity_matrix() - self.generalized_modularity_matrix = self.calculate_generalized_modularity_matrix() - + self.generalized_modularity_matrix = ( + self.calculate_generalized_modularity_matrix() + ) def calculate_full_modularity_matrix(self) -> np.ndarray: adj_matrix: np.ndarray = nx.to_numpy_array(self.graph, weight=self.weight) in_degree_matrix: np.ndarray = adj_matrix.sum(axis=1) out_degree_matrix: np.ndarray = adj_matrix.sum(axis=0) m: int = np.sum(adj_matrix) - return adj_matrix - self.resolution * np.outer(in_degree_matrix, out_degree_matrix) / m - + return ( + adj_matrix + - self.resolution * np.outer(in_degree_matrix, out_degree_matrix) / m + ) + def calculate_generalized_modularity_matrix(self) -> np.ndarray: B_bis = self.full_modularity_matrix[self.community, :] B_community = B_bis[:, self.community] @@ -128,9 +132,9 @@ def __init__( self._set_objective_function() self._set_one_hot_constraints(communities) else: - self.variables: tuple[ - sympy.Symbol - ] = self._get_discrete_variable_representation() + self.variables: tuple[sympy.Symbol] = ( + self._get_discrete_variable_representation() + ) self._set_objective_function() def _get_discrete_variable_representation( @@ -163,7 +167,9 @@ def _set_objective_function(self) -> None: nonzero_terms = sum(1 for v in equation.values() if not np.isclose(v, 0.0)) if nonzero_terms == 0: - raise ValueError(f"QUBO is empty — all terms in modularity matrix are 0 or have negative values. Try lower resolution (current: {self.resolution})") + raise ValueError( + f"The objective function is a zero polynomial - all terms in the generalized modularity matrix are 0. Try different resolution (current: {self.resolution})" + ) self.objective_function = Polynomial(equation)