diff --git a/.github/workflows/selfhosted.yml b/.github/workflows/selfhosted.yml index c968a0fb8..49a765255 100644 --- a/.github/workflows/selfhosted.yml +++ b/.github/workflows/selfhosted.yml @@ -11,14 +11,81 @@ concurrency: cancel-in-progress: true jobs: - build: + prepare: if: contains(join(github.event.pull_request.labels.*.name), 'run-on') - uses: qiboteam/workflows/.github/workflows/selfhosted.yml@main - with: - used-labels: ${{ toJSON(github.event.pull_request.labels.*.name) }} - python-version: "3.10" - artifact-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - poetry-extras: "--with tests --all-extras" + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Read platforms from labels + id: set-matrix + env: + LABELS: ${{ toJSON(github.event.pull_request.labels.*.name) }} + run: | + echo $LABELS + platforms="[" + combined="" + shopt -s lastpipe + jq -c '.[]' <<< "$LABELS" | while read label; do + platform=(${label//-/ }) + if [ ${platform[0]} == "\"run" ] && [ ${platform[1]} == "on" ]; then + platforms+="${combined:+,}\"${platform[2]}" + combined=${platforms} + fi + done + platforms+="]" + echo ${platforms} + echo matrix="${platforms}" >> $GITHUB_OUTPUT - secrets: - repo_token: ${{ secrets.GITHUB_TOKEN }} + tests: + needs: prepare + strategy: + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.matrix) }} + fail-fast: false + runs-on: self-hosted + steps: + - name: Cleanup workspace manually + run: | + rm -rf _work/* + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install and configure poetry + uses: snok/install-poetry@v1 + with: + virtualenvs-create: false + - name: Install qibolab main + run: | + python -m venv testenv + source testenv/bin/activate + poetry install --no-interaction --all-extras --with tests + - name: Execute on ${{ matrix.platform }} + run: | + source testenv/bin/activate + export platform=${{ matrix.platform }} + git clone https://github.com/qiboteam/qibolab_platforms_qrc + queues=`cat qibolab_platforms_qrc/queues.json` + export QIBOLAB_PLATFORMS=./qibolab_platforms_qrc + partition="$(jq -r -n --argjson data "$queues" '$data.'$platform)" + srun -p $partition selfhosted + mkdir coverage + mv coverage.xml coverage/ + mv htmlcov coverage/ + - name: Upload coverage report as artifact + uses: actions/upload-artifact@v3 + with: + name: coverage-from-self-hosted + path: coverage/ + - name: Notify the Pull Request + uses: thollander/actions-comment-pull-request@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + message: | + Run on QPU `${{ matrix.platform }}` completed! :atom: + + > *You can download the coverage report as an artifact, from the workflow summary page:* + > ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/selfhosted b/selfhosted index 658d0dc39..cc3c39837 100755 --- a/selfhosted +++ b/selfhosted @@ -6,8 +6,7 @@ cp -r tests /tmp/ cp pyproject.toml /tmp/ cd /tmp/tests -source /nfs/users/github/actions-runner/_work/qibolab/qibolab/testenv/bin/activate -pytest -m qpu --platforms $PLATFORM +pytest -m qpu --platform $platform pytest_status=$? if [[ $pytest_status -ne 0 ]] then diff --git a/tests/test_backends.py b/tests/test_backends.py index 1ba9b03b9..536c9001d 100644 --- a/tests/test_backends.py +++ b/tests/test_backends.py @@ -69,11 +69,13 @@ def test_measurement_samples(backend): @pytest.mark.qpu @pytest.mark.xfail(raises=AssertionError, reason="Probabilities are not well calibrated") def test_ground_state_probabilities_circuit(backend): + nshots = 5000 nqubits = backend.platform.nqubits circuit = Circuit(nqubits) circuit.add(gates.M(*range(nqubits))) - result = backend.execute_circuit(circuit, nshots=5000) - probs = result.probabilities() + result = backend.execute_circuit(circuit, nshots=nshots) + freqs = result.frequencies(binary=False) + probs = [freqs[i] / nshots for i in range(2**nqubits)] warnings.warn(f"Ground state probabilities: {probs}") target_probs = np.zeros(2**nqubits) target_probs[0] = 1 @@ -83,12 +85,14 @@ def test_ground_state_probabilities_circuit(backend): @pytest.mark.qpu @pytest.mark.xfail(raises=AssertionError, reason="Probabilities are not well calibrated") def test_excited_state_probabilities_circuit(backend): + nshots = 5000 nqubits = backend.platform.nqubits circuit = Circuit(nqubits) circuit.add(gates.X(q) for q in range(nqubits)) circuit.add(gates.M(*range(nqubits))) - result = backend.execute_circuit(circuit, nshots=5000) - probs = result.probabilities() + result = backend.execute_circuit(circuit, nshots=nshots) + freqs = result.frequencies(binary=False) + probs = [freqs[i] / nshots for i in range(2**nqubits)] warnings.warn(f"Excited state probabilities: {probs}") target_probs = np.zeros(2**nqubits) target_probs[-1] = 1 @@ -99,13 +103,15 @@ def test_excited_state_probabilities_circuit(backend): @pytest.mark.xfail(raises=AssertionError, reason="Probabilities are not well calibrated") def test_superposition_for_all_qubits(backend): """Applies an H gate to each qubit of the circuit and measures the probabilities.""" + nshots = 5000 nqubits = backend.platform.nqubits probs = [] for q in range(nqubits): circuit = Circuit(nqubits) circuit.add(gates.H(q=q)) circuit.add(gates.M(q)) - probs.append(backend.execute_circuit(circuit, nshots=5000).probabilities()) + freqs = backend.execute_circuit(circuit, nshots=nshots).frequencies(binary=False) + probs.append([freqs[i] / nshots for i in range(2)]) warnings.warn(f"Probabilities after an Hadamard gate applied to qubit {q}: {probs[-1]}") probs = np.asarray(probs) target_probs = np.repeat(a=0.5, repeats=nqubits) diff --git a/tests/test_platform.py b/tests/test_platform.py index 9c5c396f8..f72c3fd5b 100644 --- a/tests/test_platform.py +++ b/tests/test_platform.py @@ -97,6 +97,8 @@ def test_platform_execute_one_drive_pulse(qpu_platform): def test_platform_execute_one_coupler_pulse(qpu_platform): # One drive pulse platform = qpu_platform + if len(platform.couplers) == 0: + pytest.skip("The platform does not have couplers") coupler = next(iter(platform.couplers)) sequence = PulseSequence() sequence.add(platform.create_coupler_pulse(coupler, start=0, duration=200, amplitude=1))