From e89e2e0a1ed7323cc020f06ec69032ba15659439 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 24 Jul 2023 00:06:26 -0700 Subject: [PATCH 1/8] add debias and sharpen to requests --- projectq/backends/_ionq/_ionq.py | 8 ++++ projectq/backends/_ionq/_ionq_http_client.py | 37 +++++++++++++------ .../backends/_ionq/_ionq_http_client_test.py | 18 ++++----- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/projectq/backends/_ionq/_ionq.py b/projectq/backends/_ionq/_ionq.py index 4c59369c..c097985b 100644 --- a/projectq/backends/_ionq/_ionq.py +++ b/projectq/backends/_ionq/_ionq.py @@ -78,6 +78,8 @@ def __init__( verbose=False, token=None, device='ionq_simulator', + error_mitigation=None, + sharpen=None, num_retries=3000, interval=1, retrieve_execution=None, @@ -102,6 +104,8 @@ def __init__( """ super().__init__() self.device = device if use_hardware else 'ionq_simulator' + self.error_mitigation = error_mitigation + self._sharpen = sharpen self._num_runs = num_runs self._verbose = verbose self._token = token @@ -291,6 +295,9 @@ def _run(self): # pylint: disable=too-many-locals measured_ids = self._measured_ids[:] info = { 'circuit': self._circuit, + 'gateset': 'qis', + 'format': 'ionq.circuit.v0', + 'error_mitigation': self.error_mitigation, 'nq': len(qubit_mapping.keys()), 'shots': self._num_runs, 'meas_mapped': [qubit_mapping[qubit_id] for qubit_id in measured_ids], @@ -311,6 +318,7 @@ def _run(self): # pylint: disable=too-many-locals device=self.device, token=self._token, jobid=self._retrieve_execution, + sharpen=self._sharpen, num_retries=self._num_retries, interval=self._interval, verbose=self._verbose, diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index 72e16555..22b55971 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -30,7 +30,7 @@ RequestTimeoutError, ) -_API_URL = 'https://api.ionq.co/v0.2/' +_API_URL = 'https://api.ionq.co/v0.3/' _JOB_API_URL = urljoin(_API_URL, 'jobs/') @@ -59,7 +59,7 @@ def update_devices_list(self): }, 'ionq_qpu': { 'nq': 11, - 'target': 'qpu', + 'target': 'qpu.harmony', }, } for backend in r_json: @@ -139,12 +139,15 @@ def run(self, info, device): }, 'shots': info['shots'], 'registers': {'meas_mapped': info['meas_mapped']}, - 'lang': 'json', - 'body': { + 'input': { + 'format': info['format'], + 'gateset': info['gateset'], 'qubits': info['nq'], 'circuit': info['circuit'], }, } + if info['error_mitigation'] is not None: + argument['error_mitigation'] = info['error_mitigation'] # _API_URL[:-1] strips the trailing slash. # TODO: Add comprehensive error parsing for non-200 responses. @@ -166,7 +169,7 @@ def run(self, info, device): } raise JobSubmissionError(f"{failure['code']}: {failure['error']} (status={status})") - def get_result(self, device, execution_id, num_retries=3000, interval=1): + def get_result(self, device, execution_id, sharpen=None, num_retries=3000, interval=1): """ Given a backend and ID, fetch the results for this job's execution. @@ -178,6 +181,8 @@ def get_result(self, device, execution_id, num_retries=3000, interval=1): Args: device (str): The device used to run this job. execution_id (str): An IonQ Job ID. + sharpen: A boolean that determines how to aggregate error mitigated. + If True, apply majority vote mitigation; if False, apply average mitigation. num_retries (int, optional): Number of times to retry the fetch before raising a timeout error. Defaults to 3000. interval (int, optional): Number of seconds to wait between retries. @@ -196,6 +201,10 @@ def get_result(self, device, execution_id, num_retries=3000, interval=1): if self._verbose: # pragma: no cover print(f"Waiting for results. [Job ID: {execution_id}]") + params = {} + if sharpen is not None: + params["sharpen"] = sharpen + original_sigint_handler = signal.getsignal(signal.SIGINT) def _handle_sigint_during_get_result(*_): # pragma: no cover @@ -205,18 +214,20 @@ def _handle_sigint_during_get_result(*_): # pragma: no cover try: for retries in range(num_retries): - req = super().get(urljoin(_JOB_API_URL, execution_id)) + req = super().get(urljoin(_JOB_API_URL, execution_id), params=params) req.raise_for_status() - r_json = req.json() - status = r_json['status'] + req_json = req.json() + status = req_json['status'] # Check if job is completed. if status == 'completed': - meas_mapped = r_json['registers']['meas_mapped'] - meas_qubit_ids = json.loads(r_json['metadata']['meas_qubit_ids']) - output_probs = r_json['data']['registers']['meas_mapped'] + r = super().get(urljoin(_JOB_API_URL, req_json['results_url']), params=params) + r_json = r.json() + meas_mapped = req_json['registers']['meas_mapped'] + meas_qubit_ids = json.loads(req_json['metadata']['meas_qubit_ids']) + output_probs = r_json return { - 'nq': r_json['qubits'], + 'nq': req_json['qubits'], 'output_probs': output_probs, 'meas_mapped': meas_mapped, 'meas_qubit_ids': meas_qubit_ids, @@ -255,6 +266,7 @@ def retrieve( device, token, jobid, + sharpen=None, num_retries=3000, interval=1, verbose=False, @@ -281,6 +293,7 @@ def retrieve( res = ionq_session.get_result( device, jobid, + sharpen=sharpen, num_retries=num_retries, interval=interval, ) diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index d1df8fb6..9a1cd88e 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -50,7 +50,7 @@ def user_password_input(prompt): def test_is_online(monkeypatch): def mock_get(_self, path, *args, **kwargs): - assert 'https://api.ionq.co/v0.2/backends' == path + assert 'https://api.ionq.co/v0.3/backends' == path mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value=[ @@ -86,7 +86,7 @@ def mock_get(_self, path, *args, **kwargs): def test_show_devices(monkeypatch): def mock_get(_self, path, *args, **kwargs): - assert 'https://api.ionq.co/v0.2/backends' == path + assert 'https://api.ionq.co/v0.3/backends' == path mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value=[ @@ -182,7 +182,7 @@ def _dummy_update(_self): } def mock_post(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs' + assert path == 'https://api.ionq.co/v0.3/jobs' assert 'json' in kwargs assert expected_request == kwargs['json'] mock_response = mock.MagicMock() @@ -196,7 +196,7 @@ def mock_post(_self, path, *args, **kwargs): return mock_response def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs/new-job-id' + assert path == 'https://api.ionq.co/v0.3/jobs/new-job-id' mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value={ @@ -428,7 +428,7 @@ def _dummy_update(_self): ) def mock_post(_self, path, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs' + assert path == 'https://api.ionq.co/v0.3/jobs' mock_response = mock.MagicMock() mock_response.json = mock.MagicMock(return_value=err_data) return mock_response @@ -467,7 +467,7 @@ def _dummy_update(_self): ) def mock_post(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs' + assert path == 'https://api.ionq.co/v0.3/jobs' mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value={ @@ -478,7 +478,7 @@ def mock_post(_self, path, *args, **kwargs): return mock_response def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs/new-job-id' + assert path == 'https://api.ionq.co/v0.3/jobs/new-job-id' mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value={ @@ -528,7 +528,7 @@ def _dummy_update(_self): request_num = [0] def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs/old-job-id' + assert path == 'https://api.ionq.co/v0.3/jobs/old-job-id' json_response = { 'id': 'old-job-id', 'status': 'running', @@ -586,7 +586,7 @@ def _dummy_update(_self): request_num = [0] def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.2/jobs/old-job-id' + assert path == 'https://api.ionq.co/v0.3/jobs/old-job-id' json_response = { 'id': 'old-job-id', 'status': 'running', From b2ed4a8bb50ea72fd8efb415aea12a24e1f09dfe Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Mon, 24 Jul 2023 13:47:24 -0700 Subject: [PATCH 2/8] adds user-agent to request --- projectq/backends/_ionq/_ionq_http_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index 22b55971..f34b2111 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -40,6 +40,7 @@ class IonQ(Session): def __init__(self, verbose=False): """Initialize an session with IonQ's APIs.""" super().__init__() + self.user_agent() self.backends = {} self.timeout = 5.0 self.token = None @@ -101,6 +102,10 @@ def can_run_experiment(self, info, device): nb_qubit_needed = info['nq'] return nb_qubit_needed <= nb_qubit_max, nb_qubit_max, nb_qubit_needed + def user_agent(self): + """Set a User-Agent header for this session.""" + self.headers.update({'User-Agent': f'projectq-ionq/0.8.0'}) + def authenticate(self, token=None): """Set an Authorization header for this session. From 79b6c9df6575cd5ffcc01e849666397fa46942b2 Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Thu, 3 Aug 2023 01:11:46 -0700 Subject: [PATCH 3/8] pass ionq tests --- projectq/backends/_ionq/_ionq_http_client.py | 24 +++++----- .../backends/_ionq/_ionq_http_client_test.py | 48 ++++++++++--------- projectq/backends/_ionq/_ionq_test.py | 11 ++++- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index f34b2111..a0d54b80 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -137,21 +137,21 @@ def run(self, info, device): str: The ID of a newly submitted Job. """ argument = { - 'target': self.backends[device]['target'], + 'target': self.backends[device].get('target'), 'metadata': { 'sdk': 'ProjectQ', - 'meas_qubit_ids': json.dumps(info['meas_qubit_ids']), + 'meas_qubit_ids': json.dumps(info.get('meas_qubit_ids')), }, - 'shots': info['shots'], - 'registers': {'meas_mapped': info['meas_mapped']}, + 'shots': info.get('shots'), + 'registers': {'meas_mapped': info.get('meas_mapped')}, 'input': { - 'format': info['format'], - 'gateset': info['gateset'], - 'qubits': info['nq'], - 'circuit': info['circuit'], + 'format': info.get('format'), + 'gateset': info.get('gateset'), + 'qubits': info.get('nq'), + 'circuit': info.get('circuit'), }, } - if info['error_mitigation'] is not None: + if info.get('error_mitigation') is not None: argument['error_mitigation'] = info['error_mitigation'] # _API_URL[:-1] strips the trailing slash. @@ -161,11 +161,11 @@ def run(self, info, device): # Process the response. r_json = req.json() - status = r_json['status'] + status = r_json.get('status') # Return the job id. if status == 'ready': - return r_json['id'] + return r_json.get('id') # Otherwise, extract any provided failure info and raise an exception. failure = r_json.get('failure') or { @@ -226,7 +226,7 @@ def _handle_sigint_during_get_result(*_): # pragma: no cover # Check if job is completed. if status == 'completed': - r = super().get(urljoin(_JOB_API_URL, req_json['results_url']), params=params) + r = super().get(urljoin(_JOB_API_URL, req_json.get('results_url')), params=params) r_json = r.json() meas_mapped = req_json['registers']['meas_mapped'] meas_qubit_ids = json.loads(req_json['metadata']['meas_qubit_ids']) diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index 9a1cd88e..6219bfd1 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -168,8 +168,9 @@ def _dummy_update(_self): 'metadata': {'sdk': 'ProjectQ', 'meas_qubit_ids': '[2, 3]'}, 'shots': 1, 'registers': {'meas_mapped': [2, 3]}, - 'lang': 'json', - 'body': { + 'input': { + 'format': None, + 'gateset': None, 'qubits': 4, 'circuit': [ {'gate': 'x', 'targets': [0]}, @@ -195,8 +196,9 @@ def mock_post(_self, path, *args, **kwargs): ) return mock_response - def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.3/jobs/new-job-id' + +def mock_get(_self, path, *args, **kwargs): + if path == 'https://api.ionq.co/v0.3/jobs/new-job-id': mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( return_value={ @@ -205,12 +207,19 @@ def mock_get(_self, path, *args, **kwargs): 'qubits': 4, 'metadata': {'meas_qubit_ids': '[2, 3]'}, 'registers': {'meas_mapped': [2, 3]}, - 'data': { - 'registers': {'meas_mapped': {'2': 1}}, - }, + 'results_url': 'new-job-id/results', } ) - return mock_response + elif path == 'https://api.ionq.co/v0.3/jobs/new-job-id/results': + mock_response = mock.MagicMock() + mock_response.json = mock.MagicMock( + return_value={ + {'2': 1}, + } + ) + else: + raise ValueError(f"Unexpected URL: {path}") + return mock_response monkeypatch.setattr('requests.sessions.Session.post', mock_post) monkeypatch.setattr('requests.sessions.Session.get', mock_get) @@ -528,25 +537,24 @@ def _dummy_update(_self): request_num = [0] def mock_get(_self, path, *args, **kwargs): - assert path == 'https://api.ionq.co/v0.3/jobs/old-job-id' - json_response = { - 'id': 'old-job-id', - 'status': 'running', - } - if request_num[0] > 1: + if path == 'https://api.ionq.co/v0.3/jobs/old-job-id': json_response = { 'id': 'old-job-id', 'status': 'completed', 'qubits': 4, 'registers': {'meas_mapped': [2, 3]}, 'metadata': {'meas_qubit_ids': '[2, 3]'}, - 'data': { - 'registers': {'meas_mapped': {'2': 1}}, - }, + 'results_url': 'old-job-id/results', } + elif path == 'https://api.ionq.co/v0.3/jobs/old-job-id/results': + json_response = { + '2': 1, + } + else: + raise ValueError(f"Unexpected URL: {path}") + mock_response = mock.MagicMock() mock_response.json = mock.MagicMock(return_value=json_response) - request_num[0] += 1 return mock_response monkeypatch.setattr('requests.sessions.Session.get', mock_get) @@ -566,12 +574,8 @@ def user_password_input(prompt): # Code to test: # Called once per loop in _get_result while the job is not ready. - mock_sleep = mock.MagicMock() - monkeypatch.setattr(_ionq_http_client.time, 'sleep', mock_sleep) result = _ionq_http_client.retrieve('dummy', token, 'old-job-id') assert expected == result - # We only sleep twice. - assert 2 == mock_sleep.call_count def test_retrieve_that_errors_are_caught(monkeypatch): diff --git a/projectq/backends/_ionq/_ionq_test.py b/projectq/backends/_ionq/_ionq_test.py index 5a846b03..867e8e3c 100644 --- a/projectq/backends/_ionq/_ionq_test.py +++ b/projectq/backends/_ionq/_ionq_test.py @@ -393,12 +393,16 @@ def mock_retrieve(*args, **kwargs): def test_ionq_backend_functional_test(monkeypatch, mapper_factory): - """Test that the backend can handle a valid circuit with valid results.""" + """Test that sub-classed or aliased gates are handled correctly.""" + # using alias gates, for coverage expected = { 'nq': 3, 'shots': 10, 'meas_mapped': [1, 2], 'meas_qubit_ids': [1, 2], + 'error_mitigation': None, + 'format': 'ionq.circuit.v0', + 'gateset': 'qis', 'circuit': [ {'gate': 'ry', 'rotation': 0.5, 'targets': [1]}, {'gate': 'rx', 'rotation': 0.5, 'targets': [1]}, @@ -425,7 +429,7 @@ def mock_send(*args, **kwargs): backend = _ionq.IonQBackend(verbose=True, num_runs=10) eng = MainEngine( backend=backend, - engine_list=[mapper_factory()], + engine_list=[mapper_factory(9)], verbose=True, ) unused_qubit = eng.allocate_qubit() # noqa: F841 @@ -458,6 +462,9 @@ def test_ionq_backend_functional_aliases_test(monkeypatch, mapper_factory): 'shots': 10, 'meas_mapped': [2, 3], 'meas_qubit_ids': [2, 3], + 'error_mitigation': None, + 'format': 'ionq.circuit.v0', + 'gateset': 'qis', 'circuit': [ {'gate': 'x', 'targets': [0]}, {'gate': 'x', 'targets': [1]}, From 24af196b68d4bda436d980c7c7adb32d31eb3616 Mon Sep 17 00:00:00 2001 From: Nguyen Damien Date: Tue, 2 Apr 2024 22:28:03 +0200 Subject: [PATCH 4/8] Fix linter warnings --- projectq/backends/_ionq/_ionq_http_client.py | 14 ++++-- .../backends/_ionq/_ionq_http_client_test.py | 48 +++++++++---------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index a0d54b80..ac31a91f 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -104,7 +104,7 @@ def can_run_experiment(self, info, device): def user_agent(self): """Set a User-Agent header for this session.""" - self.headers.update({'User-Agent': f'projectq-ionq/0.8.0'}) + self.headers.update({'User-Agent': 'projectq-ionq/0.8.0'}) def authenticate(self, token=None): """Set an Authorization header for this session. @@ -174,7 +174,13 @@ def run(self, info, device): } raise JobSubmissionError(f"{failure['code']}: {failure['error']} (status={status})") - def get_result(self, device, execution_id, sharpen=None, num_retries=3000, interval=1): + def get_result( + self, + device, + execution_id, + sharpen=None, + num_retries=3000, + interval=1): # pylint: disable=too-many-arguments,too-many-locals """ Given a backend and ID, fetch the results for this job's execution. @@ -226,8 +232,8 @@ def _handle_sigint_during_get_result(*_): # pragma: no cover # Check if job is completed. if status == 'completed': - r = super().get(urljoin(_JOB_API_URL, req_json.get('results_url')), params=params) - r_json = r.json() + r_get = super().get(urljoin(_JOB_API_URL, req_json.get('results_url')), params=params) + re_json = r_get.json() meas_mapped = req_json['registers']['meas_mapped'] meas_qubit_ids = json.loads(req_json['metadata']['meas_qubit_ids']) output_probs = r_json diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index 6219bfd1..46f9617f 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -197,29 +197,29 @@ def mock_post(_self, path, *args, **kwargs): return mock_response -def mock_get(_self, path, *args, **kwargs): - if path == 'https://api.ionq.co/v0.3/jobs/new-job-id': - mock_response = mock.MagicMock() - mock_response.json = mock.MagicMock( - return_value={ - 'id': 'new-job-id', - 'status': 'completed', - 'qubits': 4, - 'metadata': {'meas_qubit_ids': '[2, 3]'}, - 'registers': {'meas_mapped': [2, 3]}, - 'results_url': 'new-job-id/results', - } - ) - elif path == 'https://api.ionq.co/v0.3/jobs/new-job-id/results': - mock_response = mock.MagicMock() - mock_response.json = mock.MagicMock( - return_value={ - {'2': 1}, - } - ) - else: - raise ValueError(f"Unexpected URL: {path}") - return mock_response + def mock_get(_self, path, *args, **kwargs): + if path == 'https://api.ionq.co/v0.3/jobs/new-job-id': + mock_response = mock.MagicMock() + mock_response.json = mock.MagicMock( + return_value={ + 'id': 'new-job-id', + 'status': 'completed', + 'qubits': 4, + 'metadata': {'meas_qubit_ids': '[2, 3]'}, + 'registers': {'meas_mapped': [2, 3]}, + 'results_url': 'new-job-id/results', + } + ) + elif path == 'https://api.ionq.co/v0.3/jobs/new-job-id/results': + mock_response = mock.MagicMock() + mock_response.json = mock.MagicMock( + return_value={ + {'2': 1}, + } + ) + else: + raise ValueError(f"Unexpected URL: {path}") + return mock_response monkeypatch.setattr('requests.sessions.Session.post', mock_post) monkeypatch.setattr('requests.sessions.Session.get', mock_get) @@ -534,7 +534,7 @@ def _dummy_update(_self): 'update_devices_list', _dummy_update.__get__(None, _ionq_http_client.IonQ), ) - request_num = [0] + # request_num = [0] def mock_get(_self, path, *args, **kwargs): if path == 'https://api.ionq.co/v0.3/jobs/old-job-id': From b27b75ac39bec301e5193a7ac88397746671d037 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 20:28:54 +0000 Subject: [PATCH 5/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- projectq/backends/_ionq/_ionq_http_client.py | 8 ++------ projectq/backends/_ionq/_ionq_http_client_test.py | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index ac31a91f..cd65c5ff 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -175,12 +175,8 @@ def run(self, info, device): raise JobSubmissionError(f"{failure['code']}: {failure['error']} (status={status})") def get_result( - self, - device, - execution_id, - sharpen=None, - num_retries=3000, - interval=1): # pylint: disable=too-many-arguments,too-many-locals + self, device, execution_id, sharpen=None, num_retries=3000, interval=1 + ): # pylint: disable=too-many-arguments,too-many-locals """ Given a backend and ID, fetch the results for this job's execution. diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index 46f9617f..082e2960 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -196,7 +196,6 @@ def mock_post(_self, path, *args, **kwargs): ) return mock_response - def mock_get(_self, path, *args, **kwargs): if path == 'https://api.ionq.co/v0.3/jobs/new-job-id': mock_response = mock.MagicMock() From 11897b14406355bb1459af1dab3ae52cc23c4059 Mon Sep 17 00:00:00 2001 From: Nguyen Damien Date: Tue, 2 Apr 2024 22:31:40 +0200 Subject: [PATCH 6/8] Update CHANGELOG + fix typo --- CHANGELOG.md | 4 ++++ projectq/backends/_ionq/_ionq_http_client.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7fe748f..f72d648a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed some typos (thanks to @eltociear, @Darkdragon84) +### Changed + +- Update ProjectQ to handle IonQ API v0.3 + ### Repository - Update GitHub workflows to work with merge queues diff --git a/projectq/backends/_ionq/_ionq_http_client.py b/projectq/backends/_ionq/_ionq_http_client.py index cd65c5ff..68d62a9d 100644 --- a/projectq/backends/_ionq/_ionq_http_client.py +++ b/projectq/backends/_ionq/_ionq_http_client.py @@ -229,7 +229,7 @@ def _handle_sigint_during_get_result(*_): # pragma: no cover # Check if job is completed. if status == 'completed': r_get = super().get(urljoin(_JOB_API_URL, req_json.get('results_url')), params=params) - re_json = r_get.json() + r_json = r_get.json() meas_mapped = req_json['registers']['meas_mapped'] meas_qubit_ids = json.loads(req_json['metadata']['meas_qubit_ids']) output_probs = r_json From 6d609e16a7fe62db14545655223d660046572599 Mon Sep 17 00:00:00 2001 From: Nguyen Damien Date: Tue, 2 Apr 2024 22:42:41 +0200 Subject: [PATCH 7/8] Fix JSON response to be a string --- projectq/backends/_ionq/_ionq_http_client_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index 082e2960..dfc11b28 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -546,9 +546,9 @@ def mock_get(_self, path, *args, **kwargs): 'results_url': 'old-job-id/results', } elif path == 'https://api.ionq.co/v0.3/jobs/old-job-id/results': - json_response = { + json_response = '''{ '2': 1, - } + }''' else: raise ValueError(f"Unexpected URL: {path}") From 67e768255f3fbd462bc3e074b07bf46bcfd3b31d Mon Sep 17 00:00:00 2001 From: Spencer Churchill Date: Tue, 7 May 2024 17:20:17 -0700 Subject: [PATCH 8/8] fix json parsing --- projectq/backends/_ionq/_ionq_http_client_test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/projectq/backends/_ionq/_ionq_http_client_test.py b/projectq/backends/_ionq/_ionq_http_client_test.py index dfc11b28..3987572b 100644 --- a/projectq/backends/_ionq/_ionq_http_client_test.py +++ b/projectq/backends/_ionq/_ionq_http_client_test.py @@ -212,9 +212,7 @@ def mock_get(_self, path, *args, **kwargs): elif path == 'https://api.ionq.co/v0.3/jobs/new-job-id/results': mock_response = mock.MagicMock() mock_response.json = mock.MagicMock( - return_value={ - {'2': 1}, - } + return_value={'2': 1}, ) else: raise ValueError(f"Unexpected URL: {path}") @@ -546,9 +544,7 @@ def mock_get(_self, path, *args, **kwargs): 'results_url': 'old-job-id/results', } elif path == 'https://api.ionq.co/v0.3/jobs/old-job-id/results': - json_response = '''{ - '2': 1, - }''' + json_response = {'2': 1} else: raise ValueError(f"Unexpected URL: {path}")