Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/run_aqt_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from qiskit.circuit.library import n_local

from qiskit_scaleway import ScalewayProvider
from qiskit_scaleway.primitives import EstimatorV1
from qiskit_scaleway.primitives import Estimator

provider = ScalewayProvider(
project_id="<your-scaleway-project-id>",
Expand All @@ -33,7 +33,7 @@
session_id = backend.start_session(name="test-session", deduplication_id="my-workshop")

# Create an estimator (v1) with the backend and the session
estimator = EstimatorV1(backend=backend, session_id=session_id)
estimator = Estimator(backend=backend, session_id=session_id)

# Specify the problem Hamiltonian
hamiltonian = SparsePauliOp.from_list(
Expand All @@ -56,15 +56,15 @@ def cost_function(
params,
ansatz: QuantumCircuit,
hamiltonian: BaseOperator,
estimator: EstimatorV1,
estimator: Estimator,
) -> float:
"""Cost function for the VQE.

Return the estimated expectation value of the Hamiltonian
on the state prepared by the Ansatz circuit.
"""
return float(
estimator.run(ansatz, hamiltonian, parameter_values=params).result().values[0]
estimator.run([(ansatz, hamiltonian, params)]).result().values[0]
)


Expand Down
30 changes: 26 additions & 4 deletions qiskit_scaleway/backends/aer/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from qiskit.providers import Options, convert_to_target
from typing import List
from qiskit.providers import Options
from qiskit.transpiler import Target

from qiskit_aer.backends.aer_simulator import BASIS_GATES, AerBackendConfiguration
from qiskit_aer.backends.aerbackend import NAME_MAPPING
Expand Down Expand Up @@ -49,9 +51,8 @@ def __init__(self, provider, client: QaaSClient, platform: QaaSPlatform):
}
)
self._properties = None
self._target = convert_to_target(
self._configuration, self._properties, None, NAME_MAPPING
)

self._target = self._convert_to_target()

def __repr__(self) -> str:
return f"<AerBackend(name={self.name},num_qubits={self.num_qubits},platform_id={self.id})>"
Expand All @@ -71,6 +72,7 @@ def _default_options(self):
shots=1000,
memory=False,
seed_simulator=None,
noise_model=None,
method="automatic",
precision="double",
max_shot_size=None,
Expand Down Expand Up @@ -105,3 +107,23 @@ def _default_options(self):
fusion_max_qubit=None,
fusion_threshold=None,
)

def _convert_to_target(self) -> Target:
args_lis: List[str] = [
"basis_gates",
"num_qubits",
"coupling_map",
"instruction_durations",
"concurrent_measurements",
"dt",
"timing_constraints",
"custom_name_mapping",
]

conf_dict = self._configuration.to_dict()
if conf_dict.get("custom_name_mapping") is None:
conf_dict["custom_name_mapping"] = NAME_MAPPING

return Target.from_configuration(
**{k: conf_dict[k] for k in args_lis if k in conf_dict}
)
43 changes: 43 additions & 0 deletions qiskit_scaleway/backends/base_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import time
import zlib
import httpx
import json
import numpy as np
import randomname

from typing import List, Union, Optional, Dict
Expand All @@ -34,6 +36,8 @@
QaaSJobRunData,
QaaSJobBackendData,
QaaSCircuitSerializationFormat,
QaaSNoiseModelData,
QaaSNoiseModelSerializationFormat,
)


Expand Down Expand Up @@ -90,6 +94,22 @@ def submit(self, session_id: str) -> None:
),
)

noise_model = options.pop("noise_model", None)
if noise_model:
noise_model_dict = _encode_numpy_complex(noise_model.to_dict(False))
noise_model = QaaSNoiseModelData(
serialization_format=QaaSNoiseModelSerializationFormat.AER_COMPRESSED_JSON,
noise_model_serialization=zlib.compress(
json.dumps(noise_model_dict).encode()
),
)
### Uncomment to use standard JSON serialization, provided there is no more issue with AER deserialization logic
# noise_model = QaaSNoiseModelData(
# serialization_format = QaaSNoiseModelSerializationFormat.JSON,
# noise_model_serialization = json.dumps(noise_model.to_dict(True)).encode()
# )
###

backend_data = QaaSJobBackendData(
name=self.backend().name,
version=self.backend().version,
Expand All @@ -105,6 +125,7 @@ def submit(self, session_id: str) -> None:
backend=backend_data,
run=run_data,
client=client_data,
noise_model=noise_model,
)
)

Expand Down Expand Up @@ -196,3 +217,25 @@ def _wait_for_result(
raise JobError("Job error")

time.sleep(fetch_interval)


def _encode_numpy_complex(obj):
"""
Recursively traverses a structure and converts numpy arrays and
complex numbers into a JSON-serializable format.
"""
if isinstance(obj, np.ndarray):
return {
"__ndarray__": True,
"data": _encode_numpy_complex(obj.tolist()), # Recursively encode data
"dtype": obj.dtype.name,
"shape": obj.shape,
}
elif isinstance(obj, (complex, np.complex128)):
return {"__complex__": True, "real": obj.real, "imag": obj.imag}
elif isinstance(obj, dict):
return {key: _encode_numpy_complex(value) for key, value in obj.items()}
elif isinstance(obj, (list, tuple)):
return [_encode_numpy_complex(item) for item in obj]
else:
return obj
1 change: 0 additions & 1 deletion qiskit_scaleway/primitives/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from .estimator import Estimator
from .estimator_v1 import Estimator as EstimatorV1
from .sampler import Sampler
2 changes: 1 addition & 1 deletion qiskit_scaleway/primitives/estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from qiskit.primitives import BackendEstimatorV2
from qiskit.primitives.backend_estimator import (
from qiskit.primitives.backend_estimator_v2 import (
_prepare_counts,
_run_circuits,
)
Expand Down
41 changes: 0 additions & 41 deletions qiskit_scaleway/primitives/estimator_v1.py

This file was deleted.

6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
qiskit==1.4.2
qiskit-aer==0.17
qiskit~=2.1
qiskit-aer~=0.17
randomname>=0.2.1
dataclasses-json>=0.6.4
dataclasses>=0.6
scaleway-qaas-client>=0.1.16
scaleway-qaas-client>=0.1.23
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

setup(
name="qiskit_scaleway",
version="0.2.12",
version="0.3.0",
project_urls={
"Documentation": "https://www.scaleway.com/en/quantum-as-a-service/",
"Source": "https://github.com/scaleway/qiskit-scaleway",
Expand Down
91 changes: 90 additions & 1 deletion tests/test_aer_multiple_circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,16 @@ def _random_qiskit_circuit(size: int) -> QuantumCircuit:


def test_aer_multiple_circuits():

provider = ScalewayProvider(
project_id=os.environ["QISKIT_SCALEWAY_PROJECT_ID"],
secret_key=os.environ["QISKIT_SCALEWAY_SECRET_KEY"],
url=os.getenv("QISKIT_SCALEWAY_API_URL"),
)

backend = provider.get_backend("aer_simulation_pop_c16m128")
backend = provider.get_backend(
os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")
)

assert backend is not None

Expand Down Expand Up @@ -86,3 +89,89 @@ def test_aer_multiple_circuits():
assert result.success
finally:
backend.delete_session(session_id)


def _get_noise_model():
import qiskit_aer.noise as noise

# Error probabilities (exaggerated to get a noticeable effect for demonstration)
prob_1 = 0.01 # 1-qubit gate
prob_2 = 0.1 # 2-qubit gate

# Depolarizing quantum errors
error_1 = noise.depolarizing_error(prob_1, 1)
error_2 = noise.depolarizing_error(prob_2, 2)

# Add errors to noise model
noise_model = noise.NoiseModel()
noise_model.add_all_qubit_quantum_error(error_1, ["rz", "sx", "x"])
noise_model.add_all_qubit_quantum_error(error_2, ["cx"])

return noise_model


def _bell_state_circuit():
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
return qc


def _simple_one_state_circuit():
qc = QuantumCircuit(1, 1)
qc.x(0)
qc.measure_all()
return qc


def test_aer_with_noise_model():

provider = ScalewayProvider(
project_id=os.environ["QISKIT_SCALEWAY_PROJECT_ID"],
secret_key=os.environ["QISKIT_SCALEWAY_SECRET_KEY"],
url=os.getenv("QISKIT_SCALEWAY_API_URL"),
)

backend = provider.get_backend(
os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")
)

assert backend is not None

session_id = backend.start_session(
name="my-aer-session-autotest",
deduplication_id=f"my-aer-session-autotest-{random.randint(1, 1000)}",
max_duration="15m",
)

assert session_id is not None

try:
qc1 = _bell_state_circuit()
qc2 = _simple_one_state_circuit()

run_ideal_result = backend.run(
[qc1, qc2],
shots=1000,
max_parallel_experiments=0,
session_id=session_id,
).result()

run_noisy_result = backend.run(
[qc1, qc2],
shots=1000,
max_parallel_experiments=0,
session_id=session_id,
noise_model=_get_noise_model(),
).result()

ideal_results = run_ideal_result.results
noisy_results = run_noisy_result.results

assert len(ideal_results) == len(noisy_results) == 2

for i, ideal_result in enumerate(ideal_results):
assert len(ideal_result.data.counts) < len(noisy_results[i].data.counts)
finally:
backend.delete_session(session_id)
4 changes: 3 additions & 1 deletion tests/test_estimator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ def test_estimator():
url=os.getenv("QISKIT_SCALEWAY_API_URL"),
)

backend = provider.get_backend("aer_simulation_pop_c16m128")
backend = provider.get_backend(
os.getenv("QISKIT_SCALEWAY_BACKEND_NAME", "aer_simulation_pop_c16m128")
)

assert backend is not None

Expand Down
4 changes: 3 additions & 1 deletion tests/test_qsim_simple_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def test_qsim_simple_circuit():
url=os.getenv("QISKIT_SCALEWAY_API_URL"),
)

backend = provider.get_backend("qsim_simulation_pop_c16m128")
backend = provider.get_backend(
os.getenv("QSIM_SCALEWAY_BACKEND_NAME", "qsim_simulation_pop_c16m128")
)

assert backend is not None

Expand Down
Loading