Skip to content

Commit

Permalink
Merge pull request #97 from qiboteam/feature/refactor-webapp-endpoints
Browse files Browse the repository at this point in the history
Requests reflect the new endpoint structure
  • Loading branch information
scarrazza authored Jan 11, 2025
2 parents 4280674 + 8b8d5f2 commit c0f6440
Show file tree
Hide file tree
Showing 9 changed files with 558 additions and 110 deletions.
20 changes: 13 additions & 7 deletions examples/run_successful_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@
from qibo_client import Client

# create the circuit you want to run
circuit = qibo.Circuit(11)
circuit.add(qibo.gates.GPI2(0, 0))
circuit.add(qibo.gates.M(10))
circuit = qibo.Circuit(17)
circuit.add(qibo.gates.GPI2(0, 0.0))
circuit.add(qibo.gates.GPI2(1, 0.0))
circuit.add(qibo.gates.GPI2(2, 0.0))
circuit.add(qibo.gates.M(0, 1, 2))

print(circuit.draw())
wire_names = [f"A{i}" for i in range(1, 18)]

circuit.wire_names = wire_names

circuit.draw()

# read the token from file
token_path = Path(__file__).parent / "token.txt"
token = token_path.read_text()

# authenticate to server through the client instance
client = Client(token)
client = Client(token) # , url="http://localhost:8011")

# run the circuit
print(f"{'*'*20}\nPost circuit")
start = time.time()
job = client.run_circuit(circuit, device="etna", project="Test 1", nshots=100)
print(job.result())
job = client.run_circuit(circuit, device="k2", project="personal", nshots=150)
print(job.result(wait=3, verbose=True))
print(f"Program done in {time.time() - start:.4f}s")
302 changes: 300 additions & 2 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ packaging = "^24.1"
[tool.poetry.group.dev.dependencies]
ipython = "^8"
pdbpp = "^0.10.3"
jsf = "^0.11.2"

[tool.poetry.group.test.dependencies]
pytest = "^7.4.3"
Expand Down
43 changes: 23 additions & 20 deletions src/qibo_client/qibo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, token: str, url: str = constants.BASE_URL):
:type url: str
"""
self.token = token
self.headers = {"x-api-token": token}
self.base_url = url

self.pid = None
Expand Down Expand Up @@ -68,7 +69,7 @@ def run_circuit(
self,
circuit: qibo.Circuit,
device: str,
project: str,
project: str = "personal",
nshots: T.Optional[int] = None,
verbatim: bool = False,
) -> T.Optional[
Expand Down Expand Up @@ -118,10 +119,9 @@ def _post_circuit(
nshots: T.Optional[int] = None,
verbatim: bool = False,
) -> QiboJob:
url = self.base_url + "/api/run_circuit/"
url = self.base_url + "/api/jobs/"

payload = {
"token": self.token,
"circuit": circuit.raw,
"nshots": nshots,
"device": device,
Expand All @@ -130,6 +130,7 @@ def _post_circuit(
}
response = QiboApiRequest.post(
url,
headers=self.headers,
json=payload,
timeout=constants.TIMEOUT,
)
Expand All @@ -142,6 +143,7 @@ def _post_circuit(

return QiboJob(
base_url=self.base_url,
headers=self.headers,
pid=self.pid,
circuit=circuit.raw,
nshots=nshots,
Expand All @@ -150,20 +152,25 @@ def _post_circuit(

def print_quota_info(self):
"""Logs the formatted user quota info table."""
url = self.base_url + "/api/info/quotas/"
url = self.base_url + "/api/disk_quota/"

payload = {
"token": self.token,
}
response = QiboApiRequest.post(
response = QiboApiRequest.get(
url,
json=payload,
headers=self.headers,
timeout=constants.TIMEOUT,
)

disk_quota = response.json()[0]

url = self.base_url + "/api/projectquotas/"

response = QiboApiRequest.get(
url,
headers=self.headers,
timeout=constants.TIMEOUT,
keys_to_check=["disk_quota", "projectquotas"],
)

disk_quota = response.json()["disk_quota"]
projectquotas = response.json()["projectquotas"]
projectquotas = response.json()

message = (
f"User: {disk_quota['user']['email']}\n"
Expand Down Expand Up @@ -202,14 +209,11 @@ def print_quota_info(self):

def print_job_info(self):
"""Logs the formatted user quota info table."""
url = self.base_url + "/api/info/jobs/"
url = self.base_url + "/api/jobs/"

payload = {
"token": self.token,
}
response = QiboApiRequest.post(
response = QiboApiRequest.get(
url,
json=payload,
headers=self.headers,
timeout=constants.TIMEOUT,
)

Expand All @@ -225,8 +229,7 @@ def format_date(dt: str) -> str:
user_set = {job["user"]["email"] for job in jobs}
if len(user_set) > 1:
raise ValueError(
"The `/api/info/jobs/` endpoint returned info about "
"multiple accounts."
"The `/api/jobs/` endpoint returned info about " "multiple accounts."
)
user = list(user_set)[0]

Expand Down
30 changes: 20 additions & 10 deletions src/qibo_client/qibo_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ def __init__(
self,
pid: str,
base_url: str = constants.BASE_URL,
headers: T.Dict[str, str] = None,
circuit: T.Optional[qibo.Circuit] = None,
nshots: T.Optional[int] = None,
device: T.Optional[str] = None,
):
self.base_url = base_url
self.headers = headers
self.pid = pid
self.circuit = circuit
self.nshots = nshots
Expand All @@ -93,11 +95,12 @@ def refresh(self):
This method does not query the results from server.
"""
url = self.base_url + f"/job/info/{self.pid}/"
response = response = QiboApiRequest.get(
url = self.base_url + f"/api/jobs/{self.pid}/"
response = QiboApiRequest.get(
url,
headers=self.headers,
timeout=constants.TIMEOUT,
keys_to_check=["circuit", "nshots", "device", "status"],
keys_to_check=["circuit", "nshots", "projectquota", "status"],
)

info = response.json()
Expand All @@ -107,13 +110,16 @@ def refresh(self):
def _update_job_info(self, info: T.Dict):
self.circuit = info.get("circuit")
self.nshots = info.get("nshots")
self.device = info["device"].get("name")
self.device = info["projectquota"]["partition"]["name"]
self._status = convert_str_to_job_status(info["status"])

def status(self) -> QiboJobStatus:
url = self.base_url + f"/job/info/{self.pid}/"
url = self.base_url + f"/api/jobs/{self.pid}/"
response = QiboApiRequest.get(
url, timeout=constants.TIMEOUT, keys_to_check=["status"]
url,
headers=self.headers,
timeout=constants.TIMEOUT,
keys_to_check=["status"],
)
status = response.json()["status"]
self._status = convert_str_to_job_status(status)
Expand Down Expand Up @@ -204,10 +210,12 @@ def _wait_for_response_to_get_request(
if not verbose and is_job_finished:
logger.info("Please wait until your job is completed...")

url = self.base_url + f"/job/result/{self.pid}/"
url = self.base_url + f"/api/jobs/result/{self.pid}/"

while True:
response = QiboApiRequest.get(url, timeout=constants.TIMEOUT)
response = QiboApiRequest.get(
url, headers=self.headers, timeout=constants.TIMEOUT
)
job_status = convert_str_to_job_status(response.headers["Job-Status"])

if verbose and job_status == QiboJobStatus.QUEUEING:
Expand All @@ -225,6 +233,8 @@ def _wait_for_response_to_get_request(
time.sleep(seconds_between_checks)

def delete(self) -> str:
url = self.base_url + f"/api/delete/job/{self.pid}/"
response = QiboApiRequest.delete(url, timeout=constants.TIMEOUT)
url = self.base_url + f"/api/jobs/{self.pid}/"
response = QiboApiRequest.delete(
url, headers=self.headers, timeout=constants.TIMEOUT
)
return response.json()["detail"]
21 changes: 18 additions & 3 deletions src/qibo_client/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,28 +49,43 @@ class QiboApiRequest:
def get(
endpoint: str,
params: T.Optional[T.Dict] = None,
headers: T.Optional[T.Dict] = None,
timeout: T.Optional[float] = None,
keys_to_check: T.Optional[T.List[str]] = None,
) -> requests.Response:
return _make_request(
requests.get, keys_to_check, endpoint, params=params, timeout=timeout
requests.get,
keys_to_check,
endpoint,
params=params,
headers=headers,
timeout=timeout,
)

@staticmethod
def post(
endpoint: str,
headers: T.Optional[T.Dict] = None,
json: T.Optional[T.Dict] = None,
timeout: T.Optional[float] = None,
keys_to_check: T.Optional[T.List[str]] = None,
) -> requests.Response:
return _make_request(
requests.post, keys_to_check, endpoint, json=json, timeout=timeout
requests.post,
keys_to_check,
endpoint,
headers=headers,
json=json,
timeout=timeout,
)

@staticmethod
def delete(
endpoint: str,
timeout: T.Optional[float] = None,
headers: T.Optional[T.Dict] = None,
keys_to_check: T.Optional[T.List[str]] = None,
) -> requests.Response:
return _make_request(requests.delete, keys_to_check, endpoint, timeout=timeout)
return _make_request(
requests.delete, keys_to_check, endpoint, headers=headers, timeout=timeout
)
106 changes: 106 additions & 0 deletions tests/fixs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
USER_SCHEMA = {
"type": "object",
"properties": {"email": {"type": "string", "format": "email"}},
"required": ["email"],
}

PARTITION_SCHEMA = {
"type": "object",
"properties": {
"name": {"type": "string"},
"max_num_qubits": {"type": "number"},
"hardware_type": {
"type": "string",
"enum": [
"simulator",
"hardware",
],
},
"description": {"type": "string"},
"status": {
"type": "string",
"enum": [
"available",
"unavailable",
],
},
},
"required": ["name", "max_num_qubits", "hardware_type", "description", "status"],
}

PROJECTQUOTA_SCHEMA = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"partition": PARTITION_SCHEMA,
"max_num_shots": {"type": "integer"},
"max_walltime_seconds": {"type": "number"},
"seconds_left": {"type": "number"},
"seconds_max": {"type": "number"},
"shots_left": {"type": "number"},
"shots_max": {"type": "number"},
"jobs_left": {"type": "number"},
"jobs_max": {"type": "number"},
"prealloc_seconds": {"type": "number"},
"prealloc_shots": {"type": "number"},
"prealloc_jobs": {"type": "number"},
},
"required": [
"id",
"partition",
"max_num_shots",
"max_walltime_seconds",
"seconds_left",
"seconds_max",
"shots_left",
"shots_max",
"jobs_left",
"jobs_max",
"prealloc_seconds",
"prealloc_shots",
"prealloc_jobs",
],
}

JOB_SCHEMA = {
"type": "object",
"properties": {
"pid": {"type": "string"},
"user": USER_SCHEMA,
"projectquota": PROJECTQUOTA_SCHEMA,
"circuit": {"type": ["string", "object"]},
"transpiled_circuit": {"type": ["null", "string"]},
"num_qubits": {"type": "integer"},
"status": {
"type": "string",
"enum": [
"queueing",
"pending",
"running",
"postprocessing",
"success",
"ERROR",
],
},
"created_at": {"type": "string", "format": "date-time"},
"updated_at": {"type": "string", "format": "date-time"},
"result_path": {"type": "string"},
"frequencies": {"type": ["null", "object"]},
"nshots": {"type": "integer"},
"stdout": {"type": "string"},
"stderr": {"type": "string"},
"qibo_version": {"type": ["null", "string"]},
"qibolab_version": {"type": ["null", "string"]},
"verbatim": {"type": "boolean"},
"runtime": {"type": ["null", "number"]},
},
"required": [
"pid",
"user",
"projectquota",
"circuit",
"status",
"created_at",
"updated_at",
],
}
Loading

0 comments on commit c0f6440

Please sign in to comment.