def test_retrieve_job_set(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') tags = ['test_retrieve_job_set'] circs_counts = [3, 4] for count in circs_counts: with self.subTest(count=count): circs = [] for i in range(count): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2, job_tags=tags) self.assertEqual(job_set.tags(), tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjob_set = IBMQJobManager().retrieve_job_set( job_set_id=job_set.job_set_id(), provider=provider) self.assertEqual({ job.job_id() for job in job_set.jobs() }, { rjob.job_id() for rjob in rjob_set.jobs() }, "Unexpected jobs retrieved. Job set id used was {}.".format( job_set.job_set_id())) self.assertEqual(rjob_set.tags(), job_set.tags()) self.assertEqual(len(rjob_set.qobjs()), len(job_set.qobjs())) self.log.info("Job set report:\n%s", rjob_set.report()) mjobs = job_set.managed_jobs() for index, rmjob in enumerate(rjob_set.managed_jobs()): mjob = mjobs[index] self.assertEqual(rmjob.start_index, mjob.start_index) self.assertEqual(rmjob.end_index, mjob.end_index) for exp_index, exp in enumerate( rmjob.job.qobj().experiments): self.assertEqual( exp.header.name, mjob.job.qobj().experiments[exp_index].header.name) rjob_set.results() self.assertEqual(rjob_set.statuses(), [JobStatus.DONE] * len(job_set.jobs()))
class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" def setUp(self): self._qc = _bell_circuit() self._jm = IBMQJobManager() @requires_provider def test_split_circuits(self, provider): """Test having circuits split into multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) job_set.results() statuses = job_set.statuses() self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_no_split_circuits(self, provider): """Test running all circuits in a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(int(max_circs / 2)): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) self.assertTrue(len(job_set.jobs()), 1) @requires_provider def test_custom_split_circuits(self, provider): """Test having circuits split with custom slices.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_job_report(self, provider): """Test job report.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) @requires_provider def test_skipped_status(self, provider): """Test one of jobs has no status.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) @requires_provider def test_job_qobjs(self, provider): """Test retrieving qobjs for the jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = provider.backends.retrieve_job(jobs[i].job_id()) self.assertDictEqual(qobj.__dict__, rjob.qobj().__dict__) @requires_provider def test_error_message(self, provider): """Test error message report.""" backend = provider.get_backend('ibmq_qasm_simulator') # Create a bad job. qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) circs = [] for _ in range(4): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2) job_set.results() job_set.managed_jobs()[1].job = job error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertIn(job.job_id(), error_report) @requires_provider def test_async_submit_exception(self, provider): """Test asynchronous job submit failed.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) with mock.patch.object( IBMQBackend, 'run', side_effect=[IBMQBackendError("Kaboom!"), mock.DEFAULT]): job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertIsNone(job_set.jobs()[0]) self.assertIsNotNone(job_set.jobs()[1]) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() @requires_provider def test_multiple_job_sets(self, provider): """Test submitting multiple sets of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc, self._qc], backend=backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) @requires_provider def test_retrieve_job_sets(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() name = str(time.time()).replace('.', '') self._jm.run([self._qc], backend=backend, max_experiments_per_job=1) job_set = self._jm.run([self._qc, self._qc], backend=backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set)
class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" @classmethod @requires_provider def setUpClass(cls, provider): """Initial class level setup.""" # pylint: disable=arguments-differ super().setUpClass() cls.provider = provider cls.sim_backend = provider.get_backend('ibmq_qasm_simulator') def setUp(self): """Initial test setup.""" super().setUp() self._qc = ReferenceCircuits.bell() self._jm = IBMQJobManager() self._fake_api_backend = None self._fake_api_provider = None def tearDown(self): """Tear down.""" super().tearDown() if self._fake_api_backend: self._fake_api_backend._api_client.tear_down() # Restore provider backends since we cannot deep copy provider. self.provider.backends._provider = self.provider @property def fake_api_backend(self): """Setup a backend instance with fake API client.""" if not self._fake_api_backend: self._fake_api_backend = copy.copy(self.sim_backend) self._fake_api_provider = copy.copy(self.provider) self._fake_api_provider._api_client = self._fake_api_backend._api_client \ = BaseFakeAccountClient() self._fake_api_backend._provider = self._fake_api_provider self._fake_api_provider.backends._provider = self._fake_api_provider self._fake_api_backend._configuration.max_experiments = 10 return self._fake_api_backend @property def fake_api_provider(self): """Setup a provider instance with fake API client.""" if not self._fake_api_provider: _ = self.fake_api_backend return self._fake_api_provider def test_split_circuits(self): """Test having circuits split into multiple jobs.""" max_circs = self.fake_api_backend.configuration().max_experiments circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=self.fake_api_backend) job_set.results() statuses = job_set.statuses() self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) self.assertTrue(len(job_set.jobs()), 2) def test_no_split_circuits(self): """Test running all circuits in a single job.""" max_circs = self.fake_api_backend.configuration().max_experiments job_set = self._jm.run([self._qc] * int(max_circs / 2), backend=self.fake_api_backend) self.assertTrue(len(job_set.jobs()), 1) def test_custom_split_circuits(self): """Test having circuits split with custom slices.""" job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) self.assertTrue(len(job_set.jobs()), 2) def test_job_report(self): """Test job report.""" job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) def test_skipped_status(self): """Test one of jobs has no status.""" job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) def test_job_qobjs(self): """Test retrieving qobjs for the jobs.""" qc2 = QuantumCircuit(1, 1) qc2.x(0) qc2.measure(0, 0) circs = [self._qc, qc2] job_set = self._jm.run(circs, backend=self.fake_api_backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = self.fake_api_provider.backends.retrieve_job( jobs[i].job_id()) self.maxDiff = None # pylint: disable=invalid-name self.assertDictEqual(qobj.to_dict(), rjob.qobj().to_dict()) def test_error_message(self): """Test error message report.""" self.fake_api_backend._api_client = \ BaseFakeAccountClient(job_class=[BaseFakeJob, FailedFakeJob]) self.fake_api_provider._api_client = self.fake_api_backend._api_client job_set = self._jm.run([self._qc] * 4, backend=self.fake_api_backend, max_experiments_per_job=2) job_set.results() jobs = job_set.jobs() error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertNotIn(jobs[0].job_id(), error_report) self.assertIn(jobs[1].job_id(), error_report) def test_async_submit_exception(self): """Test asynchronous job submit failed.""" self.fake_api_backend._api_client = JobSubmitFailClient( max_fail_count=1) job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) self.assertTrue(any(job is None for job in job_set.jobs())) self.assertTrue(any(job is not None for job in job_set.jobs())) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() def test_multiple_job_sets(self): """Test submitting multiple sets of jobs.""" qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=self.fake_api_backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) def test_retrieve_job_sets_by_name(self): """Test retrieving job sets by name.""" name = str(time.time()).replace('.', '') job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set) def test_retrieve_job_set(self): """Test retrieving a set of jobs.""" tags = ['test_retrieve_job_set'] circs_counts = [3, 4] for count in circs_counts: with self.subTest(count=count): circs = [] for i in range(count): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=self.sim_backend, max_experiments_per_job=2, job_tags=tags) self.assertEqual(job_set.tags(), tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjob_set = IBMQJobManager().retrieve_job_set( job_set_id=job_set.job_set_id(), provider=self.provider) self.assertEqual({ job.job_id() for job in job_set.jobs() }, { rjob.job_id() for rjob in rjob_set.jobs() }, "Unexpected jobs retrieved. Job set id used was {}.".format( job_set.job_set_id())) self.assertEqual(rjob_set.tags(), job_set.tags()) self.assertEqual(len(rjob_set.qobjs()), len(job_set.qobjs())) mjobs = job_set.managed_jobs() for index, rmjob in enumerate(rjob_set.managed_jobs()): mjob = mjobs[index] self.assertEqual(rmjob.start_index, mjob.start_index) self.assertEqual(rmjob.end_index, mjob.end_index) for exp_index, exp in enumerate( rmjob.job.qobj().experiments): self.assertEqual( exp.header.name, mjob.job.qobj().experiments[exp_index].header.name) rjob_set.results() self.assertEqual(rjob_set.statuses(), [JobStatus.DONE] * len(job_set.jobs())) def test_share_job_in_project(self): """Test sharing managed jobs within a project.""" job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1, job_share_level="project") for job in job_set.jobs(): job.refresh() self.assertEqual( job.share_level(), 'project', "Job {} has incorrect share level".format(job.job_id())) def test_invalid_job_share_level(self): """Test setting a non existent share level for managed jobs.""" self.assertRaises(IBMQJobManagerInvalidStateError, self._jm.run, [self._qc] * 2, backend=self.fake_api_backend, job_share_level="invalid_job_share_level") def test_job_tags(self): """Test job tags for managed jobs.""" job_tags = [uuid.uuid4().hex] job_set = self._jm.run([self._qc] * 2, backend=self.sim_backend, max_experiments_per_job=1, job_tags=job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjobs = self.provider.backends.jobs(job_tags=job_tags) self.assertEqual( {job.job_id() for job in job_set.jobs()}, {rjob.job_id() for rjob in rjobs}, "Unexpected jobs retrieved. Job tag used was {}".format(job_tags)) self.assertEqual(job_set.tags(), job_tags) def test_job_limit(self): """Test reaching job limit.""" job_limit = 5 self.fake_api_backend._api_client = BaseFakeAccountClient( job_limit=job_limit, job_class=CancelableFakeJob) self.fake_api_provider._api_client = self.fake_api_backend._api_client job_set = None try: with self.assertLogs(managedjob.logger, 'WARNING'): job_set = self._jm.run([self._qc] * (job_limit + 2), backend=self.fake_api_backend, max_experiments_per_job=1) time.sleep(1) # There should be 5 done and 2 running futures. running_futures = [ mjob.future for mjob in job_set.managed_jobs() if mjob.future.running() ] max_wait = 6 while len(running_futures) > 2 and max_wait > 0: running_futures = [f for f in running_futures if f.running()] time.sleep(0.5) self.assertEqual(len(running_futures), 2) for mjob in job_set.managed_jobs(): if mjob.job is not None: mjob.cancel() self.assertEqual(len(job_set.jobs()), job_limit + 2) self.assertTrue(all(job_set.jobs())) finally: # Cancel all submitted jobs first. for mjob in job_set.managed_jobs(): if mjob.job is not None: mjob.cancel() elif job_set._job_submit_lock.locked(): job_set._job_submit_lock.release() wait([mjob.future for mjob in job_set.managed_jobs()], timeout=5) def test_job_limit_timeout(self): """Test reaching job limit.""" job_limit = 5 self.fake_api_backend._api_client = JobTimeoutClient( job_limit=job_limit, max_fail_count=1) self.fake_api_provider._api_client = self.fake_api_backend._api_client job_set = None try: job_set = self._jm.run([self._qc] * (job_limit + 2), backend=self.fake_api_backend, max_experiments_per_job=1) last_mjobs = job_set._managed_jobs[-2:] for _ in range(10): if all(mjob.job for mjob in last_mjobs): break self.assertTrue(all(job.job_id() for job in job_set.jobs())) finally: # Cancel all jobs. for mjob in job_set.managed_jobs(): if mjob.job is not None: mjob.cancel() elif job_set._job_submit_lock.locked(): job_set._job_submit_lock.release() wait([mjob.future for mjob in job_set.managed_jobs()], timeout=5) def test_job_tags_replace(self): """Test updating job tags by replacing the job set's existing tags.""" initial_job_tags = [uuid.uuid4().hex] job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1, job_tags=initial_job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) tag_prefix = uuid.uuid4().hex replacement_tags = [ '{}_new_tag_{}'.format(tag_prefix, i) for i in range(2) ] _ = job_set.update_tags(replacement_tags=replacement_tags) for job in job_set.jobs(): job.refresh() self.assertEqual(set(job.tags()), set(replacement_tags + [job_set._id_long])) def test_job_tags_remove(self): """Test updating job tags by removing the job set's existing tags.""" initial_job_tags = [uuid.uuid4().hex] job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1, job_tags=initial_job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) initial_job_tags_with_id_long = initial_job_tags + [job_set._id_long] # Update the job tags _ = job_set.update_tags(removal_tags=initial_job_tags_with_id_long) for job in job_set.jobs(): job.refresh() self.assertEqual(job.tags(), [job_set._id_long]) def test_index_by_number(self): """Test indexing results by number.""" max_per_job = 5 job_set = self._jm.run([self._qc] * max_per_job * 2, backend=self.fake_api_backend, max_experiments_per_job=max_per_job) result_manager = job_set.results() jobs = job_set.jobs() for i in [0, max_per_job - 1, max_per_job + 1]: with self.subTest(i=i): job_index = int(i / max_per_job) exp_index = i % max_per_job self.assertEqual( result_manager.get_counts(i), jobs[job_index].result().get_counts(exp_index)) def test_index_by_name(self): """Test indexing results by name.""" max_per_job = 5 circs = [] for i in range(max_per_job * 2 + 1): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=self.fake_api_backend, max_experiments_per_job=max_per_job) result_manager = job_set.results() jobs = job_set.jobs() for i in [1, max_per_job, len(circs) - 1]: with self.subTest(i=i): job_index = int(i / max_per_job) exp_index = i % max_per_job self.assertEqual( result_manager.get_counts(circs[i].name), jobs[job_index].result().get_counts(exp_index)) def test_index_out_of_range(self): """Test result index out of range.""" job_set = self._jm.run([self._qc], backend=self.fake_api_backend) result_manager = job_set.results() with self.assertRaises(IBMQJobManagerJobNotFound): result_manager.get_counts(1) def test_skipped_result(self): """Test one of jobs has no result.""" self.fake_api_backend._api_client = BaseFakeAccountClient( job_class=[BaseFakeJob, CancelableFakeJob]) job_set = self._jm.run([self._qc] * 2, backend=self.fake_api_backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1].cancel() result_manager = job_set.results() with self.assertRaises(IBMQManagedResultDataNotAvailable): result_manager.get_counts(1) def test_combine_results(self): """Test converting ManagedResult to Result.""" max_per_job = 5 job_set = self._jm.run([self._qc] * max_per_job * 2, backend=self.fake_api_backend, max_experiments_per_job=max_per_job) result_manager = job_set.results() combined_result = result_manager.combine_results() for i in range(max_per_job * 2): self.assertEqual(result_manager.get_counts(i), combined_result.get_counts(i)) def test_ibmq_managed_results_signature(self): """Test ``ManagedResults`` and ``Result`` contain the same public methods. Note: Aside from ensuring the two classes contain the same public methods, it is also necessary to check that the corresponding methods have the same signature. """ result_methods = self._get_class_methods(Result) self.assertTrue(result_methods) managed_results_methods = self._get_class_methods(ManagedResults) self.assertTrue(managed_results_methods) del managed_results_methods['combine_results'] for ignore_meth in ['to_dict', 'from_dict']: result_methods.pop(ignore_meth, None) # Ensure both classes share the *exact* same public methods. self.assertEqual(result_methods.keys(), managed_results_methods.keys()) # Ensure the signature for the public methods from both classes are compatible. for name, method in managed_results_methods.items(): managed_results_params = getattr(getfullargspec(method), 'args', []) result_params = getattr(getfullargspec(result_methods[name]), 'args', []) self.assertTrue(managed_results_params) self.assertTrue(result_params) # pylint: disable=duplicate-string-formatting-argument self.assertEqual( result_params, managed_results_params, "The signatures for method `{}` differ. " "`Result.{}` params = {} " "`ManagedResults.{}` params = {}.".format( name, name, managed_results_params, name, result_params)) def _get_class_methods(self, cls): """Get public class methods from its namespace. Note: Since the methods are found using the class itself and not and instance, the "methods" are categorized as functions. Methods are only bound when they belong to an actual instance. """ cls_methods = {} for name, method in cls.__dict__.items(): if isfunction(method) and not name.startswith('_'): cls_methods[name] = method return cls_methods
class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" def setUp(self): """Initial test setup.""" self._qc = ReferenceCircuits.bell() self._jm = IBMQJobManager() @requires_provider def test_split_circuits(self, provider): """Test having circuits split into multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) job_set.results() statuses = job_set.statuses() self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_no_split_circuits(self, provider): """Test running all circuits in a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(int(max_circs / 2)): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) self.assertTrue(len(job_set.jobs()), 1) @requires_provider def test_custom_split_circuits(self, provider): """Test having circuits split with custom slices.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_job_report(self, provider): """Test job report.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) @requires_provider def test_skipped_status(self, provider): """Test one of jobs has no status.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) @requires_provider def test_job_qobjs(self, provider): """Test retrieving qobjs for the jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() provider._api = backend._api qc2 = QuantumCircuit(1, 1) qc2.x(0) qc2.measure(0, 0) circs = [self._qc, qc2] job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = provider.backends.retrieve_job(jobs[i].job_id()) self.maxDiff = None # pylint: disable=invalid-name self.assertDictEqual(qobj.to_dict(), rjob.qobj().to_dict()) @requires_provider def test_error_message(self, provider): """Test error message report.""" backend = provider.get_backend('ibmq_qasm_simulator') # Create a bad job. qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj, validate_qobj=True) job_set = self._jm.run([self._qc] * 4, backend=backend, max_experiments_per_job=2) job_set.results() job_set.managed_jobs()[1].job = job error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertIn(job.job_id(), error_report) @requires_provider def test_async_submit_exception(self, provider): """Test asynchronous job submit failed.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = JobSubmitFailClient(max_fail_count=1) job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1) self.assertTrue(any(job is None for job in job_set.jobs())) self.assertTrue(any(job is not None for job in job_set.jobs())) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() @requires_provider def test_multiple_job_sets(self, provider): """Test submitting multiple sets of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc, self._qc], backend=backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) @requires_provider def test_retrieve_job_sets_by_name(self, provider): """Test retrieving job sets by name.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() name = str(time.time()).replace('.', '') job_set = self._jm.run([self._qc] * 2, backend=backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set) @requires_provider def test_retrieve_job_set(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') tags = ['test_retrieve_job_set'] circs_counts = [3, 4] for count in circs_counts: with self.subTest(count=count): circs = [] for i in range(count): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2, job_tags=tags) self.assertEqual(job_set.tags(), tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjob_set = IBMQJobManager().retrieve_job_set( job_set_id=job_set.job_set_id(), provider=provider) self.assertEqual({ job.job_id() for job in job_set.jobs() }, { rjob.job_id() for rjob in rjob_set.jobs() }, "Unexpected jobs retrieved. Job set id used was {}.".format( job_set.job_set_id())) self.assertEqual(rjob_set.tags(), job_set.tags()) self.assertEqual(len(rjob_set.qobjs()), len(job_set.qobjs())) self.log.info("Job set report:\n%s", rjob_set.report()) mjobs = job_set.managed_jobs() for index, rmjob in enumerate(rjob_set.managed_jobs()): mjob = mjobs[index] self.assertEqual(rmjob.start_index, mjob.start_index) self.assertEqual(rmjob.end_index, mjob.end_index) for exp_index, exp in enumerate( rmjob.job.qobj().experiments): self.assertEqual( exp.header.name, mjob.job.qobj().experiments[exp_index].header.name) rjob_set.results() self.assertEqual(rjob_set.statuses(), [JobStatus.DONE] * len(job_set.jobs())) @requires_provider def test_share_job_in_project(self, provider): """Test sharing managed jobs within a project.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1, job_share_level="project") for job in job_set.jobs(): job.refresh() self.assertEqual( getattr(job, 'share_level'), 'project', "Job {} has incorrect share level".format(job.job_id())) @requires_provider def test_invalid_job_share_level(self, provider): """Test setting a non existent share level for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') self.assertRaises(IBMQJobManagerInvalidStateError, self._jm.run, [self._qc] * 2, backend=backend, job_share_level="invalid_job_share_level") @requires_provider def test_job_tags(self, provider): """Test job tags for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') job_tags = [uuid.uuid4().hex] job_set = self._jm.run([self._qc] * 2, backend=backend, max_experiments_per_job=1, job_tags=job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) # TODO No need to wait for job to run once api is fixed while any(status not in JOB_FINAL_STATES + (JobStatus.RUNNING, ) for status in job_set.statuses()): time.sleep(0.5) rjobs = provider.backends.jobs(job_tags=job_tags) self.assertEqual( {job.job_id() for job in job_set.jobs()}, {rjob.job_id() for rjob in rjobs}, "Unexpected jobs retrieved. Job tag used was {}".format(job_tags)) self.assertEqual(job_set.tags(), job_tags) @requires_provider def test_job_limit(self, provider): """Test reaching job limit.""" job_limit = 5 backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient(job_limit=job_limit, job_class=CancelableFakeJob) provider._api = backend._api circs = transpile([self._qc] * (job_limit + 2), backend=backend) job_set = None try: with self.assertLogs(managedjob.logger, 'WARNING'): job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) time.sleep(1) # There should be 5 done and 2 running futures. running_futures = [ mjob.future for mjob in job_set.managed_jobs() if mjob.future.running() ] max_wait = 6 while len(running_futures) > 2 and max_wait > 0: running_futures = [f for f in running_futures if f.running()] time.sleep(0.5) self.assertEqual(len(running_futures), 2) for mjob in job_set.managed_jobs(): if mjob.job is not None: mjob.cancel() self.assertEqual(len(job_set.jobs()), job_limit + 2) self.assertTrue(all(job_set.jobs())) finally: # Cancel all submitted jobs first. for mjob in job_set.managed_jobs(): if mjob.job is not None: mjob.cancel() elif job_set._job_submit_lock.locked(): job_set._job_submit_lock.release() wait([mjob.future for mjob in job_set.managed_jobs()], timeout=5)
class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" def setUp(self): self._qc = _bell_circuit() self._jm = IBMQJobManager() @requires_provider def test_split_circuits(self, provider): """Test having circuits split into multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) job_set.results() statuses = job_set.statuses() self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_no_split_circuits(self, provider): """Test running all circuits in a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(int(max_circs / 2)): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) self.assertTrue(len(job_set.jobs()), 1) @requires_provider def test_custom_split_circuits(self, provider): """Test having circuits split with custom slices.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertTrue(len(job_set.jobs()), 2) @requires_provider def test_job_report(self, provider): """Test job report.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) @requires_provider def test_skipped_status(self, provider): """Test one of jobs has no status.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) @requires_provider def test_job_qobjs(self, provider): """Test retrieving qobjs for the jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = provider.backends.retrieve_job(jobs[i].job_id()) self.assertDictEqual(qobj.__dict__, rjob.qobj().__dict__) @requires_provider def test_error_message(self, provider): """Test error message report.""" backend = provider.get_backend('ibmq_qasm_simulator') # Create a bad job. qc_new = transpile(self._qc, backend) qobj = assemble([qc_new, qc_new], backend=backend) qobj.experiments[1].instructions[1].name = 'bad_instruction' job = backend.run(qobj) circs = [] for _ in range(4): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2) job_set.results() job_set.managed_jobs()[1].job = job error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertIn(job.job_id(), error_report) @requires_provider def test_async_submit_exception(self, provider): """Test asynchronous job submit failed.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) with mock.patch.object( IBMQBackend, 'run', side_effect=[IBMQBackendError("Kaboom!"), mock.DEFAULT]): job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertIsNone(job_set.jobs()[0]) self.assertIsNotNone(job_set.jobs()[1]) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() @requires_provider def test_multiple_job_sets(self, provider): """Test submitting multiple sets of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc, self._qc], backend=backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) @requires_provider def test_retrieve_job_sets_by_name(self, provider): """Test retrieving job sets by name.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() name = str(time.time()).replace('.', '') self._jm.run([self._qc], backend=backend, max_experiments_per_job=1) job_set = self._jm.run([self._qc, self._qc], backend=backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set) @requires_provider def test_retrieve_job_set(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') tags = ['test_retrieve_job_set'] circs_counts = [3, 4] for count in circs_counts: with self.subTest(count=count): circs = [] for i in range(count): new_qc = copy.deepcopy(self._qc) new_qc.name = "test_qc_{}".format(i) circs.append(new_qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=2, job_tags=tags) self.assertEqual(job_set.tags(), tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) rjob_set = IBMQJobManager().retrieve_job_set( job_set_id=job_set.job_set_id(), provider=provider) self.assertEqual({ job.job_id() for job in job_set.jobs() }, { rjob.job_id() for rjob in rjob_set.jobs() }, "Unexpected jobs retrieved. Job set id used was {}.".format( job_set.job_set_id())) self.assertEqual(rjob_set.tags(), job_set.tags()) self.assertEqual(len(rjob_set.qobjs()), len(job_set.qobjs())) self.log.info("Job set report:\n%s", rjob_set.report()) mjobs = job_set.managed_jobs() for index, rmjob in enumerate(rjob_set.managed_jobs()): mjob = mjobs[index] self.assertEqual(rmjob.start_index, mjob.start_index) self.assertEqual(rmjob.end_index, mjob.end_index) for exp_index, exp in enumerate( rmjob.job.qobj().experiments): self.assertEqual( exp.header.name, mjob.job.qobj().experiments[exp_index].header.name) rjob_set.results() self.assertEqual(rjob_set.statuses(), [JobStatus.DONE] * len(job_set.jobs())) @requires_provider def test_share_job_in_project(self, provider): """Test sharing managed jobs within a project.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1, job_share_level="project") for job in job_set.jobs(): job.refresh() self.assertEqual( getattr(job, 'share_level'), 'project', "Job {} has incorrect share level".format(job.job_id())) @requires_provider def test_invalid_job_share_level(self, provider): """Test setting a non existent share level for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) self.assertRaises(IBMQJobManagerInvalidStateError, self._jm.run, circs, backend=backend, job_share_level="invalid_job_share_level") @requires_provider def test_job_tags(self, provider): """Test job tags for managed jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_tags = [uuid.uuid4().hex] job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1, job_tags=job_tags) # Wait for jobs to be submitted. while JobStatus.INITIALIZING in job_set.statuses(): time.sleep(1) # TODO No need to wait for job to run once api is fixed while any(status not in JOB_FINAL_STATES + (JobStatus.RUNNING, ) for status in job_set.statuses()): time.sleep(0.5) rjobs = provider.backends.jobs(job_tags=job_tags) self.assertEqual( {job.job_id() for job in job_set.jobs()}, {rjob.job_id() for rjob in rjobs}, "Unexpected jobs retrieved. Job tag used was {}".format(job_tags)) self.assertEqual(job_set.tags(), job_tags)
class TestIBMQJobManager(IBMQTestCase): """Tests for IBMQJobManager.""" def setUp(self): self._qc = QuantumCircuit(2, 2) self._qc.h(0) self._qc.cx(0, 1) self._qc.measure([0, 1], [0, 1]) self._jm = IBMQJobManager() @requires_provider def test_split_circuits(self, provider): """Test having circuits split into multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) results = job_set.results() statuses = job_set.statuses() self.assertEqual(len(results), 2) self.assertEqual(len(statuses), 2) self.assertTrue(all(s is JobStatus.DONE for s in statuses)) @requires_provider def test_no_split_circuits(self, provider): """Test running all circuits in a single job.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments backend._api = BaseFakeAccountClient() circs = [] for _ in range(int(max_circs / 2)): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) results = job_set.results() statuses = job_set.statuses() self.assertEqual(len(results), 1) self.assertEqual(len(statuses), 1) @requires_provider def test_custom_split_circuits(self, provider): """Test having circuits split with custom slices.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) results = job_set.results() statuses = job_set.statuses() self.assertEqual(len(results), 2) self.assertEqual(len(statuses), 2) @requires_provider def test_result(self, provider): """Test getting results for multiple jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) results = job_set.results() jobs = job_set.jobs() self.assertEqual(len(results), 2) for i, result in enumerate(results): self.assertIsNotNone(result) self.assertDictEqual(result.get_counts(0), jobs[i].result().get_counts(0)) @requires_provider def test_job_report(self, provider): """Test job report.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() report = self._jm.report() for job in jobs: self.assertIn(job.job_id(), report) @requires_provider def test_skipped_result(self, provider): """Test one of jobs has no result.""" backend = provider.get_backend('ibmq_qasm_simulator') max_circs = backend.configuration().max_experiments circs = [] for _ in range(max_circs + 2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend) jobs = job_set.jobs() cjob = jobs[0] cancelled = False for _ in range(2): # Try twice in case job is not in a cancellable state try: cancelled = cjob.cancel() if cancelled: break except JobError: pass results = job_set.results() statuses = job_set.statuses() if cancelled: self.assertTrue(statuses[0] is JobStatus.CANCELLED) self.assertIsNone( results[0], "Job {} cancelled but result is not None.".format( cjob.job_id())) else: self.log.warning("Unable to cancel job %s", cjob.job_id()) @requires_provider def test_skipped_status(self, provider): """Test one of jobs has no status.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() jobs[1]._job_id = 'BAD_ID' statuses = job_set.statuses() self.assertIsNone(statuses[1]) @requires_provider def test_job_qobjs(self, provider): """Test retrieving qobjs for the jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') circs = [] for _ in range(2): circs.append(self._qc) job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() job_set.results() for i, qobj in enumerate(job_set.qobjs()): rjob = provider.backends.retrieve_job(jobs[i].job_id()) self.assertDictEqual(qobj.__dict__, rjob.qobj().__dict__) @run_on_staging def test_error_message(self, provider): """Test error message report.""" backend = least_busy(provider.backends(simulator=False)) bad_qc = copy.deepcopy(self._qc) circs = [transpile(self._qc, backend=backend), bad_qc] job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) jobs = job_set.jobs() results = job_set.results(timeout=300) self.assertIsNone(results[1]) error_report = job_set.error_messages() self.assertIsNotNone(error_report) self.assertIn(jobs[1].job_id(), error_report) @requires_provider def test_async_submit_exception(self, provider): """Test asynchronous job submit failed.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() circs = [] for _ in range(2): circs.append(self._qc) with mock.patch.object( IBMQBackend, 'run', side_effect=[IBMQBackendError("Kaboom!"), mock.DEFAULT]): job_set = self._jm.run(circs, backend=backend, max_experiments_per_job=1) self.assertIsNone(job_set.jobs()[0]) self.assertIsNotNone(job_set.jobs()[1]) # Make sure results() and statuses() don't fail job_set.results() job_set.statuses() @requires_provider def test_multiple_job_sets(self, provider): """Test submitting multiple sets of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() qc2 = QuantumCircuit(1, 1) qc2.h(0) qc2.measure([0], [0]) job_set1 = self._jm.run([self._qc, self._qc], backend=backend, max_experiments_per_job=1) job_set2 = self._jm.run([qc2], backend=backend, max_experiments_per_job=1) id1 = {job.job_id() for job in job_set1.jobs()} id2 = {job.job_id() for job in job_set2.jobs()} self.assertTrue(id1.isdisjoint(id2)) @requires_provider def test_retrieve_job_sets(self, provider): """Test retrieving a set of jobs.""" backend = provider.get_backend('ibmq_qasm_simulator') backend._api = BaseFakeAccountClient() name = str(time.time()).replace('.', '') self._jm.run([self._qc], backend=backend, max_experiments_per_job=1) job_set = self._jm.run([self._qc, self._qc], backend=backend, name=name, max_experiments_per_job=1) rjob_set = self._jm.job_sets(name=name)[0] self.assertEqual(job_set, rjob_set)