diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5290823..b34ac88 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ on: jobs: lint: - runs-on: ubuntu-22.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -24,5 +24,4 @@ jobs: run: | black --check . - name: Run pylint - run: | - pylint */ + run: pylint */ diff --git a/docs/changelog.rst b/docs/changelog.rst index 6f7ed8f..3f22193 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,10 +1,12 @@ Changelog ~~~~~~~~~ -0.36.0 (unreleased) -------------------- +Unreleased +---------- -* Updated pytket version requirement to 1.30. +* Update pytket version requirement to 1.30. +* Update pyquil version requirement to 4.13. +* Remove upper bounds on dependency versions. 0.35.0 (April 2024) ------------------- diff --git a/pytket/extensions/pyquil/backends/forest.py b/pytket/extensions/pyquil/backends/forest.py index 4c08f87..653a30d 100644 --- a/pytket/extensions/pyquil/backends/forest.py +++ b/pytket/extensions/pyquil/backends/forest.py @@ -227,6 +227,8 @@ def process_circuits( ).apply(circuit) p, bits = tk_to_pyquil(c0, return_used_bits=True) + bit_indices = [c0.bits.index(bit) for bit in bits] + p.wrap_in_numshots_loop(n_shots) ex = self._qc.compiler.native_quil_to_executable(p) qam = self._qc.qam @@ -237,11 +239,14 @@ def process_circuits( if measures == 0: self._cache[handle] = { "handle": pyquil_handle, - "c_bits": sorted(bits), + "bit_indices": sorted(bit_indices), "result": self.empty_result(circuit, n_shots=n_shots), } else: - self._cache[handle] = {"handle": pyquil_handle, "c_bits": sorted(bits)} + self._cache[handle] = { + "handle": pyquil_handle, + "bit_indices": sorted(bit_indices), + } handle_list.append(handle) return handle_list @@ -283,12 +288,13 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul raw_shots = self._qc.qam.get_result(pyquil_handle).readout_data["ro"] if raw_shots is None: raise ValueError("Could not read job results in memory") + # Measurement results are returned even for unmeasured bits, so we + # have to filter the shots table: + raw_shots = raw_shots[:, self._cache[handle]["bit_indices"]] shots = OutcomeArray.from_readouts(raw_shots.tolist()) ppcirc_rep = json.loads(cast(str, handle[1])) ppcirc = Circuit.from_dict(ppcirc_rep) if ppcirc_rep is not None else None - res = BackendResult( - shots=shots, c_bits=self._cache[handle]["c_bits"], ppcirc=ppcirc - ) + res = BackendResult(shots=shots, ppcirc=ppcirc) self._cache[handle].update({"result": res}) return res @@ -319,7 +325,14 @@ def _get_backend_info(cls, qc: QuantumComputer) -> BackendInfo: def available_devices(cls, **kwargs: Any) -> List[BackendInfo]: """ See :py:meth:`pytket.backends.Backend.available_devices`. - Supported kwargs: `qpus` (default true), `qvms` (default false). + + Supported kwargs: + + - `qpus` (bool, default True): whether to include QPUs in the list + - `qvms` (bool, default False): whether to include QVMs in the list + - `timeout` (float, default 10.0) time limit for request, in seconds + - `client_configuration` (optional qcs_sdk.QCSClient, defaut None): + optional client configuration; if None, a default one will be loaded. """ if "qvms" not in kwargs: kwargs["qvms"] = False diff --git a/pytket/extensions/pyquil/pyquil_convert.py b/pytket/extensions/pyquil/pyquil_convert.py index 3f20672..74bbd4a 100644 --- a/pytket/extensions/pyquil/pyquil_convert.py +++ b/pytket/extensions/pyquil/pyquil_convert.py @@ -121,6 +121,12 @@ def param_from_pyquil(p: Union[float, Expression]) -> Expr: def to_sympy(e: Any) -> Union[float, int, Expr, Symbol]: if isinstance(e, (float, int)): return e + elif isinstance(e, complex): + if abs(e.imag) >= 1e-12: + raise NotImplementedError( + "Quil expression could not be converted to a parameter: " + str(e) + ) + return e.real elif isinstance(e, MemoryReference): return Symbol(e.name) elif isinstance(e, Function_): @@ -177,11 +183,11 @@ def pyquil_to_tk(prog: Program) -> Circuit: raise NotImplementedError( "Operation not supported by tket: " + str(i) ) from error - qubits = [qmap[q.index] for q in i.qubits] + qubits = [qmap[cast(Qubit_, q).index] for q in i.qubits] params: list[Union[Expr, float]] = [param_from_pyquil(p) for p in i.params] # type: ignore tkc.add_gate(optype, params, qubits) elif isinstance(i, Measurement): - qubit = qmap[i.qubit.index] + qubit = qmap[cast(Qubit_, i.qubit).index] reg = cregmap[i.classical_reg.name] # type: ignore bit = reg[i.classical_reg.offset] # type: ignore tkc.Measure(qubit, bit) diff --git a/setup.py b/setup.py index 874b451..356f96e 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,8 @@ include_package_data=True, install_requires=[ "pytket >= 1.30.0", - "pyquil ~= 3.5", - "typing-extensions ~= 4.2", + "pyquil >= 4.13.0", + "typing-extensions >= 4.12.2", ], classifiers=[ "Environment :: Console", diff --git a/tests/qvm_backend_test.py b/tests/qvm_backend_test.py index 06a6c59..c6b680b 100644 --- a/tests/qvm_backend_test.py +++ b/tests/qvm_backend_test.py @@ -510,6 +510,7 @@ def test_process_characterisation(qvm: None, quilc: None) -> None: @pytest.mark.skipif( skip_qvm_tests, reason="Can only run Rigetti QVM if docker is installed" ) +@pytest.mark.xfail(reason="https://github.com/CQCL/pytket-pyquil/issues/93") def test_retrieve_available_devices() -> None: backend_infos = ForestBackend.available_devices() assert len(backend_infos) > 0 diff --git a/tests/test-requirements.txt b/tests/test-requirements.txt index 1f98cf6..54abba7 100644 --- a/tests/test-requirements.txt +++ b/tests/test-requirements.txt @@ -1,5 +1,5 @@ pytest -pytest-timeout ~= 2.3.1 +pytest-timeout hypothesis requests_mock docker