def encode_bqm_as_qp(solver, linear, quadratic): """Encode the binary quadratic problem for submission to a given solver, using the `qp` format for data. Args: solver (:class:`dwave.cloud.solver.Solver`): The solver used. linear (dict[variable, bias]/list[variable, bias]): Linear terms of the model. quadratic (dict[(variable, variable), bias]): Quadratic terms of the model. Returns: encoded submission dictionary """ active = active_qubits(linear, quadratic) # Encode linear terms. The coefficients of the linear terms of the objective # are encoded as an array of little endian 64 bit doubles. # This array is then base64 encoded into a string safe for json. # The order of the terms is determined by the _encoding_qubits property # specified by the server. # Note: only active qubits are coded with double, inactive with NaN nan = float('nan') lin = [ uniform_get(linear, qubit, 0 if qubit in active else nan) for qubit in solver._encoding_qubits ] lin = base64.b64encode(struct.pack('<' + ('d' * len(lin)), *lin)) # Encode the coefficients of the quadratic terms of the objective # in the same manner as the linear terms, in the order given by the # _encoding_couplers property, discarding tailing zero couplings quad = [ quadratic.get((q1, q2), 0) + quadratic.get((q2, q1), 0) for (q1, q2) in solver._encoding_couplers if q1 in active and q2 in active ] quad = base64.b64encode(struct.pack('<' + ('d' * len(quad)), *quad)) # The name for this encoding is 'qp' and is explicitly included in the # message for easier extension in the future. return { 'format': 'qp', 'lin': lin.decode('utf-8'), 'quad': quad.decode('utf-8') }
def test_active_qubits_list(self): self.assertEqual(active_qubits([], {}), set()) self.assertEqual(active_qubits([2], {}), {0}) self.assertEqual(active_qubits([2, 2, 0], {}), {0, 1, 2}) self.assertEqual(active_qubits([], {(0, 1): 0}), {0, 1}) self.assertEqual(active_qubits([0, 0], {(0, 2): 0}), {0, 1, 2})
def from_qmi_response(problem, response, embedding_context=None, warnings=None, params=None, sampleset=None): """Construct problem data for visualization based on the low-level sampling problem definition and the low-level response. Args: problem ((list/dict, dict[(int, int), float]) or dict[(int, int), float]): Problem in Ising or QUBO form, conforming to solver graph. Note: if problem is given as tuple, it is assumed to be in Ising variable space, and if given as a dict, Binary variable space is assumed. Zero energy offset is always implied. response (:class:`dwave.cloud.computation.Future`): Sampling response, as returned by the low-level sampling interface in the Cloud Client (e.g. :meth:`dwave.cloud.Solver.sample_ising` for Ising problems). embedding_context (dict, optional): A map containing an embedding of logical problem onto the solver's graph (the ``embedding`` key) and embedding parameters used (e.g. ``chain_strength``). warnings (list[dict], optional): Optional list of warnings. params (dict, optional): Sampling parameters used. sampleset (:class:`dimod.SampleSet`, optional): Optional unembedded sampleset. """ logger.debug("from_qmi_response({!r})".format( dict(problem=problem, response=response, response_energies=response['energies'], embedding_context=embedding_context, warnings=warnings, params=params, sampleset=sampleset))) try: linear, quadratic = problem except: linear, quadratic = reformat_qubo_as_ising(problem) # make sure lin/quad are not dimod views (that handle directed edges) if isinstance(linear, BQMView): linear = dict(linear) if isinstance(quadratic, BQMView): quadratic = dict(quadratic) solver = response.solver if not isinstance(response.solver, StructuredSolver): raise TypeError("only structured solvers are supported") topology = _get_solver_topology(solver) if topology['type'] not in SUPPORTED_SOLVER_TOPOLOGY_TYPES: raise TypeError("unsupported solver topology type") solver_id = solver.id problem_type = response.problem_type variables = list(response.variables) active = active_qubits(linear, quadratic) # filter out invalid values (user's error in problem definition), since # SAPI ignores them too active = {q for q in active if q in solver.variables} # sanity check active_variables = response['active_variables'] assert set(active) == set(active_variables) solutions = list(map(itemsgetter(*active_variables), response['solutions'])) energies = response['energies'] num_occurrences = response.num_occurrences num_variables = solver.num_qubits timing = response.timing # note: we can't use encode_problem_as_qp(solver, linear, quadratic) because # visualizer accepts decoded lists (and nulls instead of NaNs) problem_data = { "format": "qp", # SAPI non-conforming (nulls vs nans) "lin": [ uniform_get(linear, v, 0 if v in active else None) for v in solver._encoding_qubits ], "quad": [ quadratic.get((q1, q2), 0) + quadratic.get((q2, q1), 0) for (q1, q2) in solver._encoding_couplers if q1 in active and q2 in active ] } # include optional embedding if embedding_context is not None and 'embedding' in embedding_context: problem_data['embedding'] = embedding_context['embedding'] # try to reconstruct sampling params if params is None: params = {'num_reads': int(sum(num_occurrences))} # expand with defaults params = _expand_params(solver, params, timing) # construct problem stats problem_stats = _problem_stats(response=response, sampleset=sampleset, embedding_context=embedding_context) data = { "ready": True, "details": _details_dict(response), "data": _problem_dict(solver_id, problem_type, problem_data, params, problem_stats), "answer": _answer_dict(solutions, active_variables, energies, num_occurrences, timing, num_variables), "warnings": _warnings(warnings), "rel": dict(solver=solver), } if sampleset is not None: data["unembedded_answer"] = _unembedded_answer_dict(sampleset) logger.trace("from_qmi_response returned %r", data) return data
def test_active_qubits_dict(self): self.assertEqual(active_qubits({}, {}), set()) self.assertEqual(active_qubits({0: 0}, {}), {0}) self.assertEqual(active_qubits({}, {(0, 1): 0}), {0, 1}) self.assertEqual(active_qubits({2: 0}, {(0, 1): 0}), {0, 1, 2})