def test_retrieval(self): eq = {(-1, -1), (1, 1)} spec = pm.Specification(nx.path_graph(2), (0, 1), eq, vartype=pm.SPIN) widget = pm.get_penalty_model(spec) self.assert_dict_almost_equal(widget.model.linear, {0: 0, 1: 0}) self.assert_dict_almost_equal(widget.model.quadratic, {(0, 1): -1})
def gate_model(gate_type, fault=True): labels, configurations = GATES[gate_type] if fault: configurations = fault_gate(configurations, FAULT_GAP) num_variables = len(next(iter(configurations))) for size in range(num_variables, num_variables + 4): # reasonable margin G = nx.complete_graph(size) nx.relabel_nodes(G, dict(enumerate(labels)), copy=False) spec = pm.Specification(G, labels, configurations, dimod.SPIN) try: pmodel = pm.get_penalty_model(spec) if pmodel is not None: return pmodel except pm.ImpossiblePenaltyModel: pass raise ValueError("unable to get the penalty model from factories")
def test_retrieval(self): # put some stuff in the database spec = pm.Specification(nx.path_graph(2), (0, 1), {(-1, -1), (1, 1)}, vartype=pm.SPIN) model = dimod.BinaryQuadraticModel({ 0: 0, 1: 0 }, {(0, 1): -1}, 0.0, vartype=pm.SPIN) widget = pm.PenaltyModel.from_specification(spec, model, 2, -1) for cache in pm.iter_caches(): cache(widget) # now try to get it back new_widget = pm.get_penalty_model(spec) self.assertEqual(widget, new_widget)
def test_one_variable_insert_retrieve(self): """Test case when there is no quadratic contribution (i.e. cache will receive an empty value for the quadratic contribution) """ dbfile = self.database # generate one variable model (i.e. no quadratic terms) spec = pm.Specification(graph=nx.complete_graph(1), decision_variables=[0], feasible_configurations=[(-1, )], min_classical_gap=2, vartype='SPIN') pmodel = pm.get_penalty_model(spec) # insert model into cache pmc.cache_penalty_model(pmodel, database=dbfile) # retrieve model back from cache retrieved_model = pmc.get_penalty_model(spec, database=dbfile) self.assertEqual(pmodel, retrieved_model)
def gate_model(gate_type, fault=True): labels, configurations = GATES[gate_type] if fault: configurations = fault_gate(configurations, FAULT_GAP) size = len(next(iter(configurations))) while True: G = nx.complete_graph(size) nx.relabel_nodes(G, dict(enumerate(labels)), copy=False) spec = pm.Specification(G, labels, configurations, dimod.SPIN) try: pmodel = pm.get_penalty_model(spec) if not pmodel: raise LookupError("failed to get penalty model from factory") # print("penalty model fits on K{}".format(size)) break except pm.ImpossiblePenaltyModel: # print("penalty model does not fit on K{}".format(size)) size += 1 # print('h: {}'.format(pmodel.model.linear)) # print('J: {}\n'.format(pmodel.model.quadratic)) return pmodel
def stitch(csp, min_classical_gap=2.0, max_graph_size=8): """Build a binary quadratic model with minimal energy levels at solutions to the specified constraint satisfaction problem. Args: csp (:obj:`.ConstraintSatisfactionProblem`): Constraint satisfaction problem. min_classical_gap (float, optional, default=2.0): Minimum energy gap from ground. Each constraint violated by the solution increases the energy level of the binary quadratic model by at least this much relative to ground energy. max_graph_size (int, optional, default=8): Maximum number of variables in the binary quadratic model that can be used to represent a single constraint. Returns: :class:`~dimod.BinaryQuadraticModel` Notes: For a `min_classical_gap` > 2 or constraints with more than two variables, requires access to factories from the penaltymodel_ ecosystem to construct the binary quadratic model. .. _penaltymodel: https://github.com/dwavesystems/penaltymodel Examples: This example creates a binary-valued constraint satisfaction problem with two constraints, :math:`a = b` and :math:`b \\ne c`, and builds a binary quadratic model such that each constraint violation by a solution adds the default minimum energy gap. >>> import operator >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY) >>> csp.add_constraint(operator.eq, ['a', 'b']) # a == b >>> csp.add_constraint(operator.ne, ['b', 'c']) # b != c >>> bqm = dwavebinarycsp.stitch(csp) Variable assignments that satisfy the CSP above, violate one, then two constraints, produce energy increases of the default minimum classical gap: >>> bqm.energy({'a': 0, 'b': 0, 'c': 1}) # doctest: +SKIP -2.0 >>> bqm.energy({'a': 0, 'b': 0, 'c': 0}) # doctest: +SKIP 0.0 >>> bqm.energy({'a': 1, 'b': 0, 'c': 0}) # doctest: +SKIP 2.0 This example creates a binary-valued constraint satisfaction problem with two constraints, :math:`a = b` and :math:`b \\ne c`, and builds a binary quadratic model with a minimum energy gap of 4. Note that in this case the conversion to binary quadratic model adds two ancillary variables that must be minimized over when solving. >>> import operator >>> import itertools >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY) >>> csp.add_constraint(operator.eq, ['a', 'b']) # a == b >>> csp.add_constraint(operator.ne, ['b', 'c']) # b != c >>> bqm = dwavebinarycsp.stitch(csp, min_classical_gap=4.0) >>> list(bqm) # doctest: +SKIP ['a', 'aux1', 'aux0', 'b', 'c'] Variable assignments that satisfy the CSP above, violate one, then two constraints, produce energy increases of the specified minimum classical gap: >>> min([bqm.energy({'a': 0, 'b': 0, 'c': 1, 'aux0': aux0, 'aux1': aux1}) for ... aux0, aux1 in list(itertools.product([0, 1], repeat=2))]) # doctest: +SKIP -6.0 >>> min([bqm.energy({'a': 0, 'b': 0, 'c': 0, 'aux0': aux0, 'aux1': aux1}) for ... aux0, aux1 in list(itertools.product([0, 1], repeat=2))]) # doctest: +SKIP -2.0 >>> min([bqm.energy({'a': 1, 'b': 0, 'c': 0, 'aux0': aux0, 'aux1': aux1}) for ... aux0, aux1 in list(itertools.product([0, 1], repeat=2))]) # doctest: +SKIP 2.0 This example finds for the previous example the minimum graph size. >>> import operator >>> csp = dwavebinarycsp.ConstraintSatisfactionProblem(dwavebinarycsp.BINARY) >>> csp.add_constraint(operator.eq, ['a', 'b']) # a == b >>> csp.add_constraint(operator.ne, ['b', 'c']) # b != c >>> for n in range(8, 1, -1): ... try: ... bqm = dwavebinarycsp.stitch(csp, min_classical_gap=4.0, max_graph_size=n) ... except dwavebinarycsp.exceptions.ImpossibleBQM: ... print(n+1) ... 3 """ # ensure we have penaltymodel factory available try: dwavebinarycsp.assert_penaltymodel_factory_available() except AssertionError as e: raise RuntimeError(e) def aux_factory(): for i in count(): yield 'aux{}'.format(i) aux = aux_factory() bqm = dimod.BinaryQuadraticModel.empty(csp.vartype) # developer note: we could cache them and relabel, for now though let's do the simple thing # penalty_models = {} for const in csp.constraints: configurations = const.configurations if len(const.variables) > max_graph_size: msg = ( "The given csp contains a constraint {const} with {num_var} variables. " "This cannot be mapped to a graph with {max_graph_size} nodes. " "Consider checking whether your constraint is irreducible." "").format(const=const, num_var=len(const.variables), max_graph_size=max_graph_size) raise ImpossibleBQM(msg) pmodel = None if len(const) == 0: # empty constraint continue # at the moment, penaltymodel-cache cannot handle 1-variable PMs, so # we handle that case here if min_classical_gap <= 2.0 and len( const) == 1 and max_graph_size >= 1: bqm.update(_bqm_from_1sat(const)) continue # developer note: we could cache them and relabel, for now though let's do the simple thing # if configurations in penalty_models: # raise NotImplementedError for G in iter_complete_graphs(const.variables, max_graph_size + 1, aux): # construct a specification spec = pm.Specification(graph=G, decision_variables=const.variables, feasible_configurations=configurations, min_classical_gap=min_classical_gap, vartype=csp.vartype) # try to use the penaltymodel ecosystem try: pmodel = pm.get_penalty_model(spec) except pm.ImpossiblePenaltyModel: # hopefully adding more variables will make it possible continue if pmodel.classical_gap >= min_classical_gap: break # developer note: we could cache them and relabel, for now though let's do the simple thing # penalty_models[configurations] = pmodel else: msg = ("No penalty model can be built for constraint {}".format( const)) raise ImpossibleBQM(msg) bqm.update(pmodel.model) return bqm