Skip to content

Commit

Permalink
validate num_qubits in qscout compile (#766)
Browse files Browse the repository at this point in the history
  • Loading branch information
richrines1 authored Sep 13, 2023
1 parent 4dc3267 commit 972d201
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 14 deletions.
15 changes: 12 additions & 3 deletions cirq-superstaq/cirq_superstaq/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,11 @@ def qscout_compile(
**kwargs,
}

if circuits_is_list:
max_circuit_qubits = max(cirq.num_qubits(c) for c in circuits)
else:
max_circuit_qubits = cirq.num_qubits(circuits)

if error_rates is not None:
error_rates_list = list(error_rates.items())
options_dict["error_rates"] = error_rates_list
Expand All @@ -590,9 +595,13 @@ def qscout_compile(
max_index = max(q for qs, _ in error_rates_list for q in qs)
num_qubits = max_index + 1

if num_qubits is not None:
gss.validation.validate_integer_param(num_qubits)
options_dict["num_qubits"] = num_qubits
elif num_qubits is None:
num_qubits = max_circuit_qubits

gss.validation.validate_integer_param(num_qubits)
if num_qubits < max_circuit_qubits:
raise ValueError(f"At least {max_circuit_qubits} qubits are required for this input.")
options_dict["num_qubits"] = num_qubits

json_dict = self._client.qscout_compile(
{
Expand Down
37 changes: 37 additions & 0 deletions cirq-superstaq/cirq_superstaq/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,41 @@ def test_service_qscout_compile_single(mock_qscout_compile: mock.MagicMock) -> N
service.qscout_compile(cirq.Circuit(), target="ss_example_qpu")


@mock.patch("general_superstaq.superstaq_client._SuperstaqClient.qscout_compile")
def test_service_qscout_compile_multiple(mock_qscout_compile: mock.MagicMock) -> None:
q0, q1 = cirq.LineQubit.range(2)
circuits = [
cirq.Circuit(cirq.H(q0), cirq.measure(q0)),
cirq.Circuit(cirq.ISWAP(q0, q1)),
]
final_logical_to_physicals = [{q0: q0}, {q0: q1, q1: q0}]

jaqal_programs = ["jaqal", "programs"]

mock_qscout_compile.return_value = {
"cirq_circuits": css.serialization.serialize_circuits(circuits),
"final_logical_to_physicals": cirq.to_json(
[list(l2p.items()) for l2p in final_logical_to_physicals]
),
"jaqal_programs": jaqal_programs,
}

service = css.Service(api_key="key", remote_host="http://example.com")
out = service.qscout_compile(circuits)
assert out.circuits == circuits
assert out.final_logical_to_physicals == final_logical_to_physicals
assert out.jaqal_programs == jaqal_programs

assert json.loads(mock_qscout_compile.call_args[0][0]["options"]) == {
"mirror_swaps": False,
"base_entangling_gate": "xx",
"num_qubits": 2,
}

with pytest.raises(ValueError, match="At least 2 qubits are required"):
_ = service.qscout_compile(circuits, num_qubits=1)


@mock.patch("general_superstaq.superstaq_client._SuperstaqClient.qscout_compile")
@pytest.mark.parametrize("mirror_swaps", (True, False))
def test_qscout_compile_swap_mirror(
Expand All @@ -456,6 +491,7 @@ def test_qscout_compile_swap_mirror(
assert json.loads(mock_qscout_compile.call_args[0][0]["options"]) == {
"mirror_swaps": mirror_swaps,
"base_entangling_gate": "xx",
"num_qubits": 1,
}


Expand Down Expand Up @@ -513,6 +549,7 @@ def test_qscout_compile_base_entangling_gate(
assert json.loads(mock_qscout_compile.call_args[0][0]["options"]) == {
"mirror_swaps": False,
"base_entangling_gate": base_entangling_gate,
"num_qubits": 1,
}


Expand Down
15 changes: 12 additions & 3 deletions qiskit-superstaq/qiskit_superstaq/superstaq_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ def qscout_compile(
"base_entangling_gate": base_entangling_gate,
}

if isinstance(circuits, qiskit.QuantumCircuit):
max_circuit_qubits = circuits.num_qubits
else:
max_circuit_qubits = max(c.num_qubits for c in circuits)

if error_rates is not None:
error_rates_list = list(error_rates.items())
options["error_rates"] = error_rates_list
Expand All @@ -363,9 +368,13 @@ def qscout_compile(
max_index = max(q for qs, _ in error_rates_list for q in qs)
num_qubits = max_index + 1

if num_qubits is not None:
gss.validation.validate_integer_param(num_qubits)
options["num_qubits"] = num_qubits
elif num_qubits is None:
num_qubits = max_circuit_qubits

gss.validation.validate_integer_param(num_qubits)
if num_qubits < max_circuit_qubits:
raise ValueError(f"At least {max_circuit_qubits} qubits are required for this input.")
options["num_qubits"] = num_qubits

request_json = self._get_compile_request_json(circuits, **options)
json_dict = self._provider._client.qscout_compile(request_json)
Expand Down
27 changes: 19 additions & 8 deletions qiskit-superstaq/qiskit_superstaq/superstaq_provider_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,23 @@ def test_qscout_compile(
assert out.circuits == [qc]
assert out.final_logical_to_physicals == [{0: 13}]

qc2 = qiskit.QuantumCircuit(2)
mock_post.return_value.json = lambda: {
"qiskit_circuits": qss.serialization.serialize_circuits([qc, qc]),
"final_logical_to_physicals": json.dumps([[(0, 13)], [(0, 13)]]),
"qiskit_circuits": qss.serialization.serialize_circuits([qc, qc2]),
"final_logical_to_physicals": json.dumps([[(0, 13)], [(0, 13), (1, 11)]]),
"jaqal_programs": [jaqal_program, jaqal_program],
}
out = fake_superstaq_provider.qscout_compile([qc, qc])
assert out.circuits == [qc, qc]
assert out.final_logical_to_physicals == [{0: 13}, {0: 13}]
out = fake_superstaq_provider.qscout_compile([qc, qc2])
assert out.circuits == [qc, qc2]
assert out.final_logical_to_physicals == [{0: 13}, {0: 13, 1: 11}]
assert json.loads(mock_post.call_args.kwargs["json"]["options"]) == {
"mirror_swaps": False,
"base_entangling_gate": "xx",
"num_qubits": 2,
}

with pytest.raises(ValueError, match="At least 2 qubits are required"):
_ = fake_superstaq_provider.qscout_compile([qc, qc2], num_qubits=1)


def test_invalid_target_qscout_compile(fake_superstaq_provider: MockSuperstaqProvider) -> None:
Expand All @@ -246,7 +255,7 @@ def test_invalid_target_qscout_compile(fake_superstaq_provider: MockSuperstaqPro
def test_qscout_compile_swap_mirror(
mock_post: MagicMock, mirror_swaps: bool, fake_superstaq_provider: MockSuperstaqProvider
) -> None:
qc = qiskit.QuantumCircuit()
qc = qiskit.QuantumCircuit(1)

mock_post.return_value.json = lambda: {
"qiskit_circuits": qss.serialization.serialize_circuits(qc),
Expand All @@ -259,6 +268,7 @@ def test_qscout_compile_swap_mirror(
assert json.loads(mock_post.call_args.kwargs["json"]["options"]) == {
"mirror_swaps": mirror_swaps,
"base_entangling_gate": "xx",
"num_qubits": 1,
}

_ = fake_superstaq_provider.qscout_compile(qc, mirror_swaps=mirror_swaps, num_qubits=3)
Expand All @@ -274,7 +284,7 @@ def test_qscout_compile_swap_mirror(
def test_qscout_compile_change_entangler(
mock_post: MagicMock, base_entangling_gate: str, fake_superstaq_provider: MockSuperstaqProvider
) -> None:
qc = qiskit.QuantumCircuit()
qc = qiskit.QuantumCircuit(2)

mock_post.return_value.json = lambda: {
"qiskit_circuits": qss.serialization.serialize_circuits(qc),
Expand All @@ -287,6 +297,7 @@ def test_qscout_compile_change_entangler(
assert json.loads(mock_post.call_args.kwargs["json"]["options"]) == {
"mirror_swaps": False,
"base_entangling_gate": base_entangling_gate,
"num_qubits": 2,
}

_ = fake_superstaq_provider.qscout_compile(
Expand All @@ -310,7 +321,7 @@ def test_qscout_compile_wrong_entangler(fake_superstaq_provider: MockSuperstaqPr
def test_qscout_compile_error_rates(
mock_post: MagicMock, fake_superstaq_provider: MockSuperstaqProvider
) -> None:
circuit = qiskit.QuantumCircuit()
circuit = qiskit.QuantumCircuit(1)

mock_post.return_value.json = lambda: {
"qiskit_circuits": qss.serialization.serialize_circuits(circuit),
Expand Down

0 comments on commit 972d201

Please sign in to comment.