def test_construction(self): cqm = CQM() cqm.set_objective(dimod.AdjVectorBQM({'ab': 1}, 'SPIN')) label = cqm.add_constraint(dimod.AdjVectorBQM({'ab': 1}, 'SPIN'), sense='==', rhs=1) self.assertIsInstance(cqm.objective, BQM) self.assertIsInstance(cqm.constraints[label].lhs, BQM)
def test_empty(self): bqm = dimod.AdjVectorBQM('BINARY') fixed = roof_duality(bqm, strict=True) self.assertEqual(fixed, {}) fixed = roof_duality(bqm, strict=False) self.assertEqual(fixed, {})
def bqm_as_file(bqm, **options): """Encode in-memory BQM as DIMODBQM binary file format. Args: bqm (:class:`~dimod.BQM`): Binary quadratic model. **options (dict): :class:`~dimod.serialization.fileview.FileView` options. Returns: file-like: Binary stream with BQM encoded in DIMODBQM format. """ # This now the preferred way of BQM binary serialization. # XXX: temporary implemented here, until something like # dwavesystems/dimod#599 is available. try: import dimod from dimod.serialization.fileview import FileView as BQMFileView except ImportError: # pragma: no cover raise RuntimeError("Can't encode BQM without 'dimod'. " "Re-install the library with 'bqm' support.") if isinstance(bqm, BQMFileView): return bqm # test explicitly to avoid copy on cast if possible fileviewable = (dimod.AdjArrayBQM, dimod.AdjVectorBQM, dimod.AdjMapBQM) if not isinstance(bqm, fileviewable): bqm = dimod.AdjVectorBQM(bqm) return BQMFileView(bqm, **options)
def test_ferromagnet_chimera(self): # submit a maximum ferromagnet bqm = dimod.AdjVectorBQM('SPIN') for u, v in itertools.combinations(chimera_sampler.largest_clique(), 2): bqm.quadratic[u, v] = -1 chimera_sampler.sample(bqm).resolve()
def test_empty(self): bqm = dimod.AdjVectorBQM('SPIN', _ignore_warning=True) with tempfile.TemporaryFile() as tf: with bqm.to_file() as bqmf: shutil.copyfileobj(bqmf, tf) tf.seek(0) new = dimod.AdjVectorBQM.from_file(tf) self.assertEqual(bqm, new)
def test_construction(self): cqm = CQM() with self.assertWarns(DeprecationWarning): bqm = dimod.AdjVectorBQM({'ab': 1}, 'SPIN') cqm.set_objective(bqm) label = cqm.add_constraint(bqm, sense='==', rhs=1) self.assertIsInstance(cqm.objective, dimod.QuadraticModel) self.assertIsInstance(cqm.constraints[label].lhs, BQM)
def bqm_as_file(bqm, **options): """Encode in-memory BQM as DIMODBQM binary file format. Args: bqm (:class:`~dimod.BQM`): Binary quadratic model. **options (dict): :class:`~dimod.serialization.fileview.FileView` options. Returns: file-like: Binary stream with BQM encoded in DIMODBQM format. """ # TODO: replace with `bqm.to_file` when we drop support for dimod 0.9.x try: import dimod except ImportError: # pragma: no cover raise RuntimeError("Can't encode BQM without 'dimod'. " "Re-install the library with 'bqm' support.") try: # Using `bqm.to_file()` for serialization is preferred since # dwavesystems/dimod#599, but we need a fallback for older dimods. # Specifically: # - `Adj{Vector,Array,Map}BQM.to_file` was added in dimod 0.9.6. # - `BQM.to_file` method was added in dimod 0.10.0. bqm.to_file except AttributeError: # pragma: no cover pass else: return bqm.to_file(**options) try: # we need FileView in dimod < 0.9.6 from dimod.serialization.fileview import FileView as BQMFileView except ImportError: # pragma: no cover # this should never happen raise RuntimeError( f"Can't import FileView serializer from dimod=={dimod.__version__}" ) if isinstance(bqm, BQMFileView): return bqm # test explicitly to avoid copy on cast if possible # note: it's safe to use Adj*BQMs (removed in dimod 0.10), as we never get # here if dimod 0.10 is used fileviewable = (dimod.AdjArrayBQM, dimod.AdjVectorBQM, dimod.AdjMapBQM) if not isinstance(bqm, fileviewable): bqm = dimod.AdjVectorBQM(bqm) return BQMFileView(bqm, **options)
def test_all_zero(self): num_vars = 3 bqm = dimod.AdjVectorBQM(num_vars, 'BINARY') fixed = roof_duality(bqm, strict=True) self.assertEqual(fixed, {}) fixed = roof_duality(bqm, strict=False) self.assertEqual(len(fixed), num_vars) for val in fixed.values(): self.assertEqual(val, 1)
def knapsack_bqm(costs, weights, weight_capacity): costs = costs # Initialize BQM - use large-capacity BQM so that the problem can be # scaled by the user. bqm = dimod.AdjVectorBQM(dimod.Vartype.BINARY) # Lagrangian multiplier # First guess as suggested in Lucas's paper lagrange = max(costs) # Number of objects x_size = len(costs) # Lucas's algorithm introduces additional slack variables to handle # the inequality. max_y_index indicates the maximum index in the y # sum; hence the number of slack variables. max_y_index = ceil(log(weight_capacity)) # Slack variable list for Lucas's algorithm. The last variable has # a special value because it terminates the sequence. y = [2**n for n in range(max_y_index - 1)] y.append(weight_capacity + 1 - 2**(max_y_index - 1)) # Hamiltonian xi-xi terms for k in range(x_size): bqm.set_linear('x' + str(k), lagrange * (weights[k]**2) - costs[k]) # Hamiltonian xi-xj terms for i in range(x_size): for j in range(i + 1, x_size): key = ('x' + str(i), 'x' + str(j)) bqm.quadratic[key] = 2 * lagrange * weights[i] * weights[j] # Hamiltonian y-y terms for k in range(max_y_index): bqm.set_linear('y' + str(k), lagrange * (y[k]**2)) # Hamiltonian yi-yj terms for i in range(max_y_index): for j in range(i + 1, max_y_index): key = ('y' + str(i), 'y' + str(j)) bqm.quadratic[key] = 2 * lagrange * y[i] * y[j] # Hamiltonian x-y terms for i in range(x_size): for j in range(max_y_index): key = ('x' + str(i), 'y' + str(j)) bqm.quadratic[key] = -2 * lagrange * weights[i] * y[j] return bqm
def build_bqm(potential_new_cs_nodes, num_poi, pois, num_cs, charging_stations, num_new_cs): """ Build bqm that models our problem scenario for the hybrid sampler. """ # Tunable parameters gamma1 = len(potential_new_cs_nodes) * 4 gamma2 = len(potential_new_cs_nodes) / 3 gamma3 = len(potential_new_cs_nodes) * 1.7 gamma4 = len(potential_new_cs_nodes) ** 3 # Build BQM using adjVectors to find best new charging location s.t. min # distance to POIs and max distance to existing charging locations bqm = dimod.AdjVectorBQM(len(potential_new_cs_nodes), 'BINARY') # Constraint 1: Min average distance to POIs if num_poi > 0: for i in range(len(potential_new_cs_nodes)): # Compute average distance to POIs from this node avg_dist = 0 cand_loc = potential_new_cs_nodes[i] for loc in pois: dist = (cand_loc[0]**2 - 2*cand_loc[0]*loc[0] + loc[0]**2 + cand_loc[1]**2 - 2*cand_loc[1]*loc[1] + loc[1]**2) avg_dist += dist / num_poi bqm.linear[i] += avg_dist * gamma1 # Constraint 2: Max distance to existing chargers if num_cs > 0: for i in range(len(potential_new_cs_nodes)): # Compute average distance to POIs from this node avg_dist = 0 cand_loc = potential_new_cs_nodes[i] for loc in charging_stations: dist = (-1*cand_loc[0]**2 + 2*cand_loc[0]*loc[0] - loc[0]**2 - cand_loc[1]**2 + 2*cand_loc[1]*loc[1] - loc[1]**2) avg_dist += dist / num_cs bqm.linear[i] += avg_dist * gamma2 # Constraint 3: Max distance to other new charging locations if num_new_cs > 1: for i in range(len(potential_new_cs_nodes)): for j in range(i+1, len(potential_new_cs_nodes)): ai = potential_new_cs_nodes[i] aj = potential_new_cs_nodes[j] dist = (-1*ai[0]**2 + 2*ai[0]*aj[0] - aj[0]**2 - ai[1]**2 + 2*ai[1]*aj[1] - aj[1]**2) bqm.add_interaction(i, j, dist * gamma3) # Constraint 4: Choose exactly num_new_cs new charging locations bqm.update(dimod.generators.combinations(bqm.variables, num_new_cs, strength=gamma4)) return bqm
def knapsack_bqm(costs, weights, weight_capacity): costs = costs # Initialize BQM - use large-capacity BQM so that the problem can be # scaled by the user. bqm = dimod.AdjVectorBQM(dimod.Vartype.BINARY) # Lagrangian multiplier (A in Lucas's paper) # Note: 0 < B*max(c_a) < A # 0.2 <= B <= 0.5 seems like a good range lagrange = max(costs) B = 0.5 # Number of objects x_size = len(costs) # Lucas's algorithm introduces additional slack variables to handle # the inequality. In Lucas' paper y = [1, ..., W_max] y = [n for n in range(1, weight_capacity + 1)] max_y_index = len(y) # Hamiltonian xi-xi terms for k in range(x_size): bqm.set_linear('x' + str(k), (lagrange * weights[k]**2) - (B * costs[k])) # Hamiltonian xi-xj terms for i in range(x_size): for j in range(i + 1, x_size): key = ('x' + str(i), 'x' + str(j)) bqm.quadratic[key] = 2 * lagrange * weights[i] * weights[j] # Hamiltonian y-y terms for k in range(max_y_index): bqm.set_linear('y' + str(k), lagrange * (y[k]**2 - 1)) # Hamiltonian yi-yj terms for i in range(max_y_index): for j in range(i + 1, max_y_index): key = ('y' + str(i), 'y' + str(j)) bqm.quadratic[key] = 2 * lagrange * (y[i] * y[j] + 1) # Hamiltonian x-y terms for i in range(x_size): for j in range(max_y_index): key = ('x' + str(i), 'y' + str(j)) bqm.quadratic[key] = -2 * lagrange * weights[i] * y[j] return bqm
def test_pegasus(self): try: sampler = DWaveCliqueSampler(solver=dict(topology__type='pegasus')) except (ValueError, ConfigFileError, SolverNotFoundError): raise unittest.SkipTest("no Pegasus-structured QPU available") dimod.testing.assert_sampler_api(sampler) # submit a maximum ferromagnet bqm = dimod.AdjVectorBQM('SPIN') for u, v in itertools.combinations(sampler.largest_clique(), 2): bqm.quadratic[u, v] = -1 sampler.sample(bqm).resolve()
def createBQM(incidencias, controles): idIncidencias = incidencias.keys() idControles = controles.keys() Q = dimod.AdjVectorBQM(dimod.Vartype.BINARY) J = {} tiempos = [] for i in idIncidencias: tiempos.append(incidencias[i]['tiempo']) penalizacion = max(tiempos) + 1 print(tiempos) for i, j in zip(idIncidencias, range(len(idIncidencias))): Q.set_linear(str(i), tiempos[j]) J[(i, i)] = tiempos[j] for k in idControles: cIncidencias = controles[k] for i in cIncidencias: key = (str(i)) Q.linear[key] -= penalizacion J[(i, i)] -= int(penalizacion) for i in range(len(idIncidencias)): for j in range(i + 1, len(idIncidencias)): incidencia1 = list(idIncidencias)[i] incidencia2 = list(idIncidencias)[j] key = (str(incidencia1), str(incidencia2)) Q.quadratic[key] = 0 J[(incidencia1, incidencia2)] = 0 for k in idControles: cIncidencias = controles[k] for i in range(len(cIncidencias)): for j in range(i + 1, len(cIncidencias)): incidencia1 = cIncidencias[i] incidencia2 = cIncidencias[j] key = (str(incidencia1), str(incidencia2)) Q.quadratic[key] += 2 * penalizacion J[(incidencia1, incidencia2)] += 2 * penalizacion return Q, J
def test_saved_adjvector_5x5_v1(self): bqm = dimod.AdjVectorBQM(np.triu(np.arange(25).reshape((5, 5))), 'BINARY') filename = os.path.join(TEST_DATA_DIR, '5x5_v1.bqm') # with open(filename, 'wb') as fp: # with FileView(bqm, version=1) as fv: # fp.write(fv.readall()) with open(filename, 'rb') as fp: buff = fp.read() # test that we still encode the same way with FileView(bqm, version=1) as fv: self.assertEqual(buff, fv.read()) # and that loading gives the same new = load(buff) self.assertEqual(new, bqm)
def build_knapsack_bqm(costs, weights, weight_capacity): """Construct BQM for the knapsack problem Args: costs (array-like): Array of costs associated with the items weights (array-like): Array of weights associated with the items weight_capacity (int): Maximum allowable weight Returns: Binary quadratic model instance """ # Initialize BQM - use large-capacity BQM so that the problem can be # scaled by the user. bqm = dimod.AdjVectorBQM(dimod.Vartype.BINARY) # Lagrangian multiplier # First guess as suggested in Lucas's paper lagrange = max(costs) # Number of objects x_size = len(costs) # Lucas's algorithm introduces additional slack variables to # handle the inequality. M+1 binary slack variables are needed to # represent the sum using a set of powers of 2. M = floor(log2(weight_capacity)) num_slack_variables = M + 1 # Slack variable list for Lucas's algorithm. The last variable has # a special value because it terminates the sequence. y = [2**n for n in range(M)] y.append(weight_capacity + 1 - 2**M) # Hamiltonian xi-xi terms for k in range(x_size): bqm.set_linear('x' + str(k), lagrange * (weights[k]**2) - costs[k]) # Hamiltonian xi-xj terms for i in range(x_size): for j in range(i + 1, x_size): key = ('x' + str(i), 'x' + str(j)) bqm.quadratic[key] = 2 * lagrange * weights[i] * weights[j] # Hamiltonian y-y terms for k in range(num_slack_variables): bqm.set_linear('y' + str(k), lagrange * (y[k]**2)) # Hamiltonian yi-yj terms for i in range(num_slack_variables): for j in range(i + 1, num_slack_variables): key = ('y' + str(i), 'y' + str(j)) bqm.quadratic[key] = 2 * lagrange * y[i] * y[j] # Hamiltonian x-y terms for i in range(x_size): for j in range(num_slack_variables): key = ('x' + str(i), 'y' + str(j)) bqm.quadratic[key] = -2 * lagrange * weights[i] * y[j] return bqm
def construct_bqm(demand, nfreq, reuse_distances, penalty_coef=1.0): """Construct BQM for feasibility frequency assignment problem. Args: demand (dict): Dictionary mapping each node number to a demand value nfreq (int): Number of frequencies to consider reuse_distances (list): List of reuse distances penalty_coef (float): Penalty coefficient associated with constraint penalty function. Not needed in current formulation, which does not include an objective component of the problem formulation. Retained only as a placeholder in case the problem is extended to include an objective. Returns: AdjVectorBQM """ # Variables: # x_vf, v in nodes, f in frequencies: Is f assigned to node v? nodes = sorted(list(demand.keys())) bqm = dimod.AdjVectorBQM(dimod.BINARY) # Constraints to enforce demand at each node: # Sum_f[ (1-2C) xvf ] + Sum_j>i[ 2 xvfi xvfj ] + C^2 # Linear parts: for v in nodes: for f in range(nfreq): bqm.add_variable('x_{}_{}'.format(v, f), penalty_coef * (1.0 - 2 * demand[v])) # Interactions: for v in nodes: for fi in range(nfreq): for fj in range(fi + 1, nfreq): bqm.add_interaction('x_{}_{}'.format(v, fi), 'x_{}_{}'.format(v, fj), penalty_coef * 2.0) # Define penalties associated with the interference constraints. # The interference constraints are represented by the inequality # xvf + xwg <= 1 for all combinations that would produce # interference. # First enforce the self-conflicts between frequencies in the same node: T = get_forbidden_set(1, 1, reuse_distances) for v in nodes: for f in range(nfreq): for g in range(f + 1, nfreq): if abs(f - g) in T: bqm.add_interaction('x_{}_{}'.format(v, f), 'x_{}_{}'.format(v, g), penalty_coef) # Now enforce the cross-node conflicts: for iv, v in enumerate(nodes): for w in nodes[iv + 1:]: T = get_forbidden_set(v, w, reuse_distances) if not T: # No disallowed frequencies at this distance continue for f in range(nfreq): # Note f and g are frequencies on different nodes, so we do need to look at all combinations for g in range(nfreq): if abs(f - g) in T: bqm.add_interaction('x_{}_{}'.format(v, f), 'x_{}_{}'.format(w, g), penalty_coef) return bqm
charging_stations = random.sample(nodes, k=num_cs) # Number of new charging stations to place num_new_cs = 2 poi_cs_list = set(pois) - (set(pois) - set(charging_stations)) poi_cs_graph = G.subgraph(poi_cs_list) poi_graph = G.subgraph(pois) cs_graph = G.subgraph(charging_stations) # Identify potential new charging locations potential_new_cs_nodes = list(nodes - charging_stations) # Build BQM using adjVectors to find best new charging location s.t. min # distance to POIs and max distance to existing charging locations bqm = dimod.AdjVectorBQM(len(potential_new_cs_nodes), 'BINARY') # Constraint 1: Min average distance to POIs for i in range(len(potential_new_cs_nodes)): # Compute average distance to POIs from this node ave_dist = 0 cand_loc = potential_new_cs_nodes[i] for loc in pois: dist = (cand_loc[0]**2 - 2 * cand_loc[0] * loc[0] + loc[0]**2 + cand_loc[1]**2 - 2 * cand_loc[1] * loc[1] + loc[1]**2) ave_dist += dist / num_cs bqm.linear[i] = ave_dist * gamma1 # Constraint 2: Max distance to existing chargers for i in range(len(potential_new_cs_nodes)): # Compute average distance to POIs from this node
def knapsack_bqm(cities, values, weights, total_capacity, value_r=0, weight_r=0): """ build the knapsack binary quadratic model From DWave Knapsack examples Originally from Andrew Lucas, NP-hard combinatorial problems as Ising spin glasses Workshop on Classical and Quantum Optimization; ETH Zuerich - August 20, 2014 based on Lucas, Frontiers in Physics _2, 5 (2014) See # Q-Alpha version for original introduction of value_r and weight_r value_r: the proportion of value contributed from the objects outside of the knapsack. For the standard knapsack problem this is 0, but in the case of GDP a closed city retains some % of GDP value; or for health problems it may contribute negative value (-1). weight_r: the proportion of weight contributed from the objects outside of the knapsack. For the standard knapsack problem this is 0, but in the case of sick people we might consider that a closed city retains some % of its sick people over time; or for health problems it may contribute negative value (-1) """ # Initialize BQM - use large-capacity BQM so that the problem can be # scaled by the user. bqm = dimod.AdjVectorBQM(dimod.Vartype.BINARY) # Lagrangian multiplier # First guess as suggested in Lucas's paper lagrange = max(values) # Number of objects x_size = len(values) # Lucas's algorithm introduces additional slack variables to handle # the inequality. max_y_index indicates the maximum index in the y # sum; hence the number of slack variables. max_y_index = ceil(log(total_capacity)) # Slack variable list for Lucas's algorithm. The last variable has # a special value because it terminates the sequence. y = [2**n for n in range(max_y_index - 1)] y.append(total_capacity + 1 - 2**(max_y_index - 1)) # Q-Alpha - calculate the extra constant in second part of problem hamiltonian C = sum([weight * weight_r for weight in weights]) # Q-Alpha - change weights to weight*(1-weight_r) weights = [weight*(1-weight_r) for weight in weights] # Q-Alpha - change values to value*(1-value_r) values = [value*(1-value_r) for value in values] # Hamiltonian xi-xi terms for k in range(x_size): # Q-Alpha add final term lagrange * C * weights[k] bqm.set_linear( cities[k], lagrange * (weights[k] ** 2) - values[k] + lagrange * C * weights[k]) # Hamiltonian xi-xj terms for i in range(x_size): for j in range(i + 1, x_size): key = (cities[i], cities[j]) bqm.quadratic[key] = 2 * lagrange * weights[i] * weights[j] # Hamiltonian y-y terms for k in range(max_y_index): # Q-Alpha add final term -lagrange * C * y[k] bqm.set_linear('y' + str(k), lagrange * (y[k]**2) - lagrange * C * y[k]) # Hamiltonian yi-yj terms for i in range(max_y_index): for j in range(i + 1, max_y_index): key = ('y' + str(i), 'y' + str(j)) bqm.quadratic[key] = 2 * lagrange * y[i] * y[j] # Hamiltonian x-y terms for i in range(x_size): for j in range(max_y_index): key = (cities[i], 'y' + str(j)) bqm.quadratic[key] = -2 * lagrange * weights[i] * y[j] return bqm
# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import dimod import numpy as np from parameterized import parameterized BQMs = dict( bqm_k150=dimod.BinaryQuadraticModel(np.ones((150, 150)), 'BINARY'), adj_k150=dimod.AdjVectorBQM(np.ones((150, 150)), 'BINARY'), bqm_k500=dimod.BinaryQuadraticModel(np.ones((500, 500)), 'BINARY'), adj_k500=dimod.AdjVectorBQM(np.ones((500, 500)), 'BINARY'), ) class BQMSuite: @parameterized.expand(BQMs.items()) def time_iter_quadratic(self, _, bqm): for _ in bqm.iter_quadratic(): pass @parameterized.expand((name, bqm, np.ones((1000, bqm.num_variables))) for name, bqm in BQMs.items()) def time_energies(self, _, bqm, samples): bqm.energies(samples)
from dwave.system import LeapHybridSampler import dimod import sys # Graph partitioning on clique N = int(sys.argv[1]) gamma = 3 linear = (N - 1) * (1 - gamma) quad = (2 * gamma) - 2 bqm = dimod.AdjVectorBQM(dimod.Vartype.BINARY) bqm.offset = gamma * N**2 / 4 for i in range(N): bqm.set_linear(i, linear) for j in range(i + 1, N): bqm.quadratic[i, j] = quad sampler = LeapHybridSampler(profile='hss') response = sampler.sample(bqm) print(response.info) for sample, energy in response.data(['sample', 'energy']): print(sample, energy)
def createBQM(incidencias, controles): idIncidencias = incidencias.keys( ) #Se extraen las ids de las incidencias para mejor gestión. idControles = controles.keys( ) #Se extraen las ids de los controles para mejor gestión. Q = dimod.AdjVectorBQM( dimod.Vartype.BINARY ) #Este es el formato principal del BQM, una matriz cuadrática. J = {} #Este formato se utiliza para usarlo con el inspector. tiempos = [ ] #Se extraen los tiempos para mejor gestión y para sacar el máximo. for i in idIncidencias: tiempos.append(incidencias[i]['tiempo']) y = {} slack_incidencias = {} for k in idControles: num_incidencias = len(controles[k]) num_slack = ceil(log2(num_incidencias)) slack_incidencias[k] = num_slack for i in range(num_slack): if k in y: y[str(k)].append(2**i) else: y[str(k)] = [2**i] penalizacion = max( tiempos ) + 10 #La penalización es el valor que nos va a ayudar a seleccionar qué incidencias tenemos #que seleccionar. La condición que necesitamos que se cumpla es que todos los controles #sean resueltos. Cualquier solución que no cumpla con esa condición, será descartada #gracias al valor de penalización. Se ha decidido que el valor sea el máximo tiempo #entre todas las incidencias para que pueda variar según sea necesario, y es #incrementado en 1 para que no interfiera con el peso de las decisiones. #Término linear xi (-P*xi). #Este término linear se encarga de relacionar el tiempo que se tarda en gestionar una incidencia. De forma muy simplificada, #esto es lo que determinará qué incidencias son las mejores en caso de que haya varias soluciones válidas. Se recomienda leer #la documentación para una explicación en mayor detalle y cómo se ha llegado a esta operación. for i, j in zip(idIncidencias, range(len(idIncidencias))): Q.set_linear('x' + str(i), tiempos[j]) J[(i, i)] = tiempos[j] #Término linear xi (-P*xi). #Esta parte se utiliza para restar la penalización a cada incidencia que resuelva un control. De forma muy simplificada, si #una incidencia resuelve varios controles, es más probable que sea seleccionada. Se recomienda leer la documentación para #una explicación en mayor detalle y cómo se ha llegado a esta operación. for k in idControles: cIncidencias = controles[ k] #Se extraen todas las incidencias de las que forma parte cada control, de forma que se #pueda usar para acceder al BQM. for i in cIncidencias: key = ('x' + str(i)) Q.linear[key] -= penalizacion J[(i, i)] -= penalizacion #Término linear y-y (sk² + 2Psk) for k in idControles: if k in y: slacks = y[k] if type(slacks) is int: Q.set_linear('y' + str(k) + '-' + str(slacks), (slacks**2 + 2) * penalizacion) else: for i in slacks: Q.set_linear('y' + str(k) + '-' + str(i), (i**2 + 2) * penalizacion) #Término cuadrático xi-xj (2P*xi*xj). #Esta parte sirve simplemente para inicializar los términos cuadráticos en el BQM. for k in idControles: cIncidencias = controles[k] for i in range(len(cIncidencias)): for j in range(i + 1, len(cIncidencias)): incidencia1 = cIncidencias[i] incidencia2 = cIncidencias[j] key = ('x' + str(incidencia1), 'x' + str(incidencia2)) Q.quadratic[key] = 0 J[(incidencia1, incidencia2)] = 0 #Término cuadrático xi-xj (2P*xi*xj). #El término cuadrático se encarga de evitar redundancias entre los controles. De forma muy simplificada, si un control es #resuelto por dos incidencias, puede ser que una de esas incidencias sea innecesaria. Se recomienda leer la documentación #para una explicación en mayor detalle y cómo se ha llegado a esta operación. for k in idControles: cIncidencias = controles[k] for i in range(len(cIncidencias)): for j in range(i + 1, len(cIncidencias)): incidencia1 = cIncidencias[i] incidencia2 = cIncidencias[j] key = ('x' + str(incidencia1), 'x' + str(incidencia2)) Q.quadratic[key] += 2 * penalizacion J[(incidencia1, incidencia2)] += 2 * penalizacion #Término cuadrático yi-yj for k in idControles: if k in y: slacks = y[k] if type(slacks) is not int: for i in range(len(slacks)): for j in range(i + 1, len(slacks)): slack1 = slacks[i] slack2 = slacks[j] key = ('y' + str(k) + '-' + str(i), 'y' + str(k) + '-' + str(j)) Q.quadratic[key] = 2 * penalizacion * slack1 * slack2 #Término cuadrático y-x for k in idControles: if k in y: cIncidencias = controles[k] slacks = y[k] if type(slacks) is int: for i in range(len(cIncidencias)): incidencia1 = cIncidencias[i] key = ('x' + str(incidencia1), 'y' + str(k) + '-' + str(slacks)) Q.quadratic[key] = -2 * penalizacion * slacks else: for i in range(len(cIncidencias)): for j in range(len(slacks)): incidencia1 = cIncidencias[i] slack1 = slacks[j] key = ('x' + str(incidencia1), 'y' + str(k) + '-' + str(j)) Q.quadratic[key] = 2 * penalizacion * slack1 print(Q) return Q, J