""" Smoke tests for dashboard_core.py — the compute/panel-builder layer behind app_dashboard.py's Quantum Simulator tab. Mirrors the checks performed manually while building the module: correct shapes/keys, no exceptions, every panel builder returns a usable figure (including the empty-data placeholder paths). """ import json import matplotlib matplotlib.use("Agg") import numpy as np import pandas as pd import pytest import dashboard_core as dc from dashboard_core.qasm_library import _run_on_mps from dashboard_core.simulation_runner import estrai_valore_puro BELL_CIRCUIT = "Bell |Φ+⟩" # no parametric gates -> mock VQE path VQE_CIRCUIT = "VQE ansatz H₂" # has parametric gates -> real VQE path @pytest.fixture(scope="module") def bell_res(): return dc.run_simulation("Libreria Built-in", BELL_CIRCUIT, "", "ideal", 0.0, 128, 42, use_float32=True) @pytest.fixture(scope="module") def noisy_res(): return dc.run_simulation("Libreria Built-in", VQE_CIRCUIT, "", "depolarizing", 0.03, 128, 42, use_float32=True) @pytest.fixture(scope="module") def df_vqe_mock(bell_res): return dc.run_vqe_telemetry( bell_res["sim"], bell_res["parser"], dc.QASM_LIBRARY[BELL_CIRCUIT], BELL_CIRCUIT, bell_res["n_qubits"], True, epochs=6, lr=0.05, beta1=0.9, beta2=0.999, seed=42, ) @pytest.fixture(scope="module") def df_vqe_real(noisy_res): return dc.run_vqe_telemetry( noisy_res["sim"], noisy_res["parser"], dc.QASM_LIBRARY[VQE_CIRCUIT], VQE_CIRCUIT, noisy_res["n_qubits"], True, epochs=4, lr=0.05, beta1=0.9, beta2=0.999, seed=42, ) @pytest.fixture(scope="module") def md_telemetry(): return dc.run_md_telemetry(md_steps=20, md_temp=300) # ── run_simulation ────────────────────────────────────────────────────── def test_run_simulation_ideal_keys(bell_res): expected_keys = { "prob", "prob_ideal", "noise_factor", "fidelity", "n_qubits", "entropy", "idx_max", "stato_dominante", "tempo", "ram", "nome", "porte_count", "shots_data", "sim", "parser", } assert expected_keys <= set(bell_res.keys()) assert bell_res["n_qubits"] == 2 assert not np.isnan(bell_res["prob"]).any() assert np.isclose(bell_res["prob"].sum(), 1.0, atol=1e-4) def test_run_simulation_with_noise(noisy_res): assert not np.isnan(noisy_res["prob"]).any() assert 0.0 <= noisy_res["fidelity"] <= 1.0 + 1e-6 def test_run_simulation_noise_is_reproducible_from_seed(): # Regression guard: `seed` used to only reach np.random.seed() (which # controls shot sampling), never NoiseModel.apply_to_sv's own # randomness -- for a JAX statevector (the normal case here), # apply_to_sv falls back to OS entropy unless `rng=`/`jax_key=` is # passed explicitly, so two runs with the identical seed silently # produced different noisy probability distributions. Found as a # flaky test in test_mitigation_runner.py (same seed, different # fidelity_per_scale on 2 of 3 runs) before being traced here. kwargs = dict(source_mode="Libreria Built-in", circuit_name=BELL_CIRCUIT, qasm_text="", noise_model="depolarizing", noise_p=0.2, shots=64, seed=99, use_float32=True) res_a = dc.run_simulation(**kwargs) res_b = dc.run_simulation(**kwargs) np.testing.assert_allclose(res_a["prob"], res_b["prob"]) assert res_a["fidelity"] == pytest.approx(res_b["fidelity"]) def test_run_simulation_heavy_circuit_15q(): res = dc.run_simulation("Libreria Built-in", "Error Mitigation (Real-Stress)", "", "bitflip", 0.05, 64, 42, use_float32=True) assert res["n_qubits"] == 15 def test_bell_circuit_via_qasm_produces_real_entanglement(bell_res): # Regression guard for a control/target swap bug found by audit: the QASM # translation used to build (gate, target, control) instead of compiler.py's # documented (gate, control, target) contract, so H+CX through the QASM # path silently produced two separable qubits instead of a Bell state. # Exercises the actual production path (parse -> run_circuit_jit_beast_mode), # not the direct apply_cx API the rest of the test suite relies on. # Reuses the module-scoped bell_res fixture rather than a fresh # run_simulation call, so it doesn't flip the global jax_enable_x64 config # away from what other tests in this module expect. prob = bell_res["prob"] assert np.isclose(prob[0], 0.5, atol=1e-8) assert np.isclose(prob[3], 0.5, atol=1e-8) assert prob[1] < 1e-8 assert prob[2] < 1e-8 # ── run_vqe_telemetry ─────────────────────────────────────────────────── def test_vqe_telemetry_mock_path(df_vqe_mock): assert list(df_vqe_mock.columns) == ["VQE_Energy", "Entropy", "Purity", "Gradient", "Noise_Factor", "Theta_Correction"] assert len(df_vqe_mock) == 6 def test_vqe_telemetry_real_path(df_vqe_real): assert list(df_vqe_real.columns) == ["VQE_Energy", "Entropy", "Purity", "Gradient", "Noise_Factor", "Theta_Correction"] assert len(df_vqe_real) == 4 def test_vqe_telemetry_heavy_qubit_guard_is_caller_responsibility(): # dashboard_core itself doesn't refuse heavy circuits (the UI layer # gates this via QM_MM_HEAVY_QUBIT_THRESHOLD) — just confirm the # threshold constant it's built around is exported and sane. assert dc.QM_MM_HEAVY_QUBIT_THRESHOLD == 12 def test_vqe_gradient_matches_finite_difference(): # The real gradient (jax.grad through the engine dashboard_core now # gets from dense_evolution.circuit_to_energy_fn) replaced a fake # formula (0.5*(E-target)*sin(theta)+noise, no actual derivative # anywhere). This is the correctness bar: compare against a # finite-difference gradient computed by re-evaluating the energy # function directly, on the dashboard's own QASM_LIBRARY circuit. import jax import jax.numpy as jnp de = __import__("dense_evolution") parser = de.QASMParser() circ = parser.parse(dc.QASM_LIBRARY[VQE_CIRCUIT]) energy_fn, n_params = de.circuit_to_energy_fn(circ, circ.n_qubits) assert n_params > 0 stato_zero = jnp.zeros(2 ** circ.n_qubits, dtype=jnp.complex128).at[0].set(1.0) rng = np.random.default_rng(7) h_matrix = jnp.diag(jnp.array(np.sort(rng.uniform(-2.5, 2.5, 2 ** circ.n_qubits)))) theta0 = rng.uniform(-np.pi, np.pi, n_params) energy_and_grad = jax.jit(jax.value_and_grad(energy_fn, argnums=0, has_aux=True)) (_, _), grad = energy_and_grad(jnp.asarray(theta0), h_matrix, stato_zero) eps = 1e-6 fd_grad = np.zeros(n_params) for i in range(n_params): tp, tm = theta0.copy(), theta0.copy() tp[i] += eps tm[i] -= eps (ep, _), _ = energy_and_grad(jnp.asarray(tp), h_matrix, stato_zero) (em, _), _ = energy_and_grad(jnp.asarray(tm), h_matrix, stato_zero) fd_grad[i] = float((ep - em) / (2 * eps)) np.testing.assert_allclose(np.asarray(grad), fd_grad, atol=1e-6) def test_vqe_energy_trends_downward_over_epochs(): # The old fake gradient (formula + injected Gaussian noise) had no # principled reason to actually minimize the energy. A real gradient # run through enough Adam steps on a fixed circuit/Hamiltonian should # show real descent — this is what distinguishes "looks like VQE # telemetry" from "is actually optimizing something". sim = __import__("dense_evolution").DenseSVSimulator(n_qubits=2, use_float32=True) parser = __import__("dense_evolution").QASMParser() np.random.seed(123) df = dc.run_vqe_telemetry( sim, parser, dc.QASM_LIBRARY[VQE_CIRCUIT], VQE_CIRCUIT, 2, True, epochs=40, lr=0.1, beta1=0.9, beta2=0.999, seed=123, ) primi = df["VQE_Energy"].iloc[:5].mean() ultimi = df["VQE_Energy"].iloc[-5:].mean() assert ultimi < primi # ── run_md_telemetry ──────────────────────────────────────────────────── def test_md_telemetry_shape(md_telemetry): df_md, corr_matrix = md_telemetry assert len(df_md) == 20 assert "Energia_VQE_Ha" in df_md.columns assert corr_matrix.shape == (len(df_md.columns), len(df_md.columns)) def test_md_telemetry_mock_mode_labeled_honestly(md_telemetry): # No hamiltonian_values/sv/n_qubits given (the md_telemetry fixture) -- # must fall back to the mock generator and say so via df.attrs, not # present synthetic data as real. df_md, _ = md_telemetry assert df_md.attrs["is_real"] is False assert "dimostrativi" in df_md.attrs["note"].lower() def test_md_telemetry_real_mode_shape_and_attrs(bell_res): # H2 (Idrogeno) - R = 0.74 Å: a real 2-qubit (dim=4) diagonal Hamiltonian # matching BELL_CIRCUIT's 2-qubit statevector -- exercises the REAL # branch (run_md_telemetry.py:34-37, _run_md_telemetry_real), never hit # by the existing mock-only md_telemetry fixture. h2_values = [-1.13, -0.45, 0.12, 0.64] sv = bell_res["sim"].get_statevector() df_md, corr_matrix = dc.run_md_telemetry( md_steps=15, md_temp=300, hamiltonian_values=h2_values, sv=sv, n_qubits=2, seed=42) assert len(df_md) == 15 assert df_md.attrs["is_real"] is True assert "reale" in df_md.attrs["note"].lower() expected_columns = {"Energia_VQE_Ha", "Entropia_von_Neumann_Bit", "Purita_Stato", "Gradiente_Operatore", "Fattore_Rumore_Termico"} assert expected_columns.issubset(set(df_md.columns)) assert corr_matrix.shape == (len(df_md.columns), len(df_md.columns)) def test_md_telemetry_real_mode_physically_sane_values(bell_res): h2_values = [-1.13, -0.45, 0.12, 0.64] sv = bell_res["sim"].get_statevector() df_md, _ = dc.run_md_telemetry( md_steps=10, md_temp=300, hamiltonian_values=h2_values, sv=sv, n_qubits=2, seed=7) # Purity of a valid density matrix is always in (0, 1] (Tr(rho^2) <= 1, # with equality only for a pure state; > 0 since rho is never the zero # matrix). assert (df_md["Purita_Stato"] > 0).all() assert (df_md["Purita_Stato"] <= 1.0 + 1e-9).all() # von Neumann entropy is non-negative by construction (-sum(p*log2(p)) # with p in [0,1]). assert (df_md["Entropia_von_Neumann_Bit"] >= -1e-9).all() # Energy is a probability-weighted average of the Hamiltonian's own # eigenvalues, so it can never leave [min(H), max(H)]. assert df_md["Energia_VQE_Ha"].between(min(h2_values) - 1e-6, max(h2_values) + 1e-6).all() # All finite -- no NaN/inf from a degenerate normalization edge case. assert np.isfinite(df_md.to_numpy()).all() def test_md_telemetry_real_mode_zero_temperature_skips_noise(bell_res): # md_temp<=0 -> p_thermal=0 -> the noiseless branch (realizations=[psi], # md_telemetry.py:166-167), a distinct code path from the noisy ENSEMBLE # branch exercised by the other real-mode tests above. h2_values = [-1.13, -0.45, 0.12, 0.64] sv = bell_res["sim"].get_statevector() df_md, _ = dc.run_md_telemetry( md_steps=8, md_temp=0, hamiltonian_values=h2_values, sv=sv, n_qubits=2, seed=1) assert df_md.attrs["is_real"] is True assert (df_md["Fattore_Rumore_Termico"] == 0.0).all() # No thermal noise -> the state stays pure under unitary evolution only # -> purity stays at (numerically) exactly 1 every step. assert np.allclose(df_md["Purita_Stato"], 1.0, atol=1e-9) def test_md_telemetry_real_mode_reproducible_with_seed(bell_res): h2_values = [-1.13, -0.45, 0.12, 0.64] sv = bell_res["sim"].get_statevector() df_a, _ = dc.run_md_telemetry( md_steps=10, md_temp=300, hamiltonian_values=h2_values, sv=sv, n_qubits=2, seed=99) df_b, _ = dc.run_md_telemetry( md_steps=10, md_temp=300, hamiltonian_values=h2_values, sv=sv, n_qubits=2, seed=99) pd.testing.assert_frame_equal(df_a, df_b) def test_md_telemetry_dimension_mismatch_falls_back_to_mock(bell_res): # hamiltonian_values given but wrong dimension for n_qubits=2 (needs 4, # this has 8) -- exercises _check_real_mode_inputs's mismatch branch # (md_telemetry.py:48-53), distinct from the "nothing given at all" # path the mock-only fixture already covers. sv = bell_res["sim"].get_statevector() wrong_dim_hamiltonian = [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5] # dim=8, needs dim=4 df_md, _ = dc.run_md_telemetry( md_steps=5, md_temp=300, hamiltonian_values=wrong_dim_hamiltonian, sv=sv, n_qubits=2, seed=0) assert df_md.attrs["is_real"] is False assert "non" in df_md.attrs["note"].lower() and "compatibile" in df_md.attrs["note"].lower() # ── compute_overview_metrics ──────────────────────────────────────────── def test_compute_overview_metrics_keys(bell_res): metrics = dc.compute_overview_metrics(bell_res, "ideal", 0.0) expected_labels = { "Qubits", "Hilbert Dim", "Gates", "Entropy", "Concurrence", "Purity", "Spectral σ", "Top State", "P(top)", "RAM", "Time", "Noise", "Noise p", } assert {m["label"] for m in metrics} == expected_labels assert all(isinstance(m["value"], str) for m in metrics) # short values must never overflow a narrow st.metric tile — cap at a sane length assert all(len(m["value"]) <= 20 for m in metrics) def test_compute_overview_metrics_short_values_even_for_heavy_circuit(): # 15-qubit dominant-state bitstring used to blow past any reasonable # tile width ("|000000000000000⟩") — must now stay short via the #idx form res = dc.run_simulation("Libreria Built-in", "Error Mitigation (Real-Stress)", "", "ideal", 0.0, 64, 42, use_float32=True) metrics = dc.compute_overview_metrics(res, "ideal", 0.0) top_state = next(m for m in metrics if m["label"] == "Top State") assert len(top_state["value"]) <= 10 assert "⟩" in top_state["help"] # ── panel builders ────────────────────────────────────────────────────── def test_build_panel_overview(bell_res, df_vqe_mock, md_telemetry): df_md, corr_matrix = md_telemetry fig = dc.build_panel_overview(bell_res, df_vqe_mock, corr_matrix, "ideal", 0.0) assert fig is not None def test_build_panel_fisica(bell_res): fig = dc.build_panel_fisica(bell_res, seed=42) assert fig is not None def test_build_panel_mosaico(bell_res): fig = dc.build_panel_mosaico(bell_res) assert fig is not None def test_build_panel_vqe_results_with_data(df_vqe_mock): fig = dc.build_panel_vqe_results(df_vqe_mock) assert fig is not None def test_build_panel_vqe_results_empty(): fig = dc.build_panel_vqe_results(pd.DataFrame()) assert fig is not None def test_build_panel_md_results_with_data(md_telemetry): df_md, corr_matrix = md_telemetry fig = dc.build_panel_md_results(df_md, corr_matrix) assert fig is not None def test_build_panel_md_results_empty(): fig = dc.build_panel_md_results(pd.DataFrame(), pd.DataFrame()) assert fig is not None def test_build_panel_performance_with_history(bell_res): history = [{"nome": bell_res["nome"], "n_qubits": bell_res["n_qubits"], "tempo": bell_res["tempo"], "ram": bell_res["ram"]}] fig = dc.build_panel_performance(bell_res, history) assert fig is not None def test_build_panel_performance_empty_history(bell_res): fig = dc.build_panel_performance(bell_res, []) assert fig is not None def test_build_3d_helix_patch(bell_res): fig = dc.build_3d_helix_patch(bell_res["n_qubits"], bell_res["prob"]) assert fig is not None # ── benchmark + provenance export ─────────────────────────────────────── def test_run_benchmark_scan_small_range(): df = dc.run_benchmark_scan(range(2, 6, 2)) assert list(df.columns) == ["Qubits", "Hilbert_Dim", "Time_s", "RAM_Sim_MB", "Delta_RAM_RSS_MB"] assert len(df) == 2 def test_build_provenance_json_roundtrip(bell_res): history = [{"nome": bell_res["nome"], "n_qubits": bell_res["n_qubits"], "tempo": bell_res["tempo"], "ram": bell_res["ram"], "prob_sample": bell_res["prob"]}] payload = dc.build_provenance_json(history) parsed = json.loads(payload) integrity_hash = parsed["metadata"]["integrity_sha256"] assert len(integrity_hash) == 64 and all(c in "0123456789abcdef" for c in integrity_hash) assert len(parsed["records"]) == 1 assert isinstance(parsed["records"][0]["prob_sample"], list) # ── Hamiltonian library ───────────────────────────────────────────────── def test_infer_qubit_count_from_qasm(): assert dc.infer_qubit_count_from_qasm(dc.QASM_LIBRARY[VQE_CIRCUIT]) == 2 assert dc.infer_qubit_count_from_qasm(dc.QASM_LIBRARY["Error Mitigation (Real-Stress)"]) == 15 assert dc.infer_qubit_count_from_qasm("") is None assert dc.infer_qubit_count_from_qasm("garbage, no qreg here") is None def test_get_compatible_hamiltonians(): compat_2q = dc.get_compatible_hamiltonians(2) assert len(compat_2q) > 0 assert all(len(v) == 4 for v in compat_2q.values()) # the None-valued "Spettro Uniforme Classico" entry must never appear (matches dash.py's own filter) assert all(v is not None for v in compat_2q.values()) assert dc.get_compatible_hamiltonians(0) == {} assert dc.get_compatible_hamiltonians(None) == {} def test_save_custom_hamiltonian_valid_and_duplicate(): library = dict(dc.LIBRERIA_HAMILTONIANE) ok, _ = dc.save_custom_hamiltonian(library, "Test H", "[1.0, -1.0, 0.5, -0.5]") assert ok and "Test H" in library ok2, msg2 = dc.save_custom_hamiltonian(library, "Test H", "[1.0, -1.0, 0.5, -0.5]") assert not ok2 and "esiste già" in msg2 def test_save_custom_hamiltonian_invalid_inputs(): library = dict(dc.LIBRERIA_HAMILTONIANE) ok, _ = dc.save_custom_hamiltonian(library, "", "[1.0]") assert not ok ok, _ = dc.save_custom_hamiltonian(library, "Bad JSON", "not json") assert not ok ok, _ = dc.save_custom_hamiltonian(library, "Not a list", '{"a": 1}') assert not ok def test_build_panel_hamiltonian_with_and_without_data(): fig = dc.build_panel_hamiltonian([-1.13, -0.45, 0.12, 0.64], "H2 test") assert fig is not None fig_empty = dc.build_panel_hamiltonian(None, "none") assert fig_empty is not None def test_vqe_telemetry_with_custom_hamiltonian(noisy_res): compat = dc.get_compatible_hamiltonians(noisy_res["n_qubits"]) values = next(iter(compat.values())) df = dc.run_vqe_telemetry( noisy_res["sim"], noisy_res["parser"], dc.QASM_LIBRARY[VQE_CIRCUIT], VQE_CIRCUIT, noisy_res["n_qubits"], True, epochs=3, lr=0.05, beta1=0.9, beta2=0.999, seed=42, hamiltonian_values=values, ) assert len(df) == 3 # ── on_epoch callback hook (purely additive) ──────────────────────────── def test_on_epoch_callback_mock_path(bell_res): calls = [] df = dc.run_vqe_telemetry( bell_res["sim"], bell_res["parser"], dc.QASM_LIBRARY[BELL_CIRCUIT], BELL_CIRCUIT, bell_res["n_qubits"], True, epochs=5, lr=0.05, beta1=0.9, beta2=0.999, seed=42, on_epoch=lambda e, t, row: calls.append((e, t, row)), ) assert len(calls) == 5 assert calls[0][0] == 0 and calls[0][1] == 5 assert calls[-1][0] == 4 assert set(calls[0][2].keys()) >= {"VQE_Energy", "Entropy", "Purity", "Gradient", "Noise_Factor", "Theta_Correction"} assert len(df) == 5 def test_on_epoch_callback_real_path(noisy_res): calls = [] df = dc.run_vqe_telemetry( noisy_res["sim"], noisy_res["parser"], dc.QASM_LIBRARY[VQE_CIRCUIT], VQE_CIRCUIT, noisy_res["n_qubits"], True, epochs=4, lr=0.05, beta1=0.9, beta2=0.999, seed=42, on_epoch=lambda e, t, row: calls.append((e, t, row)), ) assert len(calls) == 4 assert calls[0][1] == 4 assert len(df) == 4 def test_run_vqe_telemetry_without_on_epoch_still_works(bell_res): # regression guard: on_epoch defaults to None, existing callers unaffected df = dc.run_vqe_telemetry( bell_res["sim"], bell_res["parser"], dc.QASM_LIBRARY[BELL_CIRCUIT], BELL_CIRCUIT, bell_res["n_qubits"], True, epochs=3, lr=0.05, beta1=0.9, beta2=0.999, seed=42, ) assert len(df) == 3 # ── _run_on_mps gate dispatch ──────────────────────────────────────────── # comandi_beast_mode formats: 1q non-param [name, qubit, -1], 1q parametric # [name, qubit, param], 2q non-param [name, control, target], 2q parametric # [name, control, target, param], 3q [name, c1, c2, target] -- matches # run_simulation's own comandi_beast_mode construction (simulation_runner.py:167-212). # Called directly (not via run_simulation) because run_simulation's own # gate-name whitelist there never forwards names _run_on_mps doesn't handle # (e.g. 'ch', 'cswap') -- to reach _run_on_mps's own "unhandled gate" branch, # it has to be exercised directly with a hand-built ops list. def test_run_on_mps_cx_and_parametric_1q_gates(): # cx and the rx/ry/rz/u1/p parametric-1q branch -- not exercised by any # of the gate-specific tests below, which deliberately pick other gates. mps = _run_on_mps( [["ry", 0, 0.7], ["rz", 1, 0.3], ["u1", 0, 0.1], ["p", 1, 0.2], ["cx", 0, 1]], n_qubits=2) sv = np.asarray(mps.contract_to_statevector()) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) def test_run_on_mps_cz_and_swap_gates(): mps = _run_on_mps([["h", 0, -1], ["cz", 0, 1], ["swap", 1, 2]], n_qubits=3) sv = np.asarray(mps.contract_to_statevector()) assert sv.shape == (8,) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) def test_run_on_mps_cy_gate_matches_dense_simulator(): import dense_evolution as de ops = [["h", 0, -1], ["cy", 0, 1]] mps = _run_on_mps(ops, n_qubits=2) mps_sv = np.asarray(mps.contract_to_statevector()) dense = de.DenseSVSimulator(n_qubits=2) dense.run_circuit([("h", 0), ("cy", 0, 1)]) dense_sv = np.asarray(dense.get_statevector()) # global phase can differ -- compare via fidelity, not raw amplitudes fidelity = abs(np.vdot(mps_sv, dense_sv)) ** 2 assert fidelity == pytest.approx(1.0, abs=1e-6) def test_run_on_mps_cp_and_crz_parametric_2q_gates(): mps = _run_on_mps( [["h", 0, -1], ["h", 1, -1], ["cp", 0, 1, 0.5], ["crz", 1, 2, 0.3]], n_qubits=3) sv = np.asarray(mps.contract_to_statevector()) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) def test_run_on_mps_ccx_gate(): mps = _run_on_mps([["h", 0, -1], ["h", 1, -1], ["ccx", 0, 1, 2]], n_qubits=3) sv = np.asarray(mps.contract_to_statevector()) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) def test_run_on_mps_u2_u3_skipped_with_warning(capsys): # u2/u3 can't be represented in the single-param-slot comandi_beast_mode # format -- _run_on_mps must skip them (not crash) and say so. mps = _run_on_mps([["h", 0, -1], ["u2", 0, 0.1], ["u3", 0, 0.2]], n_qubits=1) sv = np.asarray(mps.contract_to_statevector()) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) captured = capsys.readouterr() assert "u2" in captured.out and "u3" in captured.out assert "Warning" in captured.out def test_run_on_mps_unhandled_gate_name_skipped_with_warning(capsys): # A gate name outside _run_on_mps's own dispatch table (e.g. 'ch', # 'cswap' -- both appear in QASM_LIBRARY circuits, but never reach here # via run_simulation's own separate whitelist) must be skipped with a # warning, not raise. mps = _run_on_mps([["h", 0, -1], ["ch", 0, 1]], n_qubits=2) sv = np.asarray(mps.contract_to_statevector()) assert np.isclose(np.linalg.norm(sv), 1.0, atol=1e-6) captured = capsys.readouterr() assert "ch" in captured.out and "Warning" in captured.out # ── estrai_valore_puro ─────────────────────────────────────────────────── # Extracts a "pure" (int/float/str) value out of whatever shape the QASM # parser's parsed circuit ops hand it (a plain int, a callable, an object # exposing .index or .value, a numeric string) -- NOT Streamlit widget # unwrapping, despite that being a plausible-sounding guess; this module # has no Streamlit import at all. Almost none of its branches were # exercised before -- existing tests only ever fed it already-plain ints. def test_estrai_valore_puro_none_returns_zero(): assert estrai_valore_puro(None) == 0 def test_estrai_valore_puro_calls_function_like_callable(): assert estrai_valore_puro(lambda: 5) == 5 def test_estrai_valore_puro_function_like_callable_exception_returns_zero(): def boom(): raise RuntimeError("nope") assert estrai_valore_puro(boom) == 0 def test_estrai_valore_puro_calls_generic_callable_object(): class Foo: def __call__(self): return 42 assert estrai_valore_puro(Foo()) == 42 def test_estrai_valore_puro_generic_callable_exception_falls_through(): class Boom: def __call__(self): raise RuntimeError("nope") # No .index/.value, not a str/int/float -- exception in the generic # callable branch just falls through to the final `return elemento`. b = Boom() assert estrai_valore_puro(b) is b def test_estrai_valore_puro_index_attribute_plain_value(): class QubitRef: def __init__(self, idx): self.index = idx assert estrai_valore_puro(QubitRef(3)) == 3 def test_estrai_valore_puro_index_attribute_callable_value(): class QubitRef: def __init__(self, idx): self.index = lambda: idx assert estrai_valore_puro(QubitRef(7)) == 7 def test_estrai_valore_puro_value_attribute_plain_value(): class ParamRef: def __init__(self, v): self.value = v assert estrai_valore_puro(ParamRef(0.5)) == 0.5 def test_estrai_valore_puro_value_attribute_callable_value(): class ParamRef: def __init__(self, v): self.value = lambda: v assert estrai_valore_puro(ParamRef(0.25)) == 0.25 def test_estrai_valore_puro_string_with_dot_becomes_float(): assert estrai_valore_puro("3.5") == 3.5 def test_estrai_valore_puro_string_digits_become_int(): assert estrai_valore_puro("42") == 42 def test_estrai_valore_puro_non_numeric_string_returned_as_is(): assert estrai_valore_puro("abc") == "abc" assert estrai_valore_puro("a.b") == "a.b" # has '.', still not a valid float def test_estrai_valore_puro_plain_object_returned_unchanged(): class Opaque: pass o = Opaque() assert estrai_valore_puro(o) is o def test_estrai_valore_puro_numpy_scalar_types_pass_through(): assert estrai_valore_puro(np.int64(9)) == 9 assert estrai_valore_puro(np.float32(1.5)) == 1.5 # ── run_simulation: gates/engines not exercised by bell_res/noisy_res ────── def test_run_simulation_ccx_gate_through_full_pipeline(): # Grover_3q_Oracle_111 uses ccx -- BELL_CIRCUIT/VQE_CIRCUIT don't, # so the ccx/toffoli branch of _run_simulation_body's own # comandi_beast_mode builder (simulation_runner.py:197-204) was # never exercised through the real run_simulation entry point # (only via _run_on_mps directly, a different code path). res = dc.run_simulation("Libreria Built-in", "Grover_3q_Oracle_111", "", "ideal", 0.0, 64, 42, use_float32=True) assert res["n_qubits"] == 3 assert np.isclose(np.sum(res["prob"]), 1.0, atol=1e-5) def test_run_simulation_cp_gate_through_full_pipeline(): # QFT 4 qubit uses cp -- exercises the cp/crz branch # (simulation_runner.py:205-212), also never hit via bell_res/noisy_res. res = dc.run_simulation("Libreria Built-in", "QFT 4 qubit", "", "ideal", 0.0, 64, 42, use_float32=True) assert res["n_qubits"] == 4 assert np.isclose(np.sum(res["prob"]), 1.0, atol=1e-5) def test_run_simulation_mps_engine_with_noise(): # bell_res/noisy_res only ever combine engine='dense' with noise, or # engine='mps' with noise_model='ideal' -- never engine='mps' PLUS a # real noise channel, which is its own branch (the usa_mps sub-cases # inside the noisy_model != 'ideal' path, simulation_runner.py:244-245 # and 255) computing sim_ideal and sim separately via _mps_statevector. res = dc.run_simulation( "Libreria Built-in", BELL_CIRCUIT, "", "depolarizing", 0.05, 64, 42, use_float32=True, engine="mps") assert res["n_qubits"] == 2 assert 0.0 <= res["fidelity"] <= 1.0 + 1e-6 assert np.isclose(np.sum(res["prob"]), 1.0, atol=1e-5) def test_run_simulation_custom_qasm_text_mode(): # Every other run_simulation test in this file uses source_mode= # "Libreria Built-in" -- the else branch (qasm_string=qasm_text, # nome_circuito='Custom Workspace', simulation_runner.py:99-100), # the actual "paste your own QASM" path in the dashboard, was untested. custom_qasm = 'OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; creg c[2]; h q[0]; cx q[0],q[1]; measure q -> c;' res = dc.run_simulation("Custom Workspace", "", custom_qasm, "ideal", 0.0, 64, 42, use_float32=True) assert res["nome"] == "Custom Workspace" assert res["n_qubits"] == 2 assert np.isclose(np.sum(res["prob"]), 1.0, atol=1e-5) def test_run_simulation_mps_engine_rejects_over_24_qubits(): # simulation_runner.py:151-160's explicit ValueError -- MPSSimulator's # dashboard integration is capped at 24 qubits (get_probabilities_sampled # for larger circuits isn't wired to the dense-array-expecting panels # yet). A qreg-only QASM string (no gates needed) is enough to report # n_qubits=30 without actually running anything expensive. big_qasm = 'OPENQASM 2.0; include "qelib1.inc"; qreg q[30]; creg c[30];' with pytest.raises(ValueError, match="24 qubit"): dc.run_simulation("Custom Workspace", "", big_qasm, "ideal", 0.0, 64, 42, use_float32=True, engine="mps") def test_run_simulation_chunk_engine(monkeypatch): # de.Chunk is only actually used (usa_chunk=True) when # MemoryChunker(n_qubits).num_chunks > 1, which needs chunk_size_bits # (get_dynamic_chunk's real-RAM-based result, always >= 16) to be # smaller than n_qubits -- never true for the 2-4 qubit circuits used # elsewhere in this file, and not worth a genuinely 17+ qubit circuit # just to reach this branch quickly. Force it instead by patching # get_dynamic_chunk to a tiny value so even a 3-qubit circuit chunks # (de.Chunk's own correctness against DenseSVSimulator is already # covered by test_dense_evolution.py; this only checks # run_simulation's own dispatch actually uses de.Chunk when told to). import dense_evolution.chunk as chunk_module monkeypatch.setattr(chunk_module, "get_dynamic_chunk", lambda dtype_target: 1) res = dc.run_simulation("Libreria Built-in", BELL_CIRCUIT, "", "ideal", 0.0, 64, 42, use_float32=True) from dense_evolution import Chunk assert isinstance(res["sim"], Chunk) assert np.isclose(np.sum(res["prob"]), 1.0, atol=1e-5)