qhyper 0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. QHyper/__init__.py +0 -0
  2. QHyper/optimizers/__init__.py +17 -0
  3. QHyper/optimizers/base.py +39 -0
  4. QHyper/optimizers/basinhopping.py +79 -0
  5. QHyper/optimizers/cem.py +128 -0
  6. QHyper/optimizers/qml_gradient_descent.py +72 -0
  7. QHyper/optimizers/random.py +90 -0
  8. QHyper/optimizers/scipy_minimizer.py +65 -0
  9. QHyper/problems/__init__.py +0 -0
  10. QHyper/problems/base.py +62 -0
  11. QHyper/problems/community_detection.py +185 -0
  12. QHyper/problems/knapsack.py +176 -0
  13. QHyper/problems/maxcut.py +37 -0
  14. QHyper/problems/tsp.py +190 -0
  15. QHyper/problems/workflow_scheduling.py +220 -0
  16. QHyper/solvers/__init__.py +14 -0
  17. QHyper/solvers/base.py +86 -0
  18. QHyper/solvers/converter.py +128 -0
  19. QHyper/solvers/cqm/__init__.py +1 -0
  20. QHyper/solvers/cqm/cqm.py +66 -0
  21. QHyper/solvers/dqm/__init__.py +1 -0
  22. QHyper/solvers/dqm/dqm.py +23 -0
  23. QHyper/solvers/gurobi/__init__.py +1 -0
  24. QHyper/solvers/gurobi/gurobi.py +77 -0
  25. QHyper/solvers/vqa/__init__.py +25 -0
  26. QHyper/solvers/vqa/base.py +108 -0
  27. QHyper/solvers/vqa/eval_funcs/__init__.py +0 -0
  28. QHyper/solvers/vqa/eval_funcs/base.py +40 -0
  29. QHyper/solvers/vqa/eval_funcs/wfeval.py +32 -0
  30. QHyper/solvers/vqa/pqc/__init__.py +0 -0
  31. QHyper/solvers/vqa/pqc/base.py +133 -0
  32. QHyper/solvers/vqa/pqc/h_qaoa.py +86 -0
  33. QHyper/solvers/vqa/pqc/mixers.py +12 -0
  34. QHyper/solvers/vqa/pqc/qaoa.py +130 -0
  35. QHyper/solvers/vqa/pqc/wf_qaoa.py +80 -0
  36. QHyper/util.py +4 -0
  37. qhyper-0.dev1.dist-info/LICENSE.txt +21 -0
  38. qhyper-0.dev1.dist-info/METADATA +26 -0
  39. qhyper-0.dev1.dist-info/RECORD +41 -0
  40. qhyper-0.dev1.dist-info/WHEEL +5 -0
  41. qhyper-0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,220 @@
1
+ import math
2
+ from collections import defaultdict
3
+ from dataclasses import dataclass
4
+
5
+ import networkx as nx
6
+ import numpy as np
7
+ import pandas as pd
8
+ import sympy
9
+ from wfcommons import Instance
10
+ from wfcommons.utils import read_json
11
+
12
+ from networkx.classes.reportviews import NodeView
13
+
14
+ from sympy.core.expr import Expr
15
+ from typing import cast
16
+
17
+ from QHyper.hyperparameter_gen.parser import Expression
18
+ from .base import Problem
19
+
20
+
21
+ @dataclass
22
+ class TargetMachine:
23
+ name: str
24
+ memory: int
25
+ cpu: dict[str, float]
26
+ price: float
27
+ memory_cost_multiplier: float
28
+
29
+
30
+ class Workflow:
31
+ def __init__(
32
+ self, tasks_file: str, machines_file: str, deadline: float
33
+ ) -> None:
34
+ self.wf_instance = Instance(tasks_file)
35
+ self.tasks = self._get_tasks()
36
+ self.machines = self._get_machines(machines_file)
37
+ self.deadline = deadline
38
+ self._set_paths()
39
+ self.time_matrix, self.cost_matrix = self._calc_dataframes()
40
+ self.task_names = self.time_matrix.index
41
+ self.machine_names = self.time_matrix.columns
42
+
43
+ def _get_tasks(self) -> NodeView:
44
+ return self.wf_instance.workflow.nodes(data=True)
45
+
46
+ def _get_machines(self, machines_file: str) -> dict[str, TargetMachine]:
47
+ target_machines = read_json(machines_file)
48
+ return {
49
+ machine['name']: TargetMachine(**machine)
50
+ for machine in target_machines["machines"]
51
+ }
52
+
53
+ def _set_paths(self) -> None:
54
+ all_paths = []
55
+ for root in self.wf_instance.roots():
56
+ for leaf in self.wf_instance.leaves():
57
+ paths = nx.all_simple_paths(
58
+ self.wf_instance.workflow, root, leaf)
59
+ all_paths.extend(paths)
60
+
61
+ self.paths = all_paths
62
+
63
+ def _calc_dataframes(self) -> tuple[pd.DataFrame, pd.DataFrame]:
64
+ costs, runtimes = {}, {}
65
+ for machine_name, machine_details in self.machines.items():
66
+ machine_cost, machine_runtime = [], []
67
+ for task_name, task in self.tasks:
68
+ old_machine = task["task"].machine
69
+ number_of_operations = (
70
+ task["task"].runtime
71
+ * old_machine.cpu_speed
72
+ * old_machine.cpu_cores
73
+ * 10 ** 6
74
+ )
75
+ # todo can this overflow?
76
+ real_runtime = number_of_operations / (
77
+ machine_details.cpu["speed"]
78
+ * machine_details.cpu["count"] * 10 ** 6)
79
+ machine_runtime.append(real_runtime)
80
+ machine_cost.append(real_runtime * machine_details.price)
81
+ costs[machine_name] = machine_cost
82
+ runtimes[machine_name] = machine_runtime
83
+
84
+ time_df = pd.DataFrame(
85
+ data=runtimes, index=self.wf_instance.workflow.nodes)
86
+ cost_df = pd.DataFrame(
87
+ data=costs, index=self.wf_instance.workflow.nodes)
88
+
89
+ return time_df, cost_df
90
+
91
+
92
+ def calc_slack_coefficients(constant: int) -> list[int]:
93
+ num_slack = int(np.floor(np.log2(constant)))
94
+ slack_coefficients = [2 ** j for j in range(num_slack)]
95
+ if constant - 2 ** num_slack >= 0:
96
+ slack_coefficients.append(constant - 2 ** num_slack + 1)
97
+ return slack_coefficients
98
+
99
+
100
+ class WorkflowSchedulingProblem(Problem):
101
+ def __init__(self, workflow: Workflow):
102
+ self.workflow: Workflow = workflow
103
+ self.slack_coefficients = self._get_slacks()
104
+ self.variables: tuple[sympy.Symbol] = sympy.symbols(' '.join(
105
+ [f'x{i}' for i in range(
106
+ len(self.workflow.tasks) * len(self.workflow.machines))]
107
+ ) + ' ' + ' '.join(
108
+ [f's{i}' for i in range(len(self.slack_coefficients))]))
109
+
110
+ self._set_objective_function()
111
+ self._set_constraints()
112
+
113
+ def _get_slacks(self) -> list[int]:
114
+ min_path_runtime, _ = self.get_deadlines()
115
+ deadline_diff = int(self.workflow.deadline - min_path_runtime)
116
+ return calc_slack_coefficients(deadline_diff)
117
+
118
+ def _set_objective_function(self) -> None:
119
+ expression: Expr = cast(Expr, 0)
120
+ for task_id, task_name in enumerate(self.workflow.time_matrix.index):
121
+ for machine_id, machine_name in enumerate(
122
+ self.workflow.time_matrix.columns):
123
+ cost = self.workflow.cost_matrix[machine_name][task_name]
124
+ expression += cost * self.variables[
125
+ machine_id
126
+ + task_id * len(self.workflow.time_matrix.columns)
127
+ ]
128
+
129
+ self.objective_function: Expression = Expression(expression)
130
+
131
+ def _set_constraints(self) -> None:
132
+ self.constraints: list[Expression] = []
133
+
134
+ # machine assignment constraint
135
+ for task_id in range(len(self.workflow.time_matrix.index)):
136
+ expression: Expr = cast(Expr, 0)
137
+ for machine_id in range(len(self.workflow.time_matrix.columns)):
138
+ expression += self.variables[
139
+ machine_id
140
+ + task_id * len(self.workflow.time_matrix.columns)
141
+ ]
142
+ expression -= 1
143
+
144
+ self.constraints.append(Expression(expression))
145
+
146
+ # deadline constraint
147
+
148
+ min_deadline, _ = self.get_deadlines()
149
+ # deadline_for_slacks = int(self.workflow.deadline - min_deadline)
150
+
151
+ for path in self.workflow.paths:
152
+ expression = cast(Expr, -self.workflow.deadline)
153
+ for task_id, task_name in enumerate(
154
+ self.workflow.time_matrix.index):
155
+ for machine_id, machine_name in enumerate(
156
+ self.workflow.time_matrix.columns):
157
+ if task_name in path:
158
+ time = self.workflow.time_matrix[
159
+ machine_name][task_name]
160
+ expression += time * self.variables[
161
+ machine_id
162
+ + task_id * len(self.workflow.time_matrix.columns)
163
+ ]
164
+
165
+ first_slack_index = (
166
+ len(self.workflow.time_matrix.index)
167
+ * len(self.workflow.time_matrix.columns)
168
+ )
169
+ for i, coefficient in enumerate(self.slack_coefficients):
170
+ expression += (
171
+ coefficient * self.variables[first_slack_index + i])
172
+
173
+ self.constraints.append(Expression(expression))
174
+
175
+ def check_solution_correctness(self) -> None:
176
+ raise NotImplementedError # todo check if slack values are correct
177
+
178
+ def decode_solution(self, solution: dict) -> dict:
179
+ decoded_solution = {}
180
+ for variable, value in solution.items():
181
+ _, id = variable[0], int(variable[1:]) # todo add validation
182
+ if value == 1.0:
183
+ machine_id = id % len(self.workflow.machines)
184
+ task_id = id // len(self.workflow.machines)
185
+ decoded_solution[self.workflow.time_matrix.index[task_id]] = (
186
+ self.workflow.time_matrix.columns[machine_id])
187
+
188
+ return decoded_solution
189
+
190
+ def get_deadlines(self) -> tuple[float, float]: # todo test this function
191
+ """Calculates the minimum and maximum path runtime
192
+ for the whole workflow."""
193
+
194
+ flat_runtimes = [
195
+ (runtime, name)
196
+ for n, machine_runtimes in self.workflow.time_matrix.items()
197
+ for runtime, name in zip(
198
+ machine_runtimes, self.workflow.task_names)
199
+ ]
200
+
201
+ max_path_runtime = 0.0
202
+ min_path_runtime = 0.0
203
+
204
+ for path in self.workflow.paths:
205
+ max_runtime: defaultdict[str, float] = defaultdict(lambda: 0.0)
206
+ min_runtime: defaultdict[str, float] = defaultdict(
207
+ lambda: math.inf)
208
+
209
+ for runtime, name in flat_runtimes:
210
+ if name not in path:
211
+ continue
212
+ max_runtime[name] = max(max_runtime[name], runtime)
213
+ min_runtime[name] = min(min_runtime[name], runtime)
214
+ max_path_runtime = max(max_path_runtime, sum(max_runtime.values()))
215
+ min_path_runtime = max(min_path_runtime, sum(min_runtime.values()))
216
+
217
+ return min_path_runtime, max_path_runtime
218
+
219
+ def get_score(self, result: str, penalty: float = 0) -> float:
220
+ return 0
@@ -0,0 +1,14 @@
1
+ from typing import Type
2
+
3
+ from .base import Solver
4
+
5
+ from .vqa.base import VQA
6
+ from .gurobi.gurobi import Gurobi
7
+ from .cqm.cqm import CQM
8
+
9
+
10
+ SOLVERS: dict[str, Type[Solver]] = {
11
+ 'vqa': VQA,
12
+ 'gurobi': Gurobi,
13
+ 'cqm': CQM,
14
+ }
QHyper/solvers/base.py ADDED
@@ -0,0 +1,86 @@
1
+ from abc import abstractmethod
2
+
3
+ from typing import Any, Optional
4
+
5
+ from QHyper.problems.base import Problem
6
+ from QHyper.optimizers import OPTIMIZERS_BY_NAME
7
+ from QHyper.optimizers.base import Optimizer
8
+
9
+
10
+ class Solver:
11
+ """
12
+ Abstract base class for solvers.
13
+
14
+ Attributes
15
+ ----------
16
+ problem : Problem
17
+ The problem to be solved.
18
+ hyper_optimizer: Optimizer, optional
19
+ Hyperparameter optmizer.
20
+ """
21
+
22
+ problem: Problem
23
+ hyper_optimizer: Optimizer = None
24
+
25
+ def __init__(self, problem: Problem, **kwargs: Any) -> None:
26
+ pass
27
+
28
+ @staticmethod
29
+ def from_config(problem: Problem, config: dict[str, Any]) -> 'Solver':
30
+ """
31
+ Alternative way of creating solver.
32
+ Expect dict with two keys:
33
+ - type - type of solver
34
+ - args - arguments which will be passed to Solver instance
35
+
36
+ Parameters
37
+ ----------
38
+ problem : Problem
39
+ The problem to be solved
40
+ config : dict[str. Any]
41
+ Configuration in form of dict
42
+
43
+ Returns
44
+ -------
45
+ Solver
46
+ Initialized Solver object
47
+ """
48
+ from QHyper.solvers import SOLVERS
49
+
50
+ try:
51
+ solver_type = config['solver'].pop('type').lower()
52
+ solver_args = config['solver'].pop('args')
53
+ except KeyError:
54
+ raise Exception("Configuration for Solver was not provided")
55
+
56
+ solver = SOLVERS[solver_type](problem, **solver_args)
57
+
58
+ if 'hyper_optimizer' in config:
59
+ try:
60
+ optimizer_type = config['hyper_optimizer'].pop('type')
61
+ except KeyError:
62
+ raise Exception("Type for hyper optimizer was not provided")
63
+
64
+ solver.hyper_optimizer = OPTIMIZERS_BY_NAME[optimizer_type](
65
+ **config['hyper_optimizer']
66
+ )
67
+ return solver
68
+
69
+ @abstractmethod
70
+ def solve(
71
+ self,
72
+ params_inits: dict[str, Any]
73
+ ) -> Any:
74
+ """
75
+ Parameters
76
+ ----------
77
+ params_inits : dict[str, Any], optional
78
+ Initial parameters for the optimization. Default is None.
79
+
80
+ Returns
81
+ -------
82
+ Any
83
+ The result of the solver.
84
+ """
85
+
86
+ ...
@@ -0,0 +1,128 @@
1
+ from typing import Any, cast
2
+
3
+ import dimod
4
+ from dimod import ConstrainedQuadraticModel, DiscreteQuadraticModel
5
+ from QHyper.hyperparameter_gen.parser import Expression
6
+ from QHyper.problems.base import Problem
7
+ from QHyper.util import QUBO, VARIABLES
8
+
9
+
10
+ def dict_to_list(my_dict: QUBO) -> list[tuple[Any, ...]]:
11
+ return [tuple([*key, val]) for key, val in my_dict.items()]
12
+
13
+
14
+ class Converter:
15
+ @staticmethod
16
+ def create_qubo(problem: Problem, weights: list[float]) -> QUBO:
17
+ results: dict[VARIABLES, float] = {}
18
+
19
+ if len(weights) != len(problem.constraints) + 1:
20
+ raise Exception(
21
+ f"Expected {len(problem.constraints)+1} weights, "
22
+ f"got {len(weights)}"
23
+ )
24
+
25
+ objective_function = Expression(
26
+ {
27
+ key: weights[0] * val
28
+ for key, val in problem.objective_function.as_dict().items()
29
+ }
30
+ )
31
+ for key, value in objective_function.as_dict().items():
32
+ if key in results:
33
+ results[key] += value
34
+ else:
35
+ results[key] = value
36
+
37
+ constraint_weights = weights[1:]
38
+
39
+ for weight, element in zip(constraint_weights, problem.constraints):
40
+ constraint = Expression(element.polynomial**2 * weight)
41
+ for key, value in constraint.as_dict().items():
42
+ if key in results:
43
+ results[key] += value
44
+ else:
45
+ results[key] = value
46
+
47
+ return results
48
+
49
+ @staticmethod
50
+ def create_weight_free_qubo(problem: Problem) -> QUBO:
51
+ results: dict[VARIABLES, float] = {}
52
+
53
+ objective_function = Expression(problem.objective_function.polynomial)
54
+ for key, value in objective_function.as_dict().items():
55
+ if key in results:
56
+ results[key] += value
57
+ else:
58
+ results[key] = value
59
+
60
+
61
+ return results
62
+
63
+ @staticmethod
64
+ def to_cqm(problem: Problem) -> ConstrainedQuadraticModel:
65
+ binary_polynomial = dimod.BinaryPolynomial(
66
+ problem.objective_function.as_dict(), dimod.BINARY
67
+ )
68
+ cqm = dimod.make_quadratic_cqm(binary_polynomial)
69
+
70
+ # todo this cqm can probably be initialized in some other way
71
+ for var in problem.variables:
72
+ if str(var) not in cqm.variables:
73
+ cqm.add_variable(dimod.BINARY, str(var))
74
+
75
+ for i, constraint in enumerate(problem.constraints):
76
+ cqm.add_constraint(
77
+ dict_to_list(constraint.as_dict()), "==", label=i
78
+ )
79
+
80
+ return cqm
81
+
82
+ @staticmethod
83
+ def to_qubo(problem: Problem) -> tuple[QUBO, float]:
84
+ cqm = Converter.to_cqm(problem)
85
+ bqm, _ = dimod.cqm_to_bqm(cqm, lagrange_multiplier=10)
86
+ return cast(tuple[QUBO, float], bqm.to_qubo()) # (qubo, offset)
87
+
88
+ @staticmethod
89
+ def to_dqm(problem: Problem) -> DiscreteQuadraticModel:
90
+ dqm = dimod.DiscreteQuadraticModel()
91
+
92
+ BIN_OFFSET = 1 if problem.cases == 1 else 0
93
+
94
+ def binary_to_discrete(v: str) -> str:
95
+ id = int(v[1:])
96
+ discrete_id = id // problem.cases
97
+ return f"x{discrete_id}"
98
+
99
+ variables_discrete = [
100
+ binary_to_discrete(str(v))
101
+ for v in problem.variables[:: problem.cases]
102
+ ]
103
+ for var in variables_discrete:
104
+ if var not in dqm.variables:
105
+ dqm.add_variable(problem.cases + BIN_OFFSET, var)
106
+
107
+ for vars, bias in problem.objective_function.as_dict().items():
108
+ s_i, *s_j = vars
109
+ x_i = binary_to_discrete(s_i)
110
+ xi_idx: int = cast(int, dqm.variables.index(x_i))
111
+ if s_j:
112
+ x_j = binary_to_discrete(*s_j)
113
+ xj_idx: int = cast(int, dqm.variables.index(x_j))
114
+ dqm.set_quadratic(
115
+ dqm.variables[xi_idx],
116
+ dqm.variables[xj_idx],
117
+ {
118
+ (case, case): bias
119
+ for case in range(problem.cases + BIN_OFFSET)
120
+ },
121
+ )
122
+ else:
123
+ dqm.set_linear(
124
+ dqm.variables[xi_idx],
125
+ [bias for _ in range(problem.cases + BIN_OFFSET)],
126
+ )
127
+
128
+ return dqm
@@ -0,0 +1 @@
1
+ from .cqm import CQM
@@ -0,0 +1,66 @@
1
+ import os
2
+ from dimod import ConstrainedQuadraticModel
3
+ from dwave.system import LeapHybridCQMSampler
4
+
5
+ from typing import Any, Optional
6
+
7
+ from QHyper.solvers.converter import Converter
8
+ from QHyper.problems.base import Problem
9
+ from QHyper.solvers.base import Solver
10
+ from QHyper.optimizers.base import Optimizer
11
+
12
+
13
+ DWAVE_API_TOKEN = os.environ.get('DWAVE_API_TOKEN')
14
+
15
+
16
+ class CQM(Solver):
17
+ """
18
+ Class for solving a problem using the Constrained Quadratic Model (CQM) approach.
19
+
20
+ Attributes
21
+ ----------
22
+ problem : Problem
23
+ The problem to be solved.
24
+ time : float
25
+ The maximum time allowed for the CQM solver.
26
+ """
27
+
28
+ def __init__(self, problem: Problem, time: float) -> None:
29
+ """
30
+ Parameters
31
+ ----------
32
+ problem : Problem
33
+ The problem to be solved.
34
+ time : float
35
+ The maximum time allowed for the CQM solver.
36
+ """
37
+ self.problem: Problem = problem
38
+ self.time: float = time
39
+
40
+ def solve(self, params_inits: dict[str, Any] = {}) -> Any:
41
+ """
42
+ Solve the problem using the CQM approach.
43
+
44
+ Parameters
45
+ ----------
46
+ params_inits : dict[str, Any], optional
47
+ Initial parameters for the optimization. Default is None.
48
+ hyper_optimizer : Optimizer, optional
49
+ Hyperparameter optimizer. Default is None.
50
+
51
+ Returns
52
+ -------
53
+ Any
54
+ The solution to the problem.
55
+ """
56
+
57
+ converter = Converter()
58
+ cqm = converter.to_cqm(self.problem)
59
+ sampler = LeapHybridCQMSampler(token=DWAVE_API_TOKEN)
60
+ solutions = sampler.sample_cqm(cqm, self.time)
61
+ correct_solutions = [
62
+ s for s in solutions
63
+ if len(cqm.violations(s, skip_satisfied=True)) == 0
64
+ ]
65
+
66
+ return correct_solutions[0]
@@ -0,0 +1 @@
1
+ from .dqm import DQM
@@ -0,0 +1,23 @@
1
+ import os
2
+ from typing import Any
3
+
4
+ from dwave.system import LeapHybridDQMSampler
5
+ from QHyper.problems.base import Problem
6
+ from QHyper.solvers.base import Solver
7
+ from QHyper.solvers.converter import Converter
8
+
9
+ DWAVE_API_TOKEN = os.environ.get("DWAVE_API_TOKEN")
10
+
11
+
12
+ class DQM(Solver):
13
+ def __init__(self, problem: Problem, time: float) -> None:
14
+ self.problem: Problem = problem
15
+ self.time: float = time
16
+
17
+ def solve(self, params_inits: dict[str, Any] = {}) -> Any:
18
+ converter = Converter()
19
+ dqm = converter.to_dqm(self.problem)
20
+ sampler = LeapHybridDQMSampler(token=DWAVE_API_TOKEN)
21
+ sampleset = sampler.sample_dqm(dqm, self.time)
22
+
23
+ return sampleset
@@ -0,0 +1 @@
1
+ from .gurobi import Gurobi
@@ -0,0 +1,77 @@
1
+ from typing import Any
2
+
3
+ import gurobipy as gp
4
+ from QHyper.problems.base import Problem
5
+ from QHyper.solvers.base import Solver
6
+ from QHyper.solvers.converter import QUBO
7
+
8
+
9
+ def calc(vars: dict[str, Any], poly_dict: QUBO) -> Any:
10
+ cost_function: float = 0
11
+ for key, value in poly_dict.items():
12
+ tmp = 1
13
+ for k in key:
14
+ tmp *= vars[k]
15
+ cost_function += tmp * value
16
+ return cost_function
17
+
18
+
19
+ class Gurobi(Solver): # todo works only for quadratic expressions
20
+ def __init__(self, problem: Problem) -> None:
21
+ self.problem: Problem = problem
22
+
23
+ def solve(self, params_inits: dict[str, Any]) -> Any:
24
+ name = params_inits.get("name", "name")
25
+ gpm = gp.Model(name)
26
+ if "MIPGap" in params_inits:
27
+ gpm.Params.MIPGap = params_inits["MIPGap"]
28
+
29
+ vars = {
30
+ str(var_name): gpm.addVar(vtype=gp.GRB.BINARY, name=str(var_name))
31
+ for var_name in self.problem.variables
32
+ }
33
+ # gpm.update()
34
+
35
+ objective_function = calc(
36
+ vars, self.problem.objective_function.as_dict()
37
+ )
38
+ gpm.setObjective(objective_function, gp.GRB.MINIMIZE)
39
+
40
+ for i, constraint in enumerate(self.problem.constraints):
41
+ tmp_constraint = calc(vars, constraint.as_dict())
42
+ gpm.addConstr(tmp_constraint == 0, f"constr_{i}")
43
+ gpm.update()
44
+
45
+ # eq_constraints = self.problem.constraints["=="]
46
+ # for i, constraint in enumerate(eq_constraints):
47
+ # tmp_constraint = calc(vars, constraint.as_dict())
48
+ # gpm.addConstr(tmp_constraint == 0, f"eq_constr_{i}")
49
+ #
50
+ # ltq_constraints = self.problem.constraints["<="]
51
+ # for i, constraint in enumerate(ltq_constraints):
52
+ # tmp_constraint = calc(vars, constraint.as_dict())
53
+ # gpm.addConstr(tmp_constraint <= 0, f"leq_constr_{i}")
54
+ #
55
+ # gtq_constraints = self.problem.constraints["<="]
56
+ # for i, constraint in enumerate(gtq_constraints):
57
+ # tmp_constraint = calc(vars, constraint.as_dict())
58
+ # gpm.addConstr(tmp_constraint >= 0, f"gtq_constr_{i}")
59
+
60
+ gpm.optimize()
61
+
62
+ # print(dir(gpm))
63
+ # print("status ", gpm.Status)
64
+ # print("is optimal? ", GRB.OPTIMAL)
65
+ #
66
+ # allvars = gpm.getVars()
67
+ # for v in allvars[-1:]:
68
+ # print(v.VarName)
69
+ # print(v.X)
70
+ # print(dir(v))
71
+
72
+ allvars = gpm.getVars()
73
+ solution = {}
74
+ for v in allvars:
75
+ solution[v.VarName] = v.X
76
+
77
+ return solution
@@ -0,0 +1,25 @@
1
+ from typing import Type
2
+
3
+ # === PQC ===
4
+
5
+ from .pqc.base import PQC
6
+ from .pqc.h_qaoa import HQAOA
7
+ from .pqc.qaoa import QAOA
8
+ from .pqc.wf_qaoa import WFQAOA
9
+
10
+ PQC_BY_NAME: dict[str, Type[PQC]] = {
11
+ 'hqaoa': HQAOA,
12
+ 'qaoa': QAOA,
13
+ 'wfqaoa': WFQAOA
14
+ }
15
+
16
+ # # === Evaluation functions ===
17
+
18
+ # from .eval_funcs.base import EvalFunc
19
+ # from .eval_funcs.expval import ExpVal
20
+ # from .eval_funcs.wfeval import WFEval
21
+
22
+ # EVAL_FUNCS_BY_NAME: dict[str, Type[EvalFunc]] = {
23
+ # 'expval': ExpVal,
24
+ # 'wfeval': WFEval,
25
+ # }