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 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 convert_openfermion_ising_to_qubo(operator: IsingOperator): """ Converts dimod BinaryQuadraticModel to OpenFermion IsingOperator object. 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 sample(self, bqm: dimod.BinaryQuadraticModel, method="miqp", num_reads=1, gurobi_params_kw=None): assert (method in ["mip", "miqp"]) bqm_bin = bqm.change_vartype(vartype=dimod.BINARY, inplace=False) variable_ids = frozenset(bqm_bin.variables) variable_product_ids = frozenset(bqm_bin.quadratic) m = Model() gurobi_params = { "OutputFlag": 0, "TimeLimit": 60, "Threads": 12, "Cuts": 1, "MIPFocus": 2, "PoolSearchMode": 2, "PoolSolutions": num_reads } if gurobi_params_kw is None: gurobi_params_kw = {} gurobi_params.update(gurobi_params_kw) for param, value in gurobi_params.items(): m.setParam(param, value) variable_lookup = {} for vid in variable_ids: variable_lookup[vid] = m.addVar(lb=0, ub=1, vtype=GRB.BINARY, name="var_{}".format(vid)) if method == "mip": for pair in variable_product_ids: variable_lookup[pair] = m.addVar(lb=0, ub=1, vtype=GRB.BINARY, name="link_{}_{}".format( str(pair[0]), str(pair[1]))) m.update() if method == "mip": for i, j in variable_product_ids: m.addConstr(variable_lookup[(i, j)] >= variable_lookup[i] + variable_lookup[j] - 1) m.addConstr(variable_lookup[(i, j)] <= variable_lookup[i]) m.addConstr(variable_lookup[(i, j)] <= variable_lookup[j]) bqm_ising = bqm.change_vartype(vartype=dimod.SPIN, inplace=False) if len(bqm_ising.linear) <= 0 or all(bqm_ising.linear[lt] == 0.0 for lt in bqm_ising.linear): warnings.warn( 'detected spin symmetry, adding symmetry breaking constraint') v1 = variable_ids[0] m.addConstr(variable_lookup[v1] == 0) obj = 0.0 for lt in bqm_bin.linear: obj += bqm_bin.linear[lt] * variable_lookup[lt] if method == "mip": for qt in bqm_bin.quadratic: obj += bqm_bin.quadratic[qt] * variable_lookup[qt] elif method == "miqp": for qt in bqm_bin.quadratic: i = qt[0] j = qt[1] obj += bqm_bin.quadratic[qt] * variable_lookup[ i] * variable_lookup[j] m.setObjective(obj, GRB.MINIMIZE) m.update() m.optimize() energies = [] samples = [] for i in range(m.SolCount): m.Params.SolutionNumber = i # set solution numbers energy = m.ObjVal + bqm_bin.offset sample = {k: int(variable_lookup[k].X) for k in variable_ids} energies.append(energy) samples.append(sample) ss = dimod.SampleSet.from_samples(samples, vartype=bqm.BINARY, energy=energies, aggregate_samples=True) return ss.change_vartype(bqm.vartype)