Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve old jobs #1099

Merged
merged 11 commits into from
Nov 13, 2024
18 changes: 9 additions & 9 deletions docs/source/get_started/access_info/access_info_qss.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
]
},
{
"cell_type": "markdown",
"id": "domestic-bryan",
"metadata": {},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Infleqtion/client-superstaq/blob/main/docs/source/get_started/access_info/access_info_qss.ipynb) [![Launch Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/Infleqtion/client-superstaq/HEAD?labpath=docs/source/get_started/access_info/access_info_qss.ipynb)"
]
},
"cell_type": "markdown",
"id": "domestic-bryan",
"metadata": {},
"source": [
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Infleqtion/client-superstaq/blob/main/docs/source/get_started/access_info/access_info_qss.ipynb) [![Launch Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/Infleqtion/client-superstaq/HEAD?labpath=docs/source/get_started/access_info/access_info_qss.ipynb)"
]
},
{
"cell_type": "markdown",
"id": "70537a94",
Expand Down Expand Up @@ -679,7 +679,7 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": null,
"id": "16bdcfff",
"metadata": {},
"outputs": [
Expand All @@ -698,7 +698,7 @@
"source": [
"backend = provider.get_backend(\"ibmq_brisbane_qpu\")\n",
"job_id = job.job_id() # Here we use the job ID from above, but this can be any old job ID\n",
"job_old = backend.retrieve_job(job_id)\n",
"job_old = provider.retrieve_job(job_id)\n",
"job_old.input_circuits(index=0).draw(output=\"mpl\")"
]
}
Expand Down
15 changes: 0 additions & 15 deletions qiskit-superstaq/qiskit_superstaq/superstaq_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,21 +170,6 @@ def run(

return job

def retrieve_job(self, job_id: str) -> qss.SuperstaqJob:
"""Gets a job that has been created on the Superstaq API.

Args:
job_id: The UUID of the job. Jobs are assigned these numbers by the server during the
creation of the job.

Returns:
A `qss.SuperstaqJob` which can be queried for status or results.

Raises:
~gss.SuperstaqServerException: If there was an error accessing the API.
"""
return qss.SuperstaqJob(self, job_id)

def compile(
self,
circuits: qiskit.QuantumCircuit | Sequence[qiskit.QuantumCircuit],
Expand Down
14 changes: 0 additions & 14 deletions qiskit-superstaq/qiskit_superstaq/superstaq_backend_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,6 @@ def test_multi_arg_run(fake_superstaq_provider: MockSuperstaqProvider) -> None:
assert answer == expected


def test_retrieve_job(fake_superstaq_provider: MockSuperstaqProvider) -> None:
qc = qiskit.QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 0], [1, 1])
backend = fake_superstaq_provider.get_backend("ibmq_brisbane_qpu")
with patch(
"general_superstaq.superstaq_client._SuperstaqClient.create_job",
return_value={"job_ids": ["job_id"], "status": "ready"},
):
job = backend.run(qc, shots=1000)
assert job == backend.retrieve_job("job_id")


def test_eq(fake_superstaq_provider: MockSuperstaqProvider) -> None:
backend1 = fake_superstaq_provider.get_backend("ibmq_brisbane_qpu")
assert backend1 != 3
Expand Down
16 changes: 16 additions & 0 deletions qiskit-superstaq/qiskit_superstaq/superstaq_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,22 @@ def backends(
superstaq_backends.append(self.get_backend(backend.target))
return superstaq_backends

def retrieve_job(self, job_id: str) -> qss.SuperstaqJob:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to get_job for cirq-superstaq parity

"""Gets a job that has been created on the Superstaq API.

Args:
job_id: The UUID of the job. Jobs are assigned these numbers by the server during the
creation of the job.

Returns:
A `qss.SuperstaqJob` which can be queried for status or results.

Raises:
~gss.SuperstaqServerException: If there was an error accessing the API.
"""
job = self._client.fetch_jobs([job_id])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should also be able to multi-circuit jobs (via a comma-separated job_id), e.g.

Suggested change
job = self._client.fetch_jobs([job_id])
jobs = self._client.fetch_jobs(job_id.split(","))

(and with the logic below updated accordingly)

in this case we'll also probably want to check that all the retrieved jobs have the same target, and if not throw an error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We opted to fetch one job to match the implementation in cirq_superstaq. Should that also be able to retrieve multi-circuit jobs?

Copy link
Contributor

@richrines1 richrines1 Nov 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

css.Service.get_job() accepts comma-separated job ids - e.g. this works for me:

service = css.Service()
job = service.create_job(
    [
        cirq.Circuit(cirq.measure(cirq.q(0))),
        cirq.Circuit(cirq.X(cirq.q(0)), cirq.measure(cirq.q(0)))
    ],
    target="ss_unconstrained_simulator",
    method="dry-run",
)
print(job.job_id())  # will have a comma, e.g. "bc58eb54-5787-4328-bd45-a53f0cd609c,66f63a82-822d-43f0-8c1d-dce1923ec67"

new_job = service.get_job(job.job_id())
print(new_job.counts(0))  # {"0": 1000}
print(new_job.counts(1))  # {"1": 1000}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad! I looked at the docstring for get_job() and it seemed like it only took a single job id.

  def get_job(self, job_id: str) -> css.job.Job:
        """Gets a job that has been created on the Superstaq API.

        Args:
            job_id: The UUID of the job. Jobs are assigned these numbers by the server during the
            creation of the job.

        Returns:
            A `css.Job` which can be queried for status or results.

        Raises:
            ~gss.SuperstaqServerException: If there was an error accessing the API.
        """
        return css.job.Job(client=self._client, job_id=job_id)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah that's confusing lol. i think the idea was that a job id with commas is still just a single "job id", but for a multi-circuit job?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that makes sense.

return qss.SuperstaqJob(self.get_backend(job[job_id]["target"]), job_id)

def resource_estimate(
self, circuits: qiskit.QuantumCircuit | Sequence[qiskit.QuantumCircuit], target: str
) -> gss.ResourceEstimate | list[gss.ResourceEstimate]:
Expand Down
32 changes: 32 additions & 0 deletions qiskit-superstaq/qiskit_superstaq/superstaq_provider_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,38 @@ def test_get_balance() -> None:
assert ss_provider.get_balance(pretty_output=False) == 12345.6789


@patch("requests.Session.post")
def test_retrieve_job(mock_post: MagicMock, fake_superstaq_provider: MockSuperstaqProvider) -> None:
qc = qiskit.QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 0], [1, 1])
backend = fake_superstaq_provider.get_backend("ibmq_brisbane_qpu")

with patch(
"general_superstaq.superstaq_client._SuperstaqClient.create_job",
return_value={"job_ids": ["job_id"], "status": "ready"},
):
job = backend.run(qc, method="dry-run", shots=100)

mock_post.return_value.json = lambda: {
"job_id": {
"provider_id": "placeholder_simprovider_id",
"num_qubits": 2,
"status": "ready",
"target": "ibmq_brisbane_qpu",
"data": {"histogram": {"11": 0.54, "00": 0.46}},
"samples": {"11": 54, "00": 46},
"shots": 100,
"state_vector": None,
"pulse_gate_circuits": None,
"circuit_type": "qiskit_circuits",
}
}

assert job == fake_superstaq_provider.retrieve_job("job_id")


@patch("requests.Session.post")
def test_aqt_compile(mock_post: MagicMock, fake_superstaq_provider: MockSuperstaqProvider) -> None:
qc = qiskit.QuantumCircuit(8)
Expand Down