def test_qp_request_encodes_offset(self): """Test `offset` is stored in problem data.""" solver = get_structured_solver() linear, quadratic = generate_const_ising_problem(solver, h=1, j=-1) # default offset is zero request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['offset'], 0) # test explicit offset offset = 2.0 request = encode_problem_as_qp(solver, linear, quadratic, offset) self.assertEqual(request['offset'], offset)
def setUpClass(cls): """Configure attributes required (used) by ProblemResourcesBaseTests.""" with Client(**config) as client: cls.token = config['token'] cls.api = Problems.from_client_config(client) # submit and solve an Ising problem as a fixture solver = client.get_solver(qpu=True) cls.solver_id = solver.id edge = next(iter(solver.edges)) cls.linear = {} cls.quadratic = {edge: 1.0} qp = encode_problem_as_qp(solver, cls.linear, cls.quadratic) cls.problem_data = models.ProblemData.parse_obj(qp) cls.problem_type = constants.ProblemType.ISING cls.params = dict(num_reads=100) future = solver.sample_ising(cls.linear, cls.quadratic, **cls.params) resolved = future.result() cls.problem_id = future.id cls.problem_label = future.label # double-check assert future.remote_status == constants.ProblemStatus.COMPLETED.value
def test_qp_request_encoding_all_qubits(self): """Test biases and coupling strengths are properly encoded (base64 little-endian doubles).""" solver = get_structured_solver() linear, quadratic = generate_const_ising_problem(solver, h=1, j=-1) request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') self.assertEqual(request['lin'], self.encode_doubles([1, 1, 1, 1])) self.assertEqual(request['quad'], self.encode_doubles([-1, -1, -1, -1]))
def problem_data(self, solver: StructuredSolver = None, problem: Tuple[dict, dict] = None) -> dict: if solver is None: solver = self.solver if problem is None: problem = self.problem linear, quadratic = problem return encode_problem_as_qp(solver, linear, quadratic)
def test_qp_request_encoding_all_qubits(self): """Test biases and coupling strengths are properly encoded (base64 little-endian doubles).""" solver = get_structured_solver() linear = {index: 1 for index in solver.nodes} quadratic = {key: -1 for key in solver.undirected_edges} request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') self.assertEqual(request['lin'], self.encode_doubles([1, 1, 1, 1])) self.assertEqual(request['quad'], self.encode_doubles([-1, -1, -1, -1]))
def test_qp_request_encoding_undirected_biases(self): """Quadratic terms are correctly encoded when given as undirected biases.""" solver = get_structured_solver() linear = {} quadratic = {(0,3): -1, (3,0): -1} request = encode_problem_as_qp(solver, linear, quadratic, undirected_biases=True) self.assertEqual(request['format'], 'qp') # [0, NaN, NaN, 0] self.assertEqual(request['lin'], self.encode_doubles([0, self.nan, self.nan, 0])) # [-1] self.assertEqual(request['quad'], self.encode_doubles([-1]))
def test_qp_request_encoding_sub_qubits_implicit_couplings(self): """Couplings should be zero for active qubits, if not specified.""" solver = get_structured_solver() linear = {0: 0, 3: 0} quadratic = {} request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') # [0, NaN, NaN, 0] self.assertEqual(request['lin'], self.encode_doubles([0, self.nan, self.nan, 0])) # [0] self.assertEqual(request['quad'], self.encode_doubles([0]))
def test_qp_request_encoding_sub_qubits_implicit_biases(self): """Biases don't have to be specified for qubits to be active.""" solver = get_structured_solver() linear = {} quadratic = {(0,3): -1} request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') # [0, NaN, NaN, 0] self.assertEqual(request['lin'], self.encode_doubles([0, self.nan, self.nan, 0])) # [-1] self.assertEqual(request['quad'], self.encode_doubles([-1]))
def test_qp_request_encoding_missing_qubits(self): """Qubits don't have to be specified with biases only, but also with couplings.""" solver = get_structured_solver() linear = {} quadratic = {(0, 1): -1} request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') # [0, 0, NaN, NaN] self.assertEqual(request['lin'], self.encode_doubles([0, 0, self.nan, self.nan])) # [-1] self.assertEqual(request['quad'], self.encode_doubles([-1]))
def test_qp_request_encoding_sub_qubits(self): """Inactive qubits should be encoded as NaNs. Inactive couplers should be omitted.""" solver = get_structured_solver() linear = {0: 1, 1: 1} quadratic = {(0, 1): -1} request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') # [1, 1, NaN, NaN] self.assertEqual(request['lin'], self.encode_doubles([1, 1, self.nan, self.nan])) # [-1] self.assertEqual(request['quad'], self.encode_doubles([-1]))
def test_qp_request_encoding_sub_qubits(self): """Inactive qubits should be encoded as NaNs. Inactive couplers should be omitted.""" solver = get_structured_solver() linear = {index: 1 for index in sorted(list(solver.nodes))[:2]} quadratic = { key: -1 for key in sorted(list(solver.undirected_edges))[:1] } request = encode_problem_as_qp(solver, linear, quadratic) self.assertEqual(request['format'], 'qp') # [1, 1, NaN, NaN] self.assertEqual(request['lin'], self.encode_doubles([1, 1, self.nan, self.nan])) # [-1] self.assertEqual(request['quad'], self.encode_doubles([-1]))
def _sample(self, type_, linear, quadratic, params): """Internal method for `sample_ising` and `sample_qubo`. Args: linear (list/dict): Linear terms of the model. quadratic (dict[(int, int), float]): Quadratic terms of the model. **params: Parameters for the sampling method, solver-specific. Returns: :class:`Future` """ # Check the problem if not self.check_problem(linear, quadratic): raise InvalidProblemError("Problem graph incompatible with solver.") # Mix the new parameters with the default parameters combined_params = dict(self._params) combined_params.update(params) # Check the parameters before submitting for key in combined_params: if key not in self.parameters and not key.startswith('x_'): raise KeyError("{} is not a parameter of this solver.".format(key)) # transform some of the parameters in-place self._format_params(type_, combined_params) body = json.dumps({ 'solver': self.id, 'data': encode_problem_as_qp(self, linear, quadratic), 'type': type_, 'params': combined_params }) logger.trace("Encoded sample request: %s", body) future = Future(solver=self, id_=None, return_matrix=self.return_matrix) logger.debug("Submitting new problem to: %s", self.id) self.client._submit(body, future) return future
def _sample(self, type_, linear, quadratic, offset, params, label=None, undirected_biases=False): """Internal method for `sample_ising`, `sample_qubo` and `sample_bqm`. Args: linear (list/dict): Linear terms of the model. quadratic (dict[(int, int), float]): Quadratic terms of the model. offset (number): Constant offset applied to the model. params (dict): Parameters for the sampling method, solver-specific. label (str, optional): Problem label. undirected_biases (boolean, default=False): Are (quadratic) biases specified on undirected edges? For triangular or symmetric matrix of quadratic biases set it to ``True``. Returns: :class:`~dwave.cloud.computation.Future` """ # Check the problem if not self.check_problem(linear, quadratic): raise ProblemStructureError( f"Problem graph incompatible with {self.id} solver") # Mix the new parameters with the default parameters combined_params = dict(self._params) combined_params.update(params) # Check the parameters before submitting for key in combined_params: if key not in self.parameters and not key.startswith('x_'): raise KeyError("{} is not a parameter of this solver.".format(key)) # transform some of the parameters in-place self._format_params(type_, combined_params) body_dict = { 'solver': self.id, 'data': encode_problem_as_qp(self, linear, quadratic, offset, undirected_biases=undirected_biases), 'type': type_, 'params': combined_params } if label is not None: body_dict['label'] = label body_data = json.dumps(body_dict) logger.trace("Encoded sample request: %s", body_data) body = Present(result=body_data) computation = Future(solver=self, id_=None, return_matrix=self.return_matrix) # XXX: offset is carried on Future until implemented in SAPI computation._offset = offset logger.debug("Submitting new problem to: %s", self.id) self.client._submit(body, computation) return computation
def _sample(self, type_, linear, quadratic, params, undirected_biases=False): """Internal method for `sample_ising`, `sample_qubo` and `sample_bqm`. Args: linear (list/dict): Linear terms of the model. quadratic (dict[(int, int), float]): Quadratic terms of the model. params (dict): Parameters for the sampling method, solver-specific. undirected_biases (boolean, default=False): Are (quadratic) biases specified on undirected edges? For triangular or symmetric matrix of quadratic biases set it to ``True``. Returns: :class:`Future` """ args = dict(type_=type_, linear=linear, quadratic=quadratic, params=params) dispatch_event('before_sample', obj=self, args=args) # Check the problem if not self.check_problem(linear, quadratic): raise InvalidProblemError( "Problem graph incompatible with solver.") # Mix the new parameters with the default parameters combined_params = dict(self._params) combined_params.update(params) # Check the parameters before submitting for key in combined_params: if key not in self.parameters and not key.startswith('x_'): raise KeyError( "{} is not a parameter of this solver.".format(key)) # transform some of the parameters in-place self._format_params(type_, combined_params) body_data = json.dumps({ 'solver': self.id, 'data': encode_problem_as_qp(self, linear, quadratic, undirected_biases=undirected_biases), 'type': type_, 'params': combined_params }) logger.trace("Encoded sample request: %s", body_data) body = Present(result=body_data) computation = Future(solver=self, id_=None, return_matrix=self.return_matrix) logger.debug("Submitting new problem to: %s", self.id) self.client._submit(body, computation) dispatch_event('after_sample', obj=self, args=args, return_value=computation) return computation