Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into job-estimation-values
Browse files Browse the repository at this point in the history
  • Loading branch information
kt474 committed Aug 1, 2023
2 parents c0f9793 + 8a2b1c0 commit 428a11a
Show file tree
Hide file tree
Showing 20 changed files with 892 additions and 90 deletions.
79 changes: 51 additions & 28 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,34 @@ Contributing to qiskit-ibm-provider
In addition to the general guidelines there are specific details for
contributing to the qiskit-ibm-provider, these are documented below.

### Pull request checklist

### Initial Steps

1. Fork your own copy of qiskit-ibm-provider repository, then clone it locally.
2. Setup a virtual environment based on the requirements.txt and requirements-dev.txt
files.
3. Create a new branch and then make your contributions on it.


### Pushing a new branch

Before pushing your contribution please ensure that:

1. The code follows the code style of this repository. For convenience, you can
check [Style guide](#style-guide)
2. If it makes sense, add tests that cover the changes.
3. Make sure all tests pass. For convenience, you can verify the [Test Types](#test-types).
4. The documentation has been updated accordingly. In particular, if a
function or class has been modified during your contribution, please update
the *docstring* accordingly. For convenience, you can check [Building the
Documentation Locally](#building-documentation-locally).

### Pull request creation

When submitting a pull request and you feel it is ready for review,
please ensure that:

1. The code follows the code style of the project and successfully
passes the tests. For convenience, you can execute `tox` locally,
which will run these checks and report any issues.
2. The documentation has been updated accordingly. In particular, if a
function or class has been modified during the PR, please update the
*docstring* accordingly.
3. If it makes sense for your change that you have added new tests that
cover the changes.
4. Ensure that if your change has an end user facing impact (new feature,
deprecation, removal etc) that you have added a reno release note for that
change and that the PR is tagged for the changelog.
if your change has an end user facing impact (new feature, deprecation, removal
etc) please ensure that you add a reno release note and that the PR is tagged
for the changelog.

### Changelog generation

Expand Down Expand Up @@ -144,7 +156,7 @@ After you've finished writing your release notes you'll want to add the note
file to your commit with `git add` and commit them to your PR branch to make
sure they're included with the code in your PR.

##### Linking to issues
#### Linking to issues

If you need to link to an issue or other github artifact as part of the release
note this should be done using an inline link with the text being the issue
Expand Down Expand Up @@ -181,7 +193,7 @@ release and the output will be submitted as a pull request to the documentation
repository's [release notes file](
https://github.com/Qiskit/qiskit/blob/master/docs/release_notes.rst)

#### Building release notes locally
### Building documentation and release notes locally

Building The release notes are part of the standard qiskit-ibm-provider
documentation builds. To check what the rendered html output of the release
Expand All @@ -199,17 +211,22 @@ Select the _Install from source_ tab and scroll down to _Installing IBM Quantum
### Test

#### Test Types
There are three different types of tests in `qiskit-ibm-provider`. The implementation is based upon the well-documented [unittest](https://docs.python.org/3/library/unittest.html) Unit testing framework.
There are three different types of tests in `qiskit-ibm-provider`. The
implementation is based upon the well-documented [unittest](https://docs.python.org/3/library/unittest.html) Unit testing framework.

##### 1. Unit tests
Run locally without connecting to an external system. They are short-running, stable and give a basic level of confidence during development.
Run locally without connecting to an external system. They are short-running,
stable and give a basic level of confidence during development.

To execute all unit tests, run:
``` {.bash}
$ make unit-test
```
##### 2. Integration tests
Executed against an external system configured via a (token, instance, url) tuple. Detailed coverage of happy and non-happy paths. They are long-running and unstable at times. A successful test run gives a high level of confidence that client and APIs work well together.
Executed against an external system configured via a (token, instance, url)
tuple. Detailed coverage of happy and non-happy paths. They are long-running and
unstable at times. A successful test run gives a high level of confidence that
client and APIs work well together.

To execute all integration tests, run
``` {.bash}
Expand All @@ -218,16 +235,19 @@ $ make integration-test-1 integration-test-2 integration-test-3

##### 3. E2E tests

Executed against an external system configured via a (token, instance, url) tuple. Basic coverage of most important user-facing happy paths. Test suite runs faster than integration but slower than unit tests and is stable.
Executed against an external system configured via a (token, instance, url)
tuple. Basic coverage of most important user-facing happy paths. Test suite runs
faster than integration but slower than unit tests and is stable.

To execute all e2e tests, run
``` {.bash}
$ make e2e-test
```

#### Configuration
###### Configuration

Integration and E2E tests require an environment configuration and will be run against the IBM Quantum API ("ibm_quantum").
Integration and E2E tests require an environment configuration and will be run
against the IBM Quantum API ("ibm_quantum").


Sample configuration for IBM Quantum
Expand All @@ -237,11 +257,15 @@ QISKIT_IBM_URL=https://auth.quantum-computing.ibm.com/api # IBM Quantum AP
QISKIT_IBM_INSTANCE=ibm-q/open/main # IBM Quantum provider to use (hub/group/project)
```

To enable test cases against external system in your private fork, make sure to set above values as
To enable test cases against external system in your private fork's CI workflow, make sure to set above values as
[encrypted environment secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-an-environment).
The names of the environments must match the ones that the [CI workflow](.github/workflows/ci.yml) relies
upon.

To run integration and E2E tests locally, set environment variables in your terminal.
In Linux or MacOS, this is accomplished using the `export` command.
In Windows, the method will depend on which console you are using.

### Style guide

Please submit clean code and please make effort to follow existing
Expand All @@ -255,11 +279,10 @@ commands:

All platforms:

``` {.sh}
$> cd out
out$> make lint
out$> make style
out$> make mypy
```bash
make lint
make style
make mypy
```

### Development Cycle
Expand Down
2 changes: 1 addition & 1 deletion qiskit_ibm_provider/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class IBMAccountError(IBMError):


class IBMProviderError(IBMError):
"""Base class for rrrors raise by IBMProvider."""
"""Base class for errors raise by IBMProvider."""

pass

Expand Down
7 changes: 3 additions & 4 deletions qiskit_ibm_provider/ibm_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ def run(
shots = int(shots)
if not self.configuration().simulator:
circuits = self._deprecate_id_instruction(circuits)
options = {"backend": self.name}

run_config_dict = self._get_run_config(
program_id=program_id,
Expand Down Expand Up @@ -515,7 +514,7 @@ def run(
return self._runtime_run(
program_id=program_id,
inputs=run_config_dict,
options=options,
backend_name=self.name,
job_tags=job_tags,
image=image,
)
Expand All @@ -524,7 +523,7 @@ def _runtime_run(
self,
program_id: str,
inputs: Dict,
options: Dict,
backend_name: str,
job_tags: Optional[List[str]] = None,
image: Optional[str] = None,
) -> IBMCircuitJob:
Expand All @@ -533,7 +532,7 @@ def _runtime_run(
try:
response = self.provider._runtime_client.program_run(
program_id=program_id,
backend_name=options["backend"],
backend_name=backend_name,
params=inputs,
hgp=hgp_name,
job_tags=job_tags,
Expand Down
12 changes: 7 additions & 5 deletions qiskit_ibm_provider/ibm_backend_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from .utils.hgp import to_instance_format

logger = logging.getLogger(__name__)
PAGE_SIZE = 50


class IBMBackendService:
Expand Down Expand Up @@ -322,7 +323,6 @@ def jobs(
)
)
job_list = []
original_limit = limit
while True:
job_responses = self._get_jobs(
api_filter=api_filter,
Expand Down Expand Up @@ -362,9 +362,9 @@ def jobs(
)
continue
job_list.append(job)
if len(job_list) == original_limit:
if limit and len(job_list) == limit:
return job_list
skip += limit
skip += len(job_responses)
return job_list

def _get_jobs(
Expand All @@ -391,7 +391,9 @@ def _get_jobs(
# Retrieve the requested number of jobs, using pagination. The server
# might limit the number of jobs per request.
job_responses: List[Dict[str, Any]] = []
current_page_limit = limit if (limit is not None and limit <= 50) else 50
current_page_limit = (
limit if (limit is not None and limit <= PAGE_SIZE) else PAGE_SIZE
)
while True:
if legacy:
job_page = self._default_hgp._api_client.list_jobs(
Expand Down Expand Up @@ -420,7 +422,7 @@ def _get_jobs(
break
current_page_limit = limit - len(job_responses)
else:
current_page_limit = 50
current_page_limit = PAGE_SIZE
skip = len(job_responses)
return job_responses

Expand Down
59 changes: 39 additions & 20 deletions qiskit_ibm_provider/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from qiskit import circuit as circuit_mod
from qiskit import extensions
from qiskit.circuit import library, controlflow, CircuitInstruction
from qiskit.circuit.classical import expr
from qiskit.circuit.classicalregister import ClassicalRegister, Clbit
from qiskit.circuit.gate import Gate
from qiskit.circuit.controlledgate import ControlledGate
Expand Down Expand Up @@ -157,7 +158,14 @@ def _loads_instruction_parameter( # type: ignore[no-untyped-def]
data_bytes.decode(common.ENCODE), circuit, registers
)
else:
param = value.loads_value(type_key, data_bytes, version, vectors)
param = value.loads_value(
type_key,
data_bytes,
version,
vectors,
clbits=circuit.clbits,
cregs=registers["c"],
)

return param

Expand Down Expand Up @@ -195,13 +203,18 @@ def _read_instruction( # type: ignore[no-untyped-def]
qargs = []
cargs = []
params = []
condition_tuple = None
if instruction.has_condition:
# If register name prefixed with null character it's a clbit index for single bit condition.
condition_tuple = (
condition = None
if (version < 5 and instruction.has_condition) or (
version >= 5 and instruction.conditional_key == type_keys.Condition.TWO_TUPLE
):
condition = (
_loads_register_param(condition_register, circuit, registers),
instruction.condition_value,
)
elif version >= 5 and instruction.conditional_key == type_keys.Condition.EXPRESSION:
condition = value.read_value(
file_obj, version, vectors, clbits=circuit.clbits, cregs=registers["c"]
)
if circuit is not None:
qubit_indices = dict(enumerate(circuit.qubits))
clbit_indices = dict(enumerate(circuit.clbits))
Expand Down Expand Up @@ -245,7 +258,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
inst_obj = _parse_custom_operation(
custom_operations, gate_name, params, version, vectors, registers
)
inst_obj.condition = condition_tuple
inst_obj.condition = condition
if instruction.label_size > 0:
inst_obj.label = label
if circuit is None:
Expand All @@ -256,7 +269,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
inst_obj = _parse_custom_operation(
custom_operations, gate_name, params, version, vectors, registers
)
inst_obj.condition = condition_tuple
inst_obj.condition = condition
if instruction.label_size > 0:
inst_obj.label = label
if circuit is None:
Expand All @@ -277,7 +290,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
raise AttributeError("Invalid instruction type: %s" % gate_name)

if gate_name in {"IfElseOp", "WhileLoopOp"}:
gate = gate_class(condition_tuple, *params)
gate = gate_class(condition, *params)
elif version >= 5 and issubclass(gate_class, ControlledGate):
if gate_name in {
"MCPhaseGate",
Expand All @@ -292,7 +305,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
gate = gate_class(*params)
gate.num_ctrl_qubits = instruction.num_ctrl_qubits
gate.ctrl_state = instruction.ctrl_state
gate.condition = condition_tuple
gate.condition = condition
else:
if gate_name in {
"Initialize",
Expand All @@ -309,7 +322,7 @@ def _read_instruction( # type: ignore[no-untyped-def]
elif gate_name in {"BreakLoopOp", "ContinueLoopOp"}:
params = [len(qargs), len(cargs)]
gate = gate_class(*params)
gate.condition = condition_tuple
gate.condition = condition
if instruction.label_size > 0:
gate.label = label
if circuit is None:
Expand Down Expand Up @@ -546,7 +559,7 @@ def _dumps_instruction_parameter(param, index_map): # type: ignore[no-untyped-d
type_key = type_keys.Value.REGISTER
data_bytes = _dumps_register(param, index_map)
else:
type_key, data_bytes = value.dumps_value(param)
type_key, data_bytes = value.dumps_value(param, index_map=index_map)

return type_key, data_bytes

Expand Down Expand Up @@ -580,15 +593,18 @@ def _write_instruction( # type: ignore[no-untyped-def]
custom_operations[gate_class_name] = instruction.operation
custom_operations_list.append(gate_class_name)

has_condition = False
condition_type = type_keys.Condition.NONE
condition_register = b""
condition_value = 0
if getattr(instruction.operation, "condition", None):
has_condition = True
condition_register = _dumps_register(
instruction.operation.condition[0], index_map
)
condition_value = int(instruction.operation.condition[1])
if (op_condition := getattr(instruction.operation, "condition", None)) is not None:
if isinstance(op_condition, expr.Expr):
condition_type = type_keys.Condition.EXPRESSION
else:
condition_type = type_keys.Condition.TWO_TUPLE
condition_register = _dumps_register(
instruction.operation.condition[0], index_map
)
condition_value = int(instruction.operation.condition[1])

gate_class_name = gate_class_name.encode(common.ENCODE)
label = getattr(instruction.operation, "label")
Expand Down Expand Up @@ -616,7 +632,7 @@ def _write_instruction( # type: ignore[no-untyped-def]
len(instruction_params),
instruction.operation.num_qubits,
instruction.operation.num_clbits,
has_condition,
condition_type.value,
len(condition_register),
condition_value,
num_ctrl_qubits,
Expand All @@ -625,7 +641,10 @@ def _write_instruction( # type: ignore[no-untyped-def]
file_obj.write(instruction_raw)
file_obj.write(gate_class_name)
file_obj.write(label_raw)
file_obj.write(condition_register)
if condition_type is type_keys.Condition.EXPRESSION:
value.write_value(file_obj, op_condition, index_map=index_map)
else:
file_obj.write(condition_register)
# Encode instruciton args
for qbit in instruction.qubits:
instruction_arg_raw = struct.pack(
Expand Down
Loading

0 comments on commit 428a11a

Please sign in to comment.