def solve(self): if (self.useQPU): sampler = EmbeddingComposite(DWaveSampler(solver={'qpu': True})) sampleset = sampler.sample_qubo(self.Q, num_reads=self.n_reads, chain_strength=self.chain) elif (self.useNeal): bqm = BinaryQuadraticModel.from_qubo(self.Q, offset=self.offset) sampler = neal.SimulatedAnnealingSampler() sampleset = sampler.sample(bqm, num_reads=self.n_reads, chain_strength=self.chain) elif (self.useHyb): bqm = BinaryQuadraticModel.from_qubo(self.Q, offset=self.offset) sampler = LeapHybridSampler() sampleset = sampler.sample(bqm, num_reads=self.n_reads) else: bqm = BinaryQuadraticModel.from_qubo(self.Q, offset=self.offset) sampler = TabuSampler() sampleset = sampler.sample(bqm, num_reads=self.n_reads, chain_strength=self.chain) self.sampleset = sampleset
def __create_qubo_matrix(self, length): if self.mode == "np": self.qubo = np.zeros((length, length)) elif self.mode == "bqm": self.qubo = BinaryQuadraticModel({}, {}, 0.0, dimod.BINARY) elif self.mode == "qa": self.qubo = {} else: raise Exception(f"unknown mode: {self.mode}")
def solve(self, verbose=False, num_repeats=50, target=None, debug=False): if self.qubo is None: self.pre_process() if verbose: print("Solving MAX-SMTI with Qbsolv") if self.qubo_size == 0 or self.qubo_size == 1: if debug: return None return Solution(self.matching, self.pre_evaluated_solution) if self.mode == "np": # more memory intensive response = QBSolv().sample(BinaryQuadraticModel.from_numpy_matrix(self.qubo), num_repeats=num_repeats, target=target) elif self.mode == "bqm": response = QBSolv().sample(self.qubo, num_repeats=num_repeats, target=target) else: raise Exception(f"mode: {self.mode} cannot be solved yet") if debug: return response if verbose: print(response) for index, sample in enumerate(list(response.samples())): match, valid = self.encode(sample) print(index, ":", Solution(self.matching, match).is_stable(), match, valid) energies = list(response.data_vectors['energy']) min_en = min(energies) ret_match, valid = self.encode(list(response.samples())[energies.index(min_en)]) return Solution(self.matching, ret_match, energy=min_en)
def test_ub_only(self): bqm = BinaryQuadraticModel({ 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0 }, { (0, 1): -2.0, (0, 2): -5.0, (0, 3): -2.0, (0, 4): -2.0, (1, 2): -2.0, (1, 3): -2.0, (1, 4): 4.0, (2, 3): -3.0, (2, 4): -5.0, (3, 4): -4.0 }, 0, dimod.SPIN) sampler = ClipComposite(ExactSolver()) solver = ExactSolver() response = sampler.sample(bqm, upper_bound=1) response_exact = solver.sample(bqm) self.assertEqual(response.first.sample, response_exact.first.sample) self.assertAlmostEqual(response.first.energy, response_exact.first.energy)
def test_with_labels(self): bqm = BinaryQuadraticModel( { 'a': 0.0, 'b': 0.0, 'c': 0.0, 'd': 0.0, 'e': 0.0 }, { ('a', 'b'): -2.0, ('a', 'c'): -5.0, ('a', 'd'): -2.0, ('a', 'e'): -2.0, ('b', 'c'): -2.0, ('b', 'd'): -2.0, ('b', 'e'): 4.0, ('c', 'd'): -3.0, ('c', 'e'): -5.0, ('d', 'e'): -4.0 }, 0, dimod.SPIN) sampler = ClipComposite(ExactSolver()) solver = ExactSolver() response = sampler.sample(bqm, lower_bound=-1, upper_bound=1) response_exact = solver.sample(bqm) self.assertEqual(response.first.sample, response_exact.first.sample) self.assertAlmostEqual(response.first.energy, response_exact.first.energy)
def prepare_qubo_for_feature_selection(x: np.ndarray, y: np.ndarray, alpha: float): """Creates a QUBO based on method described in "Quadratic Programming Feature Selection" by Rodriguez et al. (https://www.jmlr.org/papers/volume11/rodriguez-lujan10a/rodriguez-lujan10a.pdf) Args: x: array containing input features y: vector containing target output alpha: parameter describing the relative importance of independence and relevance of features. Returns: BinaryQuadraticModel """ if alpha < 0 or alpha > 1: raise ValueError("Wrong value of alpha parameter.") full_matrix = np.concatenate([x, np.array([y]).T], axis=1).T correlation_matrix = np.corrcoef(full_matrix) n_features = x.shape[1] Q = np.abs(correlation_matrix[:n_features, :n_features]) F = np.abs(correlation_matrix[n_features][:-1]) qubo_dict = {} for i in range(len(Q)): qubo_dict[(i, i)] = -1 * alpha * F[i] for j in range(len(Q)): if i != j: qubo_dict[(i, j)] = (1 - alpha) * Q[i][j] return BinaryQuadraticModel(qubo_dict, vartype="BINARY")
def __init__(self, child_sampler, bqm=None, h=None, J=None, offset=0, scalar=None, bias_range=1, quadratic_range=None, ignored_variables=None, ignored_interactions=None, ignore_offset=False, **other_params): scale_options = dict(scalar=scalar, bias_range=bias_range, quadratic_range=quadratic_range, ignored_variables=ignored_variables, ignored_interactions=ignored_interactions, ignore_offset=ignore_offset) self.child = child_sampler if bqm is not None: self.bqm, _ = _scaled_bqm(bqm, **scale_options) elif h is not None and J is not None: if max(map(len, J.keys())) == 2: bqm = BinaryQuadraticModel.from_ising(h, J, offset=offset) self.bqm, _ = _scaled_bqm(bqm, **scale_options) else: h_sc, J_sc, offset_sc = _scaled_hubo(h, J, offset=offset, **scale_options) self.h = h_sc self.J = J_sc self.offset = offset_sc
def convert_qubo_to_openfermion_ising( qubo: BinaryQuadraticModel) -> IsingOperator: """Converts dimod BinaryQuadraticModel to OpenFermion IsingOperator object. The resulting Openfermion IsingOperator has the following property: For every bitstring, its expected value is the same as the energy of the original QUBO. In order to ensure this, we had to add a minus sign for the coefficients of the linear terms coming from dimod conversion. For more context about conventions used please refer to note in `convert_measurements_to_sampleset` docstring. Args: qubo: Object we want to convert Returns: IsingOperator: IsingOperator representation of the input qubo. """ linear_coeffs, quadratic_coeffs, offset = qubo.to_ising() list_of_ising_strings = [f"{offset}[]"] for i, value in linear_coeffs.items(): list_of_ising_strings.append(f"{-value}[Z{i}]") for (i, j), value in quadratic_coeffs.items(): list_of_ising_strings.append(f"{value}[Z{i} Z{j}]") ising_string = " + ".join(list_of_ising_strings) return IsingOperator(ising_string)
def test_no_bounds(self): bqm = BinaryQuadraticModel({ 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0 }, { (0, 1): -2.0, (0, 2): -5.0, (0, 3): -2.0, (0, 4): -2.0, (1, 2): -2.0, (1, 3): -2.0, (1, 4): 4.0, (2, 3): -3.0, (2, 4): -5.0, (3, 4): -4.0 }, 0, dimod.SPIN) with self.assertWarns(DeprecationWarning): sampler = ClipComposite(ExactSolver()) solver = ExactSolver() response = sampler.sample(bqm) response_exact = solver.sample(bqm) self.assertEqual(response.first.sample, response_exact.first.sample) self.assertAlmostEqual(response.first.energy, response_exact.first.energy)
def solve_multi(self, verbose=False, num_repeats=200): if self.qubo is None: self.pre_process() if verbose: print("Solving multiple solutions of MAX-SMTI with Qbsolv") if self.qubo_size == 0 or self.qubo_size == 1: return [Solution(self.matching, self.pre_evaluated_solution)] if self.mode == "np": # more memory intensive response = QBSolv().sample(BinaryQuadraticModel.from_numpy_matrix(self.qubo), num_repeats=num_repeats) elif self.mode == "bqm": response = QBSolv().sample(self.qubo, num_repeats=num_repeats) else: raise Exception(f"mode: {self.mode} cannot be solved yet") if verbose: print(response) for index, sample in enumerate(list(response.samples())): match, valid = self.encode(sample) print(index, ":", Solution(self.matching, match).is_stable(), match, valid) opt_en = self.get_optimal_energy(self.matching.size) solutions = [] for sample, energy, occ in response.record: if energy == opt_en: match, valid = self.encode_qa(sample.tolist()) if verbose and not valid: print("Invalid encoding and valid energy!") solutions.append(Solution(self.matching, match)) return solutions
def solve_multi_data(self, verbose=False, target=None, num_repeats=200): if self.qubo is None: self.pre_process() if verbose: print("Solving multiple solutions of MAX-SMTI with Qbsolv") if self.qubo_size == 0 or self.qubo_size == 1: return [Solution(self.matching, self.pre_evaluated_solution)] if self.mode == "np": # more memory intensive response = QBSolv().sample(BinaryQuadraticModel.from_numpy_matrix(self.qubo), num_repeats=num_repeats, target=target, algorithm=SOLUTION_DIVERSITY) elif self.mode == "bqm": response = QBSolv().sample(self.qubo, num_repeats=num_repeats, target=target) else: raise Exception(f"mode: {self.mode} cannot be solved yet") if verbose: print(response) for index, sample in enumerate(list(response.samples())): match, valid = self.encode(sample) print(index, ":", Solution(self.matching, match).is_stable(), match, valid) samples = pd.DataFrame() for sample, energy, occ in response.record: match, valid = self.encode_qa(sample.tolist()) stable, size = Solution(self.matching, match).is_stable() samples = samples.append({"match": match, "sample": sample.tolist(), "energy": energy, "occ": occ, "valid": valid, "stable": stable, "size": size}, ignore_index=True) return samples
def __init__(self, qpu: Type[QPU], problem: dimod.BinaryQuadraticModel, partial_initial_mapping: dict = None): if problem.vartype is dimod.BINARY: problem.change_vartype(dimod.SPIN) print('changed vartype of problem definition from BINARY to SPIN') self.log_qbs = set(problem.variables) self.hard_qbs = set(qpu.qubits()) assert len(self.hard_qbs) == len( self.log_qbs ), 'number of hardware qubits does not match number of logical qubits' if partial_initial_mapping is None: self.hard2log = { hard_qb: log_qb for hard_qb, log_qb in zip(self.hard_qbs, self.log_qbs) } self.log2hard = { log_qb: hard_qb for log_qb, hard_qb in zip(self.log_qbs, self.hard_qbs) } else: assert len(set(partial_initial_mapping.values())) == len( partial_initial_mapping.values( )), 'partial_initial_mapping is not bijective' if len(partial_initial_mapping) < len(self.log_qbs): remaining_hard_qbs = self.hard_qbs - set( partial_initial_mapping.keys()) remaining_log_qbs = self.log_qbs - set( partial_initial_mapping.values()) initial_mapping = partial_initial_mapping for hard_qb, log_qb in zip(remaining_hard_qbs, remaining_log_qbs): initial_mapping[hard_qb] = log_qb else: initial_mapping = partial_initial_mapping self.hard2log = initial_mapping self.log2hard = { log_qb: hard_qb for hard_qb, log_qb in self.hard2log.items() }
def test_empty_bqm(self): bqm = BinaryQuadraticModel(linear={1: -1.3, 4: -0.5}, quadratic={(1, 4): -0.6}, offset=0, vartype=Vartype.SPIN) fixed_variables = {1: -1, 4: -1} sampler = FixedVariableComposite(ExactSolver()) response = sampler.sample(bqm, fixed_variables=fixed_variables) self.assertIsInstance(response, SampleSet)
def get_bqm(Q): """Returns a bqm representation of the problem. Args: Q(qubo): dictionary representing a QUBO """ # Convert to bqm bqm = BinaryQuadraticModel.from_qubo(Q) return bqm
def solve_tsp_hybrid(Q, G): bqm = BinaryQuadraticModel.from_qubo(Q) response = LeapHybridSampler().sample(bqm, time_limit=40) sample = response.first.sample route = [None] * len(G) for (city, time), val in sample.items(): if val: route[time] = city cost = calculate_cost(nx.to_numpy_array(G), route) return (route, cost)
def __init__(self, problem_instance: dimod.BinaryQuadraticModel, qpu: Type[QPU], initial_mapping: Mapping = None): self.problem = problem_instance self.remaining_interactions = problem_instance.to_networkx_graph() self.qpu = qpu if initial_mapping is None: self.initial_mapping = Mapping(self.qpu, self.problem) else: self.initial_mapping = initial_mapping self.mapping = cp.deepcopy(self.initial_mapping) self.layers = [Layer(self.qpu)]
def decompose_into_chains(problem_instance: dimod.BinaryQuadraticModel): problem_graph = problem_instance.to_networkx_graph() for log_qb in problem_graph: problem_graph.add_node(log_qb, endnode=False) problem_size = len(problem_graph) color_sets = find_edge_coloring(problem_graph) color_sets = sorted(color_sets, key=lambda color_set: len(color_set), reverse=True) for log_qb0, log_qb1 in problem_graph.edges(): if frozenset((log_qb0, log_qb1)) in color_sets[0]: color0 = problem_graph[log_qb0][log_qb1]['color'] break for log_qb0, log_qb1 in problem_graph.edges(): if frozenset((log_qb0, log_qb1)) in color_sets[1]: color1 = problem_graph[log_qb0][log_qb1]['color'] break def filter_edge(log_qb0, log_qb1) -> bool: if problem_graph[log_qb0][log_qb1]['color'] == color0: return True elif problem_graph[log_qb0][log_qb1]['color'] == color1: return True else: return False twocolor_graph = nx.subgraph_view(problem_graph, filter_edge=filter_edge) components = [ twocolor_graph.subgraph(c).copy() for c in nx.connected_components(twocolor_graph) ] chains, loops = [], [] for component in components: deg = 3 for node in component: if component.degree(node) < deg: node0 = node deg = component.degree(node) for neighbor in component.neighbors(node0): node1 = neighbor break lst = [node0, node1] for _ in range(len(component) - 2): for neighbor in component.neighbors(node1): if neighbor is not node0: node0 = node1 node1 = neighbor lst.append(node1) break if deg == 1: chains.append(lst) elif deg == 2: loops.append(lst) return chains, loops
def convert_openfermion_ising_to_qubo( operator: IsingOperator) -> BinaryQuadraticModel: """ Converts dimod Openfermion IsingOperator to BinaryQuadraticModel object. The resulting QUBO has the following property: For every bitstring, its energy is the same as the expected value of the original Ising Hamiltonian. For more context about conventions used please refer to note in `convert_measurements_to_sampleset` docstring. Note: The conversion might not be 100% accurate due to performing floating point operations during conversion between Ising and QUBO models. Args: operator: IsingOperator we want to convert Returns: qubo: BinaryQuadraticModel representation of the input operator """ if not isinstance(operator, IsingOperator): raise TypeError( f"Input is of type: {type(operator)}. Only Ising Operators are supported." ) offset = 0 linear_terms = {} quadratic_terms = {} for term, coeff in operator.terms.items(): if len(term) == 0: offset = coeff if len(term) == 1: linear_terms[term[0][0]] = -coeff if len(term) == 2: quadratic_terms[(term[0][0], term[1][0])] = coeff if len(term) > 2: raise ValueError( "Ising to QUBO conversion works only for quadratic Ising models." ) dimod_ising = BinaryQuadraticModel(linear_terms, quadratic_terms, offset, vartype="SPIN") return dimod_ising.change_vartype("BINARY", inplace=False)
def test_sample(self): bqm = BinaryQuadraticModel({ 1: -1.3, 4: -0.5 }, {(1, 4): -0.6}, 0, vartype=Vartype.SPIN) sampler = ConnectedComponentsComposite(ExactSolver()) response = sampler.sample(bqm) self.assertEqual(response.first.sample, {4: 1, 1: 1}) self.assertAlmostEqual(response.first.energy, -2.4)
def test_sample(self): bqm = BinaryQuadraticModel(linear={1: -1.3, 4: -0.5}, quadratic={(1, 4): -0.6}, offset=0, vartype=Vartype.SPIN) fixed_variables = {1: -1} sampler = FixedVariableComposite(ExactSolver()) response = sampler.sample(bqm, fixed_variables=fixed_variables) self.assertEqual(response.first.sample, {4: -1, 1: -1}) self.assertAlmostEqual(response.first.energy, 1.2)
def test_info_propagation(self): bqm = BinaryQuadraticModel.from_ising({}, {}) class MySampler: @staticmethod def sample(bqm): return dimod.SampleSet.from_samples_bqm([], bqm, info=dict(a=1)) sampleset = ClipComposite(MySampler).sample(bqm) self.assertEqual(sampleset.info, {'a': 1})
def test_sample_bias_range(self): linear = {'a': -4.0, 'b': -4.0} quadratic = {('a', 'b'): 3.2} ignored_variables, ignored_interactions = _check_params(None, None) bqm = BinaryQuadraticModel.from_ising(linear, quadratic) comp_parameters = dict(ignored_interactions=ignored_interactions, ignored_variables=ignored_variables, bias_range=2.) sampler = ScaleComposite( ScalingChecker(ExactSolver(), bqm=bqm, **comp_parameters)) response = sampler.sample(bqm, **comp_parameters) self.assertAlmostEqual(response.first.energy, -4.8)
def build_knapsack_cqm(costs, weights, max_weight): """Construct a CQM for the knapsack problem. Args: costs (array-like): Array of costs for the items. weights (array-like): Array of weights for the items. max_weight (int): Maximum allowable weight for the knapsack. Returns: Constrained quadratic model instance that represents the knapsack problem. """ num_items = len(costs) print("\nBuilding a CQM for {} items.".format(str(num_items))) cqm = ConstrainedQuadraticModel() obj = BinaryQuadraticModel(vartype='BINARY') constraint = QuadraticModel() for i in range(num_items): # Objective is to maximize the total costs obj.add_variable(i) obj.set_linear(i, -costs[i]) # Constraint is to keep the sum of items' weights under or equal capacity constraint.add_variable('BINARY', i) constraint.set_linear(i, weights[i]) cqm.set_objective(obj) cqm.add_constraint(constraint, sense="<=", rhs=max_weight, label='capacity') return cqm
def exact_cover_bqm(problem_set, subsets): """Returns a BQM for an exact cover. An exact cover is a collection of subsets of `problem_set` that contains every element in `problem_set` exactly once. Args: problem_set : iterable An iterable of unique numbers. subsets : list(iterable(numeric)) A list of subsets of `problem_set` used to find an exact cover. """ bqm = BinaryQuadraticModel({}, {}, 0, 'BINARY') for element in problem_set: bqm.offset += 1 for i in range(len(subsets)): if element in subsets[i]: bqm.add_variable(i, -1) for j in range(i): if element in subsets[j]: bqm.add_interaction(i, j, 2) return bqm
def test_empty_bqm(self): bqm = BinaryQuadraticModel({ 1: -1.3, 4: -0.5 }, {(1, 4): -0.6}, 0, vartype=Vartype.SPIN) fixed_variables = {1: -1, 4: -1} with self.assertWarns(DeprecationWarning): sampler = FixedVariableComposite(ExactSolver()) response = sampler.sample(bqm, fixed_variables=fixed_variables) self.assertIsInstance(response, SampleSet)
def test_empty_bqm(self): bqm = BinaryQuadraticModel({ 1: -1.3, 4: -0.5 }, {(1, 4): -0.6}, 0, vartype=Vartype.SPIN) fixed_variables = {1: -1, 4: -1} sampler = FixVariablesComposite( ConnectedComponentsComposite(ExactSolver())) response = sampler.sample(bqm, fixed_variables=fixed_variables) self.assertIsInstance(response, SampleSet)
def test_sample_two_components(self): bqm = BinaryQuadraticModel({ 0: 0.0, 1: 4.0, 2: -4.0, 3: 0.0 }, { (0, 1): -4.0, (2, 3): 4.0 }, 0.0, Vartype.BINARY) sampler = ConnectedComponentsComposite(ExactSolver()) response = sampler.sample(bqm) self.assertIsInstance(response, SampleSet) self.assertEqual(response.first.sample, {0: 0, 1: 0, 2: 1, 3: 0}) self.assertAlmostEqual(response.first.energy, bqm.energy({ 0: 0, 1: 0, 2: 1, 3: 0 }))
def test_roof_duality_triangle(self): bqm = BinaryQuadraticModel.from_ising({}, { 'ab': -1, 'bc': -1, 'ac': -1 }) # two equally good solutions sampler = FixVariablesComposite(ExactSolver(), algorithm='roof_duality') sampleset = sampler.sample(bqm) self.assertEqual(set(sampleset.variables), set('abc')) dtest.assert_response_energies(sampleset, bqm)
def test_sample_passcomponents(self): bqm = BinaryQuadraticModel({ 0: 0.0, 1: 4.0, 2: -4.0, 3: 0.0 }, { (0, 1): -4.0, (2, 3): 4.0 }, 0.0, Vartype.BINARY) with self.assertWarns(DeprecationWarning): sampler = ConnectedComponentsComposite(ExactSolver()) response = sampler.sample(bqm, components=[{0, 1}, {2, 3}]) self.assertIsInstance(response, SampleSet) self.assertEqual(response.first.sample, {0: 0, 1: 0, 2: 1, 3: 0}) self.assertAlmostEqual(response.first.energy, bqm.energy({ 0: 0, 1: 0, 2: 1, 3: 0 }))
def create_qubo(self): self.p, self.p1, self.p2 = self.get_default_penalties() assert self.p is not None and self.p1 is not None and self.p2 is not None length = self.matching.size ** 2 if self.mode == "np": self.qubo = np.zeros((length, length), dtype=np.object) elif self.mode == "bqm": self.qubo = BinaryQuadraticModel({}, {}, 0.0, dimod.BINARY) else: raise Exception(f"unknown mode: {self.mode}") for j in range(length): for i in range(j + 1): if i == j: self.__assign_qubo(j, i, - 2 * (self.matching.size - 1) * self.p1) else: self.__assign_qubo(j, i, 1) # match of current line == current "selected" matched pair w_j = self.encoding[j][1] m_j = self.encoding[j][0] # match of current column == current match under review w_i = self.encoding[i][1] m_i = self.encoding[i][0] if m_j == m_i or w_j == w_i: self.__assign_qubo(j, i, self.p) continue prefs_m_j = self.matching.prefers(m_j, w_i, w_j) and self.matching.prefers(w_i, m_j, m_i) prefs_w_j = self.matching.prefers(w_j, m_i, m_j) and self.matching.prefers(m_i, w_j, w_i) if prefs_m_j and prefs_w_j: self.__assign_qubo(j, i, 2 * self.p2) elif prefs_m_j: self.__assign_qubo(j, i, self.p2) elif prefs_w_j: self.__assign_qubo(j, i, self.p2) return self