def test_submit_continue_then_ok_reply(self): """Handle polling for a complete problem.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, {'problems/': [self.sapi.continue_reply(id='123')]}) session.get = lambda a: choose_reply( a, { 'problems/?id=123': [self.sapi.complete_no_answer_reply(id='123')], 'problems/123/': self.sapi.complete_reply(id='123') }) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem params = dict(num_reads=100) results = solver.sample_ising(linear, quadratic, **params) self._check(results, linear, quadratic, **params)
def test_submit_immediate_error_reply(self): """Handle an (obvious) error on problem submission.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, { 'problems/': [ self.sapi.immediate_error_reply( code=400, msg="Missing parameter 'num_reads' in problem JSON" ) ] }) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem results = solver.sample_ising(linear, quadratic) with self.assertRaises(SolverFailureError): results.samples
def test_submit_offset_answer_does_not_include_it(self): """Handle a normal query with offset and response that doesn't include it.""" # ising problem energy offset offset = 3 # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': [self.sapi.complete_no_answer_reply(id='123')] }) session.get = lambda a: choose_reply( a, {'problems/123/': self.sapi.complete_reply(id='123')}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem params = dict(num_reads=100) results = solver.sample_ising(linear, quadratic, offset, **params) # although SAPI response doesn't include offset, Future should patch it on-the-fly self._check(results, linear, quadratic, offset=offset, **params)
def test_immediate_polling_with_local_clock_unsynced(self): """First poll happens with minimal delay if local clock is way off from the remote/server clock.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): badnow = utcrel(100) session = mock.Mock() session.post = lambda path, _: choose_reply( path, {'problems/': [self.sapi.continue_reply(id='1')]}, date=badnow) session.get = lambda path: choose_reply(path, { 'problems/?id=1': [self.sapi.complete_no_answer_reply(id='1')], 'problems/1/': self.sapi.complete_reply(id='1') }, date=badnow) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) def assert_no_delay(s): s and self.assertTrue( abs(s - client.poll_backoff_min) < client.DEFAULTS['poll_backoff_min']) with mock.patch('time.sleep', assert_no_delay): future = solver.sample_qubo({}) future.result()
def test_immediate_polling(self): "First poll happens with minimal delay" # each thread can have its instance of a session because # responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda path, _: choose_reply( path, {'problems/': [self.sapi.continue_reply(id='1')]}) session.get = lambda path: choose_reply( path, { 'problems/?id=1': [self.sapi.complete_no_answer_reply(id='1')], 'problems/1/': self.sapi.complete_reply(id='1') }) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client(endpoint='endpoint', token='token') as client: solver = Solver(client, self.sapi.solver.data) def assert_no_delay(s): s and self.assertTrue( abs(s - client.poll_backoff_min) < client.DEFAULTS['poll_backoff_min']) with mock.patch('time.sleep', assert_no_delay): future = solver.sample_qubo({}) future.result()
def test_deprecations(self): """Proper deprecation warnings are raised.""" def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': [self.sapi.complete_no_answer_reply(id='123')] }) session.get = lambda a: choose_reply( a, {'problems/123/': self.sapi.complete_reply(id='123')}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem params = dict(num_reads=100) results = solver.sample_ising(linear, quadratic, **params) # aliased keys are deprecated in 0.8.0 with self.assertWarns(DeprecationWarning): results['samples'] with self.assertWarns(DeprecationWarning): results['occurrences'] # .error is deprecated in 0.7.x, scheduled for removal in 0.9.0 with self.assertWarns(DeprecationWarning): results.error # .occurrences is deprecated in 0.8.0, scheduled for removal in 0.10.0+ with self.assertWarns(DeprecationWarning): results.occurrences
def setUp(self): # mock client self.client = Client(token='token', solver={'qpu': True}) self.client._fetch_solvers = lambda **kw: self.solvers self.client._submit = lambda *pa, **kw: None # mock solvers self.solver = Solver(client=self.client, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2], "couplers": [[0, 1], [0, 2], [1, 2]], "num_qubits": 3, "num_reads_range": [0, 100], "parameters": { "num_reads": "Number of samples to return.", "postprocess": "either 'sampling' or 'optimization'" }, "topology": { "type": "chimera", "shape": [16, 16, 4] }, "category": "qpu", "tags": ["lower_noise"] }, "id": "solver1", "description": "A test solver 1", "status": "online" }) self.solvers = [self.solver]
def test_submit_bqm_ising_ok_reply(self): """Handle a normal query and response.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': [self.sapi.complete_no_answer_reply(id='123')] }) session.get = lambda a: choose_reply( a, {'problems/123/': self.sapi.complete_reply(id='123')}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) h, J = self.sapi.problem bqm = dimod.BinaryQuadraticModel.from_ising(h, J) params = dict(num_reads=100) results = solver.sample_bqm(bqm, **params) self._check(results, h, J, **params)
def test_answer_load_error(self): """Answer load error is propagated as exception.""" error_code = 404 error_message = 'Problem not found' # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda path, _: choose_reply( path, {'problems/': [self.sapi.complete_no_answer_reply(id='123')]}) session.get = lambda path: choose_reply( path, replies={'problems/123/': error_message}, statuses={'problems/123/': iter([error_code])}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem future = solver.sample_ising(linear, quadratic) with self.assertRaises(SolverError) as exc: future.result() self.assertEqual(str(exc.exception), error_message)
def setUp(self): # mock solvers self.solver1 = Solver(client=None, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2], "couplers": [[0, 1], [0, 2], [1, 2]], "num_qubits": 3, "num_reads_range": [0, 100], "parameters": { "num_reads": "Number of samples to return.", "postprocess": "either 'sampling' or 'optimization'" } }, "id": "solver1", "description": "A test solver 1", "status": "online" }) self.solver2 = Solver(client=None, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2, 3, 4], "couplers": [[0, 1], [0, 2], [1, 2], [2, 3], [3, 4]], "num_qubits": 5, "num_reads_range": [0, 200], "parameters": { "num_reads": "Number of samples to return.", "flux_biases": "Supported ...", "anneal_schedule": "Supported ..." }, "vfyc": True }, "id": "solver2", "description": "A test solver 2" }) self.solver3 = Solver(client=None, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1], "couplers": [[0, 1]], "num_qubits": 7, "num_reads_range": [0, 1000], "parameters": {"num_reads": "Number of samples to return."}, "vfyc": False, # the following are only present in this solver "some_set": [1, 2], "some_range": [1, 2], "some_string": "x" }, "id": "c4-sw_solver3", "description": "A test of software solver", "avg_load": 0.7 }) self.solvers = [self.solver1, self.solver2, self.solver3] # mock client self.client = Client('endpoint', 'token') self.client._fetch_solvers = lambda **kw: self.solvers
def test_polling_recovery_after_5xx(self): "Polling shouldn't be aborted on 5xx responses." # we need a "global session", because mocked responses are stateful def global_mock_session(): session = mock.Mock() # on submit, return status pending session.post = lambda path, _: choose_reply( path, {'problems/': [self.sapi.continue_reply(id='123')]}) # on first and second status poll, fail with 503 and 504 # on third status poll, return completed statuses = iter([503, 504]) def continue_then_complete(path, state={'count': 0}): state['count'] += 1 if state['count'] < 3: return choose_reply( path, replies={ 'problems/?id=123': [self.sapi.continue_reply(id='123')], 'problems/123/': self.sapi.continue_reply(id='123') }, statuses={ 'problems/?id=123': statuses, 'problems/123/': statuses }) else: return choose_reply( path, { 'problems/?id=123': [self.sapi.complete_no_answer_reply(id='123')], 'problems/123/': self.sapi.complete_reply(id='123') }) session.get = continue_then_complete return session session = global_mock_session() with mock.patch.object(Client, 'create_session', lambda self: session): with Client(endpoint='endpoint', token='token') as client: solver = Solver(client, self.sapi.solver.data) future = solver.sample_qubo({}) future.result() # after third poll, back-off interval should be 4 x initial back-off self.assertAlmostEqual( future._poll_backoff, client.poll_backoff_min * client.poll_backoff_base**2)
def setUp(self): # mock solvers self.solver1 = Solver(client=None, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2], "couplers": [[0, 1], [0, 2], [1, 2]], "num_qubits": 3, "parameters": { "num_reads": "Number of samples to return." } }, "id": "solver1", "description": "A test solver 1", "status": "online" }) self.solver2 = Solver(client=None, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2, 3, 4], "couplers": [[0, 1], [0, 2], [1, 2], [2, 3], [3, 4]], "num_qubits": 5, "parameters": { "num_reads": "Number of samples to return.", "flux_biases": "Supported ..." }, "vfyc": True }, "id": "solver2", "description": "A test solver 2" }) self.solvers = [self.solver1, self.solver2] # mock client self.client = Client('endpoint', 'token') self.client._solvers = { self.solver1.id: self.solver1, self.solver2.id: self.solver2 } self.client._all_solvers_ready = True
def test_exponential_backoff_polling(self): "After each poll, back-off should double" # we need a "global session", because mocked responses are stateful def global_mock_session(): session = mock.Mock() # on submit, return status pending session.post = lambda path, _: choose_reply( path, {'problems/': [self.sapi.continue_reply(id='123')]}) # on first and second status poll, return pending # on third status poll, return completed def continue_then_complete(path, state={'count': 0}): state['count'] += 1 if state['count'] < 3: return choose_reply( path, { 'problems/?id=123': [self.sapi.continue_reply(id='123')], 'problems/123/': self.sapi.continue_reply(id='123') }) else: return choose_reply( path, { 'problems/?id=123': [self.sapi.complete_no_answer_reply(id='123')], 'problems/123/': self.sapi.complete_reply(id='123') }) session.get = continue_then_complete return session session = global_mock_session() with mock.patch.object(Client, 'create_session', lambda self: session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) future = solver.sample_qubo({}) future.result() # after third poll, back-off interval should be 4 x initial back-off self.assertEqual(future._poll_backoff, client.poll_backoff_min * 2**2)
def test_cancel_without_id(self): """Make sure the cancel method submits to the right endpoint. When cancel is called before the submission has returned the problem id. """ submission_id = 'test-id' release_reply = threading.Event() # each thread can have its instance of a session because # we use a global lock (event) in the mocked responses def create_mock_session(client): reply_body = [self.sapi.continue_reply(id=submission_id)] session = mock.Mock() session.get = lambda a: choose_reply( a, {'problems/?id={}'.format(submission_id): reply_body}) def post(a, _): release_reply.wait() return choose_reply(a, {'problems/': reply_body}) session.post = post session.delete = DeleteEvent.handle return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem future = solver.sample_ising(linear, quadratic) future.cancel() try: release_reply.set() future.samples self.fail() except DeleteEvent as event: if event.url == 'problems/': self.assertEqual(event.body, '["{}"]'.format(submission_id)) else: self.assertEqual(event.url, 'problems/{}/'.format(submission_id))
def test_submit_null_reply(self): """Get an error when the server's response is incomplete.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, {'problems/': ''}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem results = solver.sample_ising(linear, quadratic) with self.assertRaises(InvalidAPIResponseError): results.samples
def test_solver_feature_properties(self): self.assertTrue(solver_object('solver', 'qpu').qpu) self.assertTrue(solver_object('solver', 'QPU').qpu) self.assertFalse(solver_object('solver', 'qpu').software) self.assertFalse(solver_object('solver', 'qpu').hybrid) self.assertFalse(solver_object('solver', 'software').qpu) self.assertTrue(solver_object('solver', 'software').software) self.assertFalse(solver_object('solver', 'software').hybrid) self.assertTrue(solver_object('solver', 'hybrid').hybrid) self.assertFalse(solver_object('solver', 'hybrid').qpu) self.assertFalse(solver_object('solver', 'hybrid').software) self.assertFalse(solver_object('solver').is_vfyc) self.assertEqual(solver_object('solver').num_qubits, 3) self.assertFalse(solver_object('solver').has_flux_biases) # test .num_qubits vs .num_actual_qubits data = structured_solver_data('test') data['properties']['num_qubits'] = 7 solver = Solver(None, data) self.assertEqual(solver.num_qubits, 7) self.assertEqual(solver.num_active_qubits, 3) # test .is_vfyc data = structured_solver_data('test') data['properties']['vfyc'] = 'error' self.assertFalse(Solver(None, data).is_vfyc) data['properties']['vfyc'] = True self.assertTrue(Solver(None, data).is_vfyc) # test .has_flux_biases self.assertFalse(Solver(None, data).has_flux_biases) data['properties']['parameters']['flux_biases'] = '...' self.assertTrue(Solver(None, data).has_flux_biases) # test .has_anneal_schedule self.assertFalse(Solver(None, data).has_anneal_schedule) data['properties']['parameters']['anneal_schedule'] = '...' self.assertTrue(Solver(None, data).has_anneal_schedule) # test `.online` property self.assertTrue(solver_object('solver').online) data = structured_solver_data('test') data['status'] = 'offline' self.assertFalse(Solver(None, data).online) del data['status'] self.assertTrue(Solver(None, data).online)
def test_submit_cancel_reply(self): """Handle a response for a canceled job.""" # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, {'problems/': [self.sapi.cancel_reply()]}) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem results = solver.sample_ising(linear, quadratic) with self.assertRaises(CanceledFutureError): results.samples
def test_submit_bqm_qubo_ok_reply(self): """Handle a normal query and response.""" qubo_msg_diff = dict(type="qubo") qubo_answer_diff = { 'energies': 'AAAAAAAAAAA=', 'solutions': 'AA==', 'active_variables': 'AAAAAAQAAAA=' } # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': [self.sapi.complete_no_answer_reply(id='123')] }) session.get = lambda a: choose_reply( a, { 'problems/123/': self.sapi.complete_reply(id='123', answer_patch=qubo_answer_diff, **qubo_msg_diff) }) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) qubo = {(0, 0): 4.0, (0, 4): -4, (4, 4): 4.0} offset = -2.0 params = dict(num_reads=100) results = solver.sample_qubo(qubo, offset, **params) # make sure energies are correct in raw results for energy, sample in zip(results.energies, results.samples): self.assertEqual( energy, evaluate_ising({}, qubo, sample, offset=offset))
def test_id_integration(self): """Problem ID getter blocks correctly when ID set by the client.""" submission_id = 'test-id' solver_name = 'solver-id' release_reply = threading.Event() # each thread can have its instance of a session because # we use a global lock (event) in the mocked responses def create_mock_session(client): session = mock.Mock() # delayed submit; emulates waiting in queue def post(path, _): release_reply.wait() reply_body = self.sapi.complete_reply(id=submission_id, solver=solver_name) return choose_reply(path, {'problems/': [reply_body]}) session.post = post return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem future = solver.sample_ising(linear, quadratic) # initially, the id is not available with self.assertRaises(TimeoutError): future.wait_id(timeout=1) # release the mocked sapi reply with the id release_reply.set() # verify the id is now available self.assertEqual(future.wait_id(), submission_id)
def get_solvers(self, refresh=False): """List all the solvers this client can provide, and load the data about the solvers. This is a blocking web call to `{endpoint}/solvers/remote/`` that caches the result and populates a list of available solvers described through :class:`.Solver` instances. To submit a sampling problem to the D-Wave API, filter the list returned and execute a ``sampling_*`` method on the solver of interest. Alternatively, if you know the solver name (or it's defined in config), use the :meth:`.get_solver` method. Args: refresh (bool, default=False): By default, ``get_solvers`` caches the list of solvers it receives from the API. Use this parameter to force refresh. Returns: dict[id, solver]: a mapping of solver name/id to :class:`.Solver` """ with self._solvers_lock: if self._all_solvers_ready and not refresh: return self._solvers _LOGGER.debug("Requesting list of all solver data.") response = self.session.get( posixpath.join(self.endpoint, 'solvers/remote/')) if response.status_code == 401: raise SolverAuthenticationError response.raise_for_status() _LOGGER.debug("Received list of all solver data.") data = response.json() for solver_desc in data: try: solver = Solver(self, solver_desc) if self.is_solver_handled(solver): self._solvers[solver.id] = solver _LOGGER.debug("Adding solver %r", solver) else: _LOGGER.debug( "Skipping solver %r inappropriate for client", solver) except UnsupportedSolverError as e: _LOGGER.debug("Skipping solver due to %r", e) self._all_solvers_ready = True return self._solvers
def test_submit_offset_wrong_offset_in_answer(self): """Energy levels don't match because offset in answer is respected, even if wrong""" # ising problem energy offset offset = 3 answer_offset = 2 * offset # make it wrong # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply(a, { 'problems/': [self.sapi.complete_no_answer_reply(id='123')] }) session.get = lambda a: choose_reply( a, { 'problems/123/': self.sapi.complete_reply( id='123', answer_patch=dict(offset=answer_offset)) }) return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem params = dict(num_reads=100) results = solver.sample_ising(linear, quadratic, offset, **params) # since SAPI response includes offset, Future shouldn't patch it; # but because the offset in answer is wrong, energies are off with self.assertRaises(AssertionError): self._check(results, linear, quadratic, offset=offset, **params)
def get_solver(self, name=None, refresh=False): """Load the configuration for a single solver, as publicized by the API on ``{endpoint}/solvers/remote/{solver_name}/``. This is a blocking web call that returns a :class:`.Solver` instance, which in turn can be used to submit sampling problems to the D-Wave API and fetch the results. Args: name (str): Id of the requested solver. ``None`` will return the default solver. refresh (bool): Return solver from cache (if cached with ``get_solvers()``), unless set to ``True``. Returns: :class:`.Solver` """ _LOGGER.debug("Looking for solver: %s", name) if name is None: if self.default_solver: name = self.default_solver else: raise ValueError( "No name or default name provided when loading solver.") with self._solvers_lock: if refresh or name not in self._solvers: response = self.session.get( posixpath.join(self.endpoint, 'solvers/remote/{}/'.format(name))) if response.status_code == 401: raise SolverAuthenticationError if response.status_code == 404: raise KeyError( "No solver with the name {} was available".format( name)) response.raise_for_status() solver = Solver(self, data=response.json()) if solver.id != name: raise InvalidAPIResponseError( "Asked for solver named {!r}, got {!r}".format( name, solver.id)) self._solvers[name] = solver return self._solvers[name]
def test_cancel_with_id(self): """Make sure the cancel method submits to the right endpoint. When cancel is called after the submission is finished. """ submission_id = 'test-id' # each thread can have its instance of a session because # the mocked responses are stateless def create_mock_session(client): session = mock.Mock() reply_body = [ self.sapi.continue_reply(id=submission_id, solver='solver') ] session.get = lambda a: choose_reply( a, {'problems/?id={}'.format(submission_id): reply_body}) session.delete = DeleteEvent.handle return session with mock.patch.object(Client, 'create_session', create_mock_session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) future = solver._retrieve_problem(submission_id) future.cancel() try: self.assertTrue(future.id is not None) future.samples self.fail() except DeleteEvent as event: if event.url == 'problems/': self.assertEqual(event.body, '["{}"]'.format(submission_id)) else: self.assertEqual(event.url, 'problems/{}/'.format(submission_id))
def get_solver(self, name=None, refresh=False): """Load the configuration for a single solver. To get specific solver data: ``GET /solvers/remote/{solver_name}/`` Args: name (str): Id of the requested solver. `None` will return the default solver. refresh (bool): Return solver from cache (if cached with ``get_solvers()``), unless set to `True`. Returns: :class:`.Solver` """ _LOGGER.debug("Looking for solver: %s", name) if name is None: if self.default_solver is not None: name = self.default_solver else: raise ValueError( "No name or default name provided when loading solver.") with self._solvers_lock: if refresh or name not in self._solvers: response = self.session.get( posixpath.join(self.endpoint, 'solvers/remote/{}/'.format(name))) if response.status_code == 401: raise SolverAuthenticationError if response.status_code == 404: raise KeyError( "No solver with the name {} was available".format( name)) response.raise_for_status() solver = Solver(self, data=response.json()) if solver.id != name: raise InvalidAPIResponseError( "Asked for solver named {!r}, got {!r}".format( name, solver.id)) self._solvers[name] = solver return self._solvers[name]
def test_label_is_sent(self, name, label): """Problem label is set on problem submit.""" with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) problems = self.generate_sample_problems(solver) for method_name, problem_args in problems: with self.subTest(method_name=method_name): sample = getattr(solver, method_name) with mock.patch.object( Client, '_submit', self.on_submit_label_verifier(label)): with self.assertRaises(self.PrimaryAssertionSatisfied): sample(*problem_args, label=label).result()
def get_solvers(self, refresh=False): """List all the solvers this connection can provide, and load the data about the solvers. To get all solver data: ``GET /solvers/remote/`` Args: refresh (bool, default=False): By default, ``get_solvers`` caches the list of solvers it receives from the API. Use this parameter to force refresh. Returns: dict[id, solver]: a mapping of solver name/id to :class:`.Solver` """ with self._solvers_lock: if self._all_solvers_ready and not refresh: return self._solvers _LOGGER.debug("Requesting list of all solver data.") response = self.session.get( posixpath.join(self.endpoint, 'solvers/remote/')) if response.status_code == 401: raise SolverAuthenticationError response.raise_for_status() _LOGGER.debug("Received list of all solver data.") data = response.json() for solver_desc in data: try: solver = Solver(self, solver_desc) if self.is_solver_handled(solver): self._solvers[solver.id] = solver _LOGGER.debug("Adding solver %r", solver) else: _LOGGER.debug( "Skipping solver %r inappropriate for client", solver) except UnsupportedSolverError as e: _LOGGER.debug("Skipping solver due to %r", e) self._all_solvers_ready = True return self._solvers
def test_label_is_received(self, name, label): """Problem label is set from response in result/sampleset.""" def make_session_generator(label): def create_mock_session(client): session = mock.Mock() session.post = lambda a, _: choose_reply( a, { 'problems/': [ self.sapi.complete_no_answer_reply(id='123', label=None) ] }) session.get = lambda a: choose_reply( a, { 'problems/123/': self.sapi.complete_reply(id='123', label=label) }) return session return create_mock_session with mock.patch.object(Client, 'create_session', make_session_generator(label)): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) problems = self.generate_sample_problems(solver) for method_name, problem_args in problems: with self.subTest(method_name=method_name): sample = getattr(solver, method_name) future = sample(*problem_args, label=label) future.result() # ensure future is resolved self.assertEqual(future.label, label) # sampleset will only be available if dimod is installed if dimod: info = future.sampleset.info self.assertEqual(info.get('problem_label'), label)
class TestEventDispatch(unittest.TestCase): def setUp(self): # mock client self.client = Client(token='token', solver={'qpu': True}) self.client._fetch_solvers = lambda **kw: self.solvers self.client._submit = lambda *pa, **kw: None # mock solvers self.solver = Solver(client=self.client, data={ "properties": { "supported_problem_types": ["qubo", "ising"], "qubits": [0, 1, 2], "couplers": [[0, 1], [0, 2], [1, 2]], "num_qubits": 3, "num_reads_range": [0, 100], "parameters": { "num_reads": "Number of samples to return.", "postprocess": "either 'sampling' or 'optimization'" }, "topology": { "type": "chimera", "shape": [16, 16, 4] }, "category": "qpu", "tags": ["lower_noise"] }, "id": "solver1", "description": "A test solver 1", "status": "online" }) self.solvers = [self.solver] def test_validation(self): """Event name and handler are validated.""" with self.assertRaises(ValueError): add_handler('invalid_event_name', lambda: None) with self.assertRaises(TypeError): add_handler('before_client_init', None) def test_client_init(self): """Before/After client init events are dispatched with correct signatures.""" # setup event handlers memo = {} def handler(event, **data): memo[event] = data add_handler('before_client_init', handler) add_handler('after_client_init', handler) # client init client = Client(token='token', unknown='unknown') # test entry values before = memo['before_client_init'] self.assertEqual(before['obj'], client) self.assertEqual(before['args']['endpoint'], None) self.assertEqual(before['args']['token'], 'token') self.assertEqual(before['args']['kwargs']['unknown'], 'unknown') # test exit values after = memo['after_client_init'] self.assertEqual(after['obj'], client) self.assertEqual(after['args']['token'], 'token') self.assertEqual(after['args']['kwargs']['unknown'], 'unknown') self.assertEqual(after['return_value'], None) def test_get_solvers(self): """Before/After get_solvers events are dispatched with correct signatures.""" # setup event handlers memo = {} def handler(event, **data): memo[event] = data add_handler('before_get_solvers', handler) add_handler('after_get_solvers', handler) # get solver(s) self.client.get_solver() # test entry values before = memo['before_get_solvers'] self.assertEqual(before['obj'], self.client) self.assertIn('refresh', before['args']) self.assertIn('filters', before['args']) self.assertIn('qpu', before['args']['filters']) # test exit values after = memo['after_get_solvers'] self.assertEqual(after['obj'], self.client) self.assertIn('qpu', after['args']['filters']) self.assertEqual(after['return_value'], self.solvers) def test_sample(self): """Before/After solver sample events are dispatched with correct signatures.""" # setup event handlers memo = {} def handler(event, **data): memo[event] = data add_handler('before_sample', handler) add_handler('after_sample', handler) # sample lin = {0: 1} quad = {(0, 1): 1} offset = 2 params = dict(num_reads=100) future = self.solver.sample_ising(lin, quad, offset, **params) # test entry values before = memo['before_sample'] args = dict(type_='ising', linear=lin, quadratic=quad, offset=offset, params=params, undirected_biases=False) self.assertEqual(before['obj'], self.solver) self.assertDictEqual(before['args'], args) # test exit values after = memo['after_sample'] self.assertEqual(after['obj'], self.solver) self.assertDictEqual(after['args'], args) self.assertEqual(after['return_value'], future)
def solver_object(id_, cat='qpu', incomplete=False): return Solver(client=None, data=structured_solver_data(id_, cat, incomplete))
def test_submit_continue_then_ok_and_error_reply(self): """Handle polling for the status of multiple problems.""" # we need a "global session", because mocked responses are stateful def global_mock_session(): session = mock.Mock() # on first status poll, return pending for both problems # on second status poll, return error for first problem and complete for second def continue_then_complete(path, state={'count': 0}): state['count'] += 1 if state['count'] < 2: return choose_reply( path, { 'problems/?id=1': [self.sapi.continue_reply(id='1')], 'problems/?id=2': [self.sapi.continue_reply(id='2')], 'problems/1/': self.sapi.continue_reply(id='1'), 'problems/2/': self.sapi.continue_reply(id='2'), 'problems/?id=1,2': [ self.sapi.continue_reply(id='1'), self.sapi.continue_reply(id='2') ], 'problems/?id=2,1': [ self.sapi.continue_reply(id='2'), self.sapi.continue_reply(id='1') ] }) else: return choose_reply( path, { 'problems/?id=1': [self.sapi.error_reply(id='1')], 'problems/?id=2': [self.sapi.complete_no_answer_reply(id='2')], 'problems/1/': self.sapi.error_reply(id='1'), 'problems/2/': self.sapi.complete_reply(id='2'), 'problems/?id=1,2': [ self.sapi.error_reply(id='1'), self.sapi.complete_no_answer_reply(id='2') ], 'problems/?id=2,1': [ self.sapi.complete_no_answer_reply(id='2'), self.sapi.error_reply(id='1') ] }) def accept_problems_with_continue_reply(path, body, ids=iter('12')): problems = json.loads(body) return choose_reply( path, { 'problems/': [ self.sapi.continue_reply(id=next(ids)) for _ in problems ] }) session.get = continue_then_complete session.post = accept_problems_with_continue_reply return session session = global_mock_session() with mock.patch.object(Client, 'create_session', lambda self: session): with Client('endpoint', 'token') as client: solver = Solver(client, self.sapi.solver.data) linear, quadratic = self.sapi.problem params = dict(num_reads=100) results1 = solver.sample_ising(linear, quadratic, **params) results2 = solver.sample_ising(linear, quadratic, **params) with self.assertRaises(SolverFailureError): self._check(results1, linear, quadratic, **params) self._check(results2, linear, quadratic, **params)