eqc-models 0.11.0__py3-none-any.whl → 0.12.0__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.
- eqc_models-0.12.0.data/platlib/eqc_models/assignment/__init__.py +6 -0
- eqc_models-0.12.0.data/platlib/eqc_models/assignment/resource.py +165 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/polyeval.c +4208 -3435
- eqc_models-0.12.0.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/quadratic.py +2 -2
- eqc_models-0.12.0.data/platlib/eqc_models/base/results.py +166 -0
- eqc_models-0.12.0.data/platlib/eqc_models/graph/__init__.py +9 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/graph/base.py +8 -4
- eqc_models-0.12.0.data/platlib/eqc_models/graph/shortestpath.py +157 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/classifierbase.py +31 -5
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/classifierqboost.py +14 -1
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/classifierqsvm.py +223 -19
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/clustering.py +5 -5
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/clusteringbase.py +1 -1
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/decomposition.py +39 -16
- eqc_models-0.12.0.data/platlib/eqc_models/process/base.py +18 -0
- eqc_models-0.12.0.data/platlib/eqc_models/process/mpc.py +17 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/solvers/__init__.py +1 -5
- eqc_models-0.12.0.data/platlib/eqc_models/solvers/eqcdirect.py +71 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/solvers/qciclient.py +6 -3
- {eqc_models-0.11.0.dist-info → eqc_models-0.12.0.dist-info}/METADATA +2 -3
- eqc_models-0.12.0.dist-info/RECORD +65 -0
- {eqc_models-0.11.0.dist-info → eqc_models-0.12.0.dist-info}/WHEEL +1 -1
- eqc_models-0.11.0.data/platlib/eqc_models/assignment/__init__.py +0 -5
- eqc_models-0.11.0.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
- eqc_models-0.11.0.data/platlib/eqc_models/base/results.py +0 -94
- eqc_models-0.11.0.data/platlib/eqc_models/graph/__init__.py +0 -6
- eqc_models-0.11.0.data/platlib/eqc_models/sequence/scheduling.py +0 -29
- eqc_models-0.11.0.dist-info/RECORD +0 -61
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/compile_extensions.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/algorithms/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/algorithms/base.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/algorithms/penaltymultiplier.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/allocation/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/allocation/allocation.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/allocation/portbase.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/allocation/portmomentum.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/assignment/qap.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/assignment/setpartition.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/base.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/constraints.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/operators.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/polyeval.pyx +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/base/polynomial.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/combinatorics/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/combinatorics/setcover.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/combinatorics/setpartition.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/decoding.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/graph/hypergraph.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/graph/maxcut.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/graph/maxkcut.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/graph/partition.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian.pyx +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian_c_func.c +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/cvqboost_hamiltonian_c_func.h +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/forecast.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/forecastbase.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/regressor.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/regressorbase.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/ml/reservoir.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/sequence/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/sequence/tsp.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/utilities/__init__.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/utilities/fileio.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/utilities/polynomial.py +0 -0
- {eqc_models-0.11.0.data → eqc_models-0.12.0.data}/platlib/eqc_models/utilities/qplib.py +0 -0
- {eqc_models-0.11.0.dist-info → eqc_models-0.12.0.dist-info}/licenses/LICENSE.txt +0 -0
- {eqc_models-0.11.0.dist-info → eqc_models-0.12.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from typing import (Dict, List)
|
|
2
|
+
import numpy as np
|
|
3
|
+
from eqc_models.base.quadratic import ConstrainedQuadraticModel
|
|
4
|
+
from eqc_models.base.constraints import InequalitiesMixin
|
|
5
|
+
|
|
6
|
+
class ResourceAssignmentModel(InequalitiesMixin, ConstrainedQuadraticModel):
|
|
7
|
+
"""
|
|
8
|
+
Resource assignment model
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
------------
|
|
12
|
+
|
|
13
|
+
resources : List
|
|
14
|
+
tasks : List
|
|
15
|
+
|
|
16
|
+
>>> # name is not a required attribute of the resources or tasks
|
|
17
|
+
>>> crews = [{"name": "Maintenance Crew 1", "skills": ["A", "F"], "capacity": 5, "cost": 4},
|
|
18
|
+
... {"name": "Baggage Crew 1", "skills": ["B"], "capacity": 4, "cost": 1},
|
|
19
|
+
... {"name": "Maintenance Crew 2", "skills": ["A", "F"], "capacity": 5, "cost": 2}]
|
|
20
|
+
>>> tasks = [{"name": "Refuel", "skill_need": "F", "load": 3},
|
|
21
|
+
... {"name": "Baggage", "skill_need": "B", "load": 1}]
|
|
22
|
+
>>> model = ResourceAssignmentModel(crews, tasks)
|
|
23
|
+
>>> assignments = model.createAssignmentVars()
|
|
24
|
+
>>> assignments
|
|
25
|
+
[{'resource': 0, 'task': 0}, {'resource': 1, 'task': 1}, {'resource': 2, 'task': 0}]
|
|
26
|
+
>>> A, b, senses = model.constrainAssignments(assignments)
|
|
27
|
+
>>> A
|
|
28
|
+
array([[3., 0., 0.],
|
|
29
|
+
[0., 1., 0.],
|
|
30
|
+
[0., 0., 3.],
|
|
31
|
+
[3., 0., 3.],
|
|
32
|
+
[0., 3., 0.]], dtype=float32)
|
|
33
|
+
>>> b
|
|
34
|
+
array([5., 4., 5., 3., 3.], dtype=float32)
|
|
35
|
+
>>> senses
|
|
36
|
+
['LE', 'LE', 'LE', 'EQ', 'EQ']
|
|
37
|
+
>>> A, b = model.constraints
|
|
38
|
+
>>> A
|
|
39
|
+
array([[3., 0., 0., 1., 0., 0.],
|
|
40
|
+
[0., 1., 0., 0., 1., 0.],
|
|
41
|
+
[0., 0., 3., 0., 0., 1.],
|
|
42
|
+
[3., 0., 3., 0., 0., 0.],
|
|
43
|
+
[0., 3., 0., 0., 0., 0.]])
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, resources, tasks):
|
|
48
|
+
self.resources = resources
|
|
49
|
+
self.checkTasks(tasks)
|
|
50
|
+
self.tasks = tasks
|
|
51
|
+
self.assignments = assignments = self.createAssignmentVars()
|
|
52
|
+
n = len(assignments) + len(resources)
|
|
53
|
+
self.variables = [f"a{i}" for i in range(len(assignments))]
|
|
54
|
+
self.upper_bound = np.ones((n,))
|
|
55
|
+
self.upper_bound[-len(resources):] = [resource["capacity"] for resource in resources]
|
|
56
|
+
A, b, senses = self.constrainAssignments(assignments)
|
|
57
|
+
J = np.zeros((n, n))
|
|
58
|
+
C = np.zeros((n,), dtype=np.float32)
|
|
59
|
+
# objective is to minimize cost of assignments
|
|
60
|
+
for j, assignment in enumerate(assignments):
|
|
61
|
+
C[j] = resources[assignment["resource"]]["cost"] * tasks[assignment["task"]]["load"]
|
|
62
|
+
super(ResourceAssignmentModel, self).__init__(C, J, A, b)
|
|
63
|
+
self.senses = senses
|
|
64
|
+
# always use a machine slack
|
|
65
|
+
self.machine_slacks = 1
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def checkTasks(cls, tasks):
|
|
69
|
+
for task in tasks:
|
|
70
|
+
if "skill_need" not in task:
|
|
71
|
+
raise ValueError("All tasks must have the skill_need attribute")
|
|
72
|
+
if "load" not in task:
|
|
73
|
+
raise ValueError("All tasks must have the load attribute")
|
|
74
|
+
|
|
75
|
+
def createAssignmentVars(self):
|
|
76
|
+
""" Examine all combinatins of possible crew-task assignments """
|
|
77
|
+
|
|
78
|
+
assign_vars = []
|
|
79
|
+
resources = self.resources
|
|
80
|
+
tasks = self.tasks
|
|
81
|
+
for i, resource in enumerate(resources):
|
|
82
|
+
skills = resource["skills"]
|
|
83
|
+
for j, task in enumerate(tasks):
|
|
84
|
+
if task["skill_need"] in skills:
|
|
85
|
+
assign_vars.append({"resource": i, "task": j})
|
|
86
|
+
return assign_vars
|
|
87
|
+
|
|
88
|
+
def constrainAssignments(self, assignments : List) -> List:
|
|
89
|
+
"""
|
|
90
|
+
Examine the assignments to determine the necessary constraints to
|
|
91
|
+
ensure feasibility of solution.
|
|
92
|
+
|
|
93
|
+
"""
|
|
94
|
+
# A is sized using the number of crews and the number of assignment variables plus slacks
|
|
95
|
+
m1 = len(self.resources)
|
|
96
|
+
m2 = len(self.tasks)
|
|
97
|
+
n1 = len(assignments)
|
|
98
|
+
m = m1 + m2
|
|
99
|
+
n = n1
|
|
100
|
+
A = np.zeros((m, n), dtype=np.float32)
|
|
101
|
+
b = np.zeros((m,), dtype=np.float32)
|
|
102
|
+
for i, resource in enumerate(self.resources):
|
|
103
|
+
b[i] = resource["capacity"]
|
|
104
|
+
for k, assignment in enumerate(assignments):
|
|
105
|
+
if assignment["resource"] == i:
|
|
106
|
+
A[i, k] = self.tasks[assignment["task"]]["load"]
|
|
107
|
+
assignment_coeff = np.max(A)
|
|
108
|
+
for i, task in enumerate(self.tasks):
|
|
109
|
+
b[m1+i] = assignment_coeff
|
|
110
|
+
for k, assignment in enumerate(assignments):
|
|
111
|
+
if assignment["task"] == i:
|
|
112
|
+
A[m1+i, k] = assignment_coeff
|
|
113
|
+
senses = ["LE" for resource in self.resources] + ["EQ" for task in self.tasks]
|
|
114
|
+
return A, b, senses
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def sum_constraint(self) -> int:
|
|
118
|
+
""" This value is a suggestion which should be used with a machine slack """
|
|
119
|
+
|
|
120
|
+
sc = 0
|
|
121
|
+
sc += sum([resource["capacity"] for resource in self.resources])
|
|
122
|
+
sc += len(self.tasks)
|
|
123
|
+
return sc
|
|
124
|
+
|
|
125
|
+
def decode(self, solution : np.array) -> List[Dict]:
|
|
126
|
+
"""
|
|
127
|
+
Convert the binary solution into a list of tasks
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
# ensure solution is array
|
|
132
|
+
solution = np.array(solution)
|
|
133
|
+
resource_assignments = [[] for resource in self.resources]
|
|
134
|
+
vals = [val for val in set(solution) if val <= 1.0]
|
|
135
|
+
# check if there are fractional values less than 1
|
|
136
|
+
if solution[~np.logical_or(solution==0, solution>=1)].size>0:
|
|
137
|
+
# iterate over the values and assign tasks by largest value for tasks
|
|
138
|
+
# not assigned already
|
|
139
|
+
remaining_tasks = list(range(len(self.tasks)))
|
|
140
|
+
fltr = self.upper_bound==1
|
|
141
|
+
while len(remaining_tasks) > 0 and solution[fltr].shape[0]>0:
|
|
142
|
+
largest = np.max(solution[fltr])
|
|
143
|
+
indices, = np.where(np.logical_and(fltr, solution == largest))
|
|
144
|
+
for idx in indices:
|
|
145
|
+
assignment = self.assignments[idx]
|
|
146
|
+
if assignment["task"] in remaining_tasks:
|
|
147
|
+
task = self.tasks[assignment["task"]]
|
|
148
|
+
resource_assignments[assignment["resource"]].append(task)
|
|
149
|
+
del remaining_tasks[remaining_tasks.index(assignment["task"])]
|
|
150
|
+
break
|
|
151
|
+
fltr = np.logical_and(fltr, solution < largest)
|
|
152
|
+
else:
|
|
153
|
+
# Use the restriction that a task cannot be assigned more than once
|
|
154
|
+
for j, task in enumerate(self.tasks):
|
|
155
|
+
highest = 0
|
|
156
|
+
best_resource = None
|
|
157
|
+
for a, assignment in zip(solution, self.assignments):
|
|
158
|
+
if assignment["task"] == j:
|
|
159
|
+
if a > highest:
|
|
160
|
+
highest = a
|
|
161
|
+
best_resource = assignment["resource"]
|
|
162
|
+
assert best_resource is not None, f"solution had no positive assignment values for {task}"
|
|
163
|
+
resource_assignments[best_resource].append(task)
|
|
164
|
+
|
|
165
|
+
return resource_assignments
|