def test_linear_ranges_specified(self): graph = nx.barbell_graph(4, 16) decision_variables = (0, 4, 3) feasible_configurations = {(0, 0, 1): 0.} spec = pm.Specification( graph, decision_variables, feasible_configurations, ising_linear_ranges={v: [-v, 2] for v in graph}, vartype=dimod.BINARY) # check default energy ranges for v in graph: self.assertEqual(spec.ising_linear_ranges[v], [-v, 2]) spec = pm.Specification( graph, decision_variables, feasible_configurations, ising_linear_ranges={v: (-v, 2) for v in graph}, vartype=dimod.BINARY) # check default energy ranges for v in graph: self.assertEqual(spec.ising_linear_ranges[v], [-v, 2])
def test_relabel_inplace(self): graph = nx.circular_ladder_graph(12) decision_variables = (0, 2, 5) feasible_configurations = {(1, 1, 1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) mapping = { i: v for i, v in enumerate('abcdefghijklmnopqrstuvwxyz') if i in graph } new_spec = spec.relabel_variables(mapping, copy=False) self.assertIs(new_spec, spec) # should be the same object self.assertIs(new_spec.graph, spec.graph) # create a test spec graph = nx.relabel_nodes(graph, mapping) decision_variables = (mapping[v] for v in decision_variables) test_spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) self.assertEqual(new_spec, test_spec) self.assertEqual(new_spec.ising_linear_ranges, test_spec.ising_linear_ranges) self.assertEqual(new_spec.ising_quadratic_ranges, test_spec.ising_quadratic_ranges)
def test_relabel_copy(self): graph = nx.circular_ladder_graph(12) decision_variables = (0, 2, 5) feasible_configurations = {(1, 1, 1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) mapping = dict(enumerate('abcdefghijklmnopqrstuvwxyz')) new_spec = spec.relabel_variables(mapping, copy=True) # create a test spec graph = nx.relabel_nodes(graph, mapping) decision_variables = (mapping[v] for v in decision_variables) test_spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) self.assertEqual(new_spec, test_spec) self.assertEqual(new_spec.ising_linear_ranges, test_spec.ising_linear_ranges) self.assertEqual(new_spec.ising_quadratic_ranges, test_spec.ising_quadratic_ranges)
def test_vartype_specified(self): graph = nx.complete_graph(12) decision_variables = (0, 2, 5) feasible_configurations = {(1, 1, 1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) self.assertIs(spec.vartype, dimod.SPIN) spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.BINARY) self.assertIs(spec.vartype, dimod.BINARY) # now set up a spec that can only have one vartype graph = nx.complete_graph(12) decision_variables = (0, 2, 5) feasible_configurations = {(1, 1, -1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) self.assertIs(spec.vartype, dimod.SPIN) # the feasible_configurations are spin with self.assertRaises(ValueError): spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.BINARY)
def test_construction_from_edgelist(self): graph = nx.barbell_graph(10, 7) decision_variables = (0, 4, 3) feasible_configurations = {(-1, -1, -1): 0.} # specification from edges spec0 = pm.Specification(graph.edges, decision_variables, feasible_configurations, vartype=pm.SPIN) # specification from graph spec1 = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) self.assertEqual(spec0, spec1)
def test_relabel_forwards_and_backwards(self): graph = nx.path_graph(4) graph.add_edge(0, 2) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) # make another one graph = nx.path_graph(4) graph.add_edge(0, 2) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) original_widget = pm.PenaltyModel.from_specification( spec, model, 2., -2) new_label_sets = [(10, 1), ('a', 'b'), (1, 'b'), ('1', '2', '3', '4'), ('a', 'b', 'c', 'd')] new_label_sets.extend(itertools.permutations(graph)) for new in new_label_sets: mapping = dict(enumerate(new)) inv_mapping = {u: v for v, u in mapping.items()} # apply then invert with copy=False widget.relabel_variables(mapping, copy=False) widget.relabel_variables(inv_mapping, copy=False) self.assertEqual(widget, original_widget) # apply then invert with copy=True copy_widget = widget.relabel_variables(mapping, copy=True) inv_copy = copy_widget.relabel_variables(inv_mapping, copy=True) self.assertEqual(inv_copy, original_widget)
def test_relabel_forwards_and_backwards(self): graph = nx.path_graph(4) graph.add_edge(0, 2) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) # make another one graph = nx.path_graph(4) graph.add_edge(0, 2) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} original_spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=dimod.SPIN) identity = {v: v for v in graph} new_label_sets = [(10, 1), ('a', 'b'), (1, 'b'), ('1', '2', '3', '4'), ('a', 'b', 'c', 'd')] new_label_sets.extend(itertools.permutations(graph)) for new in new_label_sets: mapping = dict(enumerate(new)) inv_mapping = {u: v for v, u in mapping.items()} # apply then invert with copy=True copy_spec = spec.relabel_variables(mapping, copy=True) inv_copy = copy_spec.relabel_variables(inv_mapping, copy=True) self.assertEqual(inv_copy, original_spec) self.assertEqual(inv_copy.ising_linear_ranges, original_spec.ising_linear_ranges) self.assertEqual(inv_copy.ising_quadratic_ranges, original_spec.ising_quadratic_ranges) # apply then invert with copy=False spec.relabel_variables(mapping, copy=False) if mapping == identity: self.assertEqual(spec, original_spec) else: self.assertNotEqual(spec, original_spec) spec.relabel_variables(inv_mapping, copy=False) self.assertEqual(spec, original_spec) self.assertEqual(spec.ising_linear_ranges, original_spec.ising_linear_ranges) self.assertEqual(spec.ising_quadratic_ranges, original_spec.ising_quadratic_ranges)
def test_linear_ranges_bad(self): graph = nx.barbell_graph(4, 16) decision_variables = (0, 4, 3) feasible_configurations = {(0, 0, 1): 0.} with self.assertRaises(ValueError): pm.Specification(graph, decision_variables, feasible_configurations, ising_linear_ranges={v: [-v, 'a'] for v in graph}, vartype=pm.BINARY) with self.assertRaises(TypeError): pm.Specification(graph, decision_variables, feasible_configurations, ising_linear_ranges={v: [-v, 1, 1] for v in graph}, vartype=pm.BINARY)
def test_binary_specification(self): graph = nx.Graph() for i in range(4): for j in range(4, 8): graph.add_edge(i, j) decision_variables = (0, 1) feasible_configurations = ((0, 0), (1, 1)) # equality spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.BINARY) widget = maxgap.get_penalty_model(spec) self.assertIs(widget.model.vartype, pm.BINARY) # test the correctness of the widget energies = {} for decision_config in itertools.product((0, 1), repeat=2): energies[decision_config] = float('inf') for aux_config in itertools.product((0, 1), repeat=6): sample = dict(enumerate(decision_config + aux_config)) energy = widget.model.energy(sample) energies[decision_config] = min(energies[decision_config], energy) for decision_config, energy in energies.items(): if decision_config in feasible_configurations: self.assertAlmostEqual(energy, widget.ground_energy) else: self.assertGreaterEqual(energy, widget.ground_energy + widget.classical_gap - 10**-6)
def test_quadratic_ranges_partially_specified(self): graph = nx.barbell_graph(4, 16) decision_variables = (0, 4, 3) feasible_configurations = {(0, 0, 1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, ising_quadratic_ranges={ 0: { 1: [0, 1], 2: [-1, 0] }, 2: { 0: [-1, 0] } }, vartype=dimod.BINARY) ising_quadratic_ranges = spec.ising_quadratic_ranges for u in ising_quadratic_ranges: for v in ising_quadratic_ranges[u]: self.assertIs(ising_quadratic_ranges[u][v], ising_quadratic_ranges[v][u]) for u, v in graph.edges: self.assertIn(v, ising_quadratic_ranges[u]) self.assertIn(u, ising_quadratic_ranges[v]) self.assertEqual(ising_quadratic_ranges[0][1], [0, 1])
def test_construction(self): # build a specification graph = nx.complete_graph(10) decision_variables = (0, 4, 5) feasible_configurations = {(-1, -1, -1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) # build a model model = pm.BinaryQuadraticModel({v: 0 for v in graph}, {edge: 0 for edge in graph.edges}, 0.0, vartype=pm.Vartype.SPIN) # build a PenaltyModel explicitly pm0 = pm.PenaltyModel(graph, decision_variables, feasible_configurations, pm.SPIN, model, .1, 0) # build from spec pm1 = pm.PenaltyModel.from_specification(spec, model, .1, 0) # should result in equality self.assertEqual(pm0, pm1)
def test_bad_energy_range(self): graph = nx.path_graph(3) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) linear = {v: -3 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) with self.assertRaises(ValueError): widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) linear = {v: 0 for v in graph} quadratic = {edge: 5 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) with self.assertRaises(ValueError): widget = pm.PenaltyModel.from_specification(spec, model, 2., -2)
def test_retrieval(self): spec = pm.Specification(nx.path_graph(2), (0, 1), {(-1, -1), (1, 1)}, vartype=pm.SPIN) widget = pm.get_penalty_model(spec) self.assertEqual(widget.model.linear, {0: 0, 1: 0}) self.assertEqual(widget.model.quadratic, {(0, 1): -1})
def test_penalty_model_insert_retrieve(self): conn = self.clean_connection graph = nx.path_graph(3) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) with conn as cur: pmc.insert_penalty_model(cur, widget) with conn as cur: pms = list(pmc.iter_penalty_model_from_specification(cur, spec)) self.assertEqual(len(pms), 1) widget_, = pms self.assertEqual(widget_, widget)
def test_construction_bad_graph(self): graph = 1 decision_variables = (0, 4, 5) feasible_configurations = {(-1, -1, -1): 0.} with self.assertRaises(TypeError): pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN)
def test_insert_retrieve(self): dbfile = self.database linear = {'1': 0.0, '0': -0.5, '3': 1.0, '2': -0.5} quadratic = { ('0', '3'): -1.0, ('1', '2'): 1.0, ('0', '2'): 0.5, ('1', '3'): 1.0 } offset = 0.0 model = pm.BinaryQuadraticModel(linear, quadratic, offset, vartype=pm.SPIN) graph = nx.Graph() graph.add_edges_from(quadratic) decision_variables = ('0', '2', '3') feasible_configurations = ((-1, -1, -1), (-1, 1, -1), (1, -1, -1), (1, 1, 1)) spec = pm.Specification(graph, decision_variables, feasible_configurations, pm.SPIN) classical_gap = 2 ground = -2.5 pmodel = pm.PenaltyModel.from_specification(spec, model, classical_gap, ground) pmc.cache_penalty_model(pmodel, database=dbfile) # print(spec.feasible_configurations) # print(spec.decision_variables) # get it back ret_pmodel = pmc.get_penalty_model(spec, database=dbfile) # now get back one with a different decision_variables spec2 = pm.Specification(graph, ('3', '0', '2'), feasible_configurations, pm.SPIN) try: ret_pmodel = pmc.get_penalty_model(spec2, database=dbfile) self.assertNotEqual(ret_pmodel, pmodel) except: pass
def test_relabel_inplace_overlap(self): graph = nx.circular_ladder_graph(12) decision_variables = (0, 2, 5) feasible_configurations = {(1, 1, 1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) mapping = {v: v + 5 for v in graph} new_spec = spec.relabel_variables(mapping, copy=False)
def test_typical(self): graph = nx.complete_graph(3) spec = pm.Specification(graph, [0, 1], {(-1, -1): 0, (+1, +1): 0}, pm.SPIN) widget = maxgap.get_penalty_model(spec) # some quick test to see that the penalty model propogated in for v in graph: self.assertIn(v, widget.model.linear) for (u, v) in graph.edges: self.assertIn(u, widget.model.adj[v])
def test_construction_typical(self): graph = nx.complete_graph(10) decision_variables = (0, 4, 5) feasible_configurations = {(-1, -1, -1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) self.assertEqual(spec.graph, graph) # literally the same object self.assertEqual(spec.decision_variables, decision_variables) self.assertEqual(spec.feasible_configurations, feasible_configurations) self.assertIs(spec.vartype, pm.SPIN)
def test_relabel(self): graph = nx.path_graph(3) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) # now set up the same widget with 0 relabelled to 'a' graph = nx.path_graph(3) graph = nx.relabel_nodes(graph, {0: 'a'}) decision_variables = ('a', 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) test_widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) # without copy new_widget = widget.relabel_variables({0: 'a'}, copy=True) self.assertEqual(test_widget, new_widget) self.assertEqual(new_widget.decision_variables, ('a', 2)) widget.relabel_variables({0: 'a'}, copy=False) self.assertEqual(widget, test_widget) self.assertEqual(widget.decision_variables, ('a', 2))
def test_ranges_default(self): graph = nx.barbell_graph(4, 16) decision_variables = (0, 4, 3) feasible_configurations = {(0, 0, 0): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.BINARY) for v in graph: self.assertEqual(spec.ising_linear_ranges[v], [-2, 2]) for u, v in graph.edges: self.assertEqual(spec.ising_quadratic_ranges[u][v], [-1, 1]) self.assertEqual(spec.ising_quadratic_ranges[v][u], [-1, 1])
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 = pm.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_bad_relabel(self): graph = nx.path_graph(4) graph.add_edge(0, 2) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.SPIN) mapping = {0: 2, 1: 1} with self.assertRaises(ValueError): spec.relabel_variables(mapping, copy=True) with self.assertRaises(ValueError): spec.relabel_variables(mapping, copy=False)
def test_arbitrary_labels_on_k44(self): dbfile = self.database graph = nx.Graph() for i in range(3): for j in range(3, 6): graph.add_edge(i, j) decision_variables = (0, 5) feasible_configurations = ((0, 0), (1, 1)) # equality spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.BINARY) linear = {v: 0 for v in graph} quadratic = {edge: 0 for edge in graph.edges} if decision_variables in quadratic: quadratic[decision_variables] = -1 else: u, v = decision_variables assert (v, u) in quadratic quadratic[(v, u)] = -1 model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) pmodel = pm.PenaltyModel.from_specification(spec, model, 2, -1) # now cache the pmodel to make sure there is something to find for thingy in itertools.permutations(range(6)): mapping = dict(enumerate(thingy)) pmodel = pmodel.relabel_variables(mapping, copy=True) pmc.cache_penalty_model(pmodel, database=dbfile) # now relabel some variables mapping = {1: '1', 2: '2', 3: '3', 4: '4'} new_spec = spec.relabel_variables(mapping) # retrieve from the new_spec # now try to retrieve it retreived_pmodel = pmc.get_penalty_model(new_spec, database=dbfile)
def test_and_on_k44(self): graph = nx.Graph() for i in range(4): for j in range(4, 8): graph.add_edge(i, j) decision_variables = (0, 2, 3) feasible_configurations = AND(2) mapping = {0: '0', 1: '1', 2: '2', 3: '3'} graph = nx.relabel_nodes(graph, mapping) decision_variables = tuple(mapping[x] for x in decision_variables) spin_configurations = tuple([tuple([2 * i - 1 for i in b]) for b in feasible_configurations]) spec = pm.Specification(graph, decision_variables, spin_configurations, vartype=pm.SPIN) pm0 = maxgap.get_penalty_model(spec) self.check_generated_ising_model(pm0.feasible_configurations, pm0.decision_variables, pm0.model.linear, pm0.model.quadratic, pm0.ground_energy, pm0.classical_gap)
def test_binary_specification(self): dbfile = self.database # set up a specification and a corresponding penaltymodel graph = nx.Graph() for i in 'abcd': for j in 'efgh': graph.add_edge(i, j) decision_variables = ('a', 'e') feasible_configurations = ((0, 0), (1, 1)) # equality spec = pm.Specification(graph, decision_variables, feasible_configurations, vartype=pm.BINARY) linear = {v: 0 for v in graph} quadratic = {edge: 0 for edge in graph.edges} if decision_variables in quadratic: quadratic[decision_variables] = -1 else: u, v = decision_variables assert (v, u) in quadratic quadratic[(v, u)] = -1 model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) pmodel = pm.PenaltyModel.from_specification(spec, model, 2, -1) # now cache the pmodel to make sure there is something to find pmc.cache_penalty_model(pmodel, database=dbfile) # now try to retrieve it retreived_pmodel = pmc.get_penalty_model(spec, database=dbfile) self.assertIs(retreived_pmodel.model.vartype, pm.BINARY) # check that the specification is equal to the retreived_pmodel self.assertTrue(spec.__eq__(retreived_pmodel))
def fault_model(gate_type): labels, configurations = GATES[gate_type] 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, pm.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 test_typical(self): dbfile = self.database # insert a penalty model graph = nx.path_graph(3) decision_variables = (0, 2) feasible_configurations = {(-1, -1): 0., (+1, +1): 0.} spec = pm.Specification(graph, decision_variables, feasible_configurations, pm.SPIN) linear = {v: 0 for v in graph} quadratic = {edge: -1 for edge in graph.edges} model = pm.BinaryQuadraticModel(linear, quadratic, 0.0, vartype=pm.SPIN) widget = pm.PenaltyModel.from_specification(spec, model, 2., -2) # cache the penaltymodel pmc.cache_penalty_model(widget, database=dbfile) # retrieve it widget_ = pmc.get_penalty_model(spec, database=dbfile) self.assertEqual(widget_, widget)
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 with a minimum energy level of -2 such that each constraint violation by a solution adds the default minimum energy gap. >>> import dwavebinarycsp >>> 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) >>> bqm.energy({'a': 0, 'b': 0, 'c': 1}) # satisfies csp -2.0 >>> bqm.energy({'a': 0, 'b': 0, 'c': 0}) # violates one constraint 0.0 >>> bqm.energy({'a': 1, 'b': 0, 'c': 0}) # violates two constraints 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 dwavebinarycsp >>> 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'] >>> min([bqm.energy({'a': 0, 'b': 0, 'c': 1, 'aux0': aux0, 'aux1': aux1}) for ... aux0, aux1 in list(itertools.product([0, 1], repeat=2))]) # satisfies csp -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))]) # violates one constraint -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))]) # violates two constraints 2.0 This example finds for the previous example the minimum graph size. >>> import dwavebinarycsp >>> 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 """ 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 if min_classical_gap <= 2.0: if len(const) == 1 and max_graph_size >= 1: bqm.update(_bqm_from_1sat(const)) continue elif len(const) == 2 and max_graph_size >= 2: bqm.update(_bqm_from_2sat(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, 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 if pmodel is None: msg = ("No penalty model can be build for constraint {}".format( const)) raise ImpossibleBQM(msg) bqm.update(pmodel.model) return bqm
def test_construction_empty(self): spec = pm.Specification(nx.Graph(), [], {}, pm.SPIN) self.assertEqual(len(spec), 0)