11# quantum_sim/backends/numpy_backend.py
22
33import numpy as np
4- from typing import Dict , List
4+ from typing import Dict , List , Optional , Any
55
66from quantum_sim .backends .backend import QuantumBackend
7+ # Note: Ensure GateOperation is defined/exported in quantum_sim.core.circuit
78from quantum_sim .core .circuit import QuantumCircuit , GateOperation
89from quantum_sim .core .noise import NoiseChannel , ThermalRelaxationChannel
910
1011
1112class NumpyBackend (QuantumBackend ):
1213 """
1314 A quantum simulation backend that uses NumPy for density matrix manipulation
14- and accurately models time-dependent noise (T1/T2) and per-gate noise using Numba JIT acceleration.
15- Acts as a time-aware scheduling engine.
15+ and accurately models time-dependent noise (T1/T2) and per-gate noise.
1616 """
1717
1818 def __init__ (self ,
1919 num_qubits : int ,
20- t1_times : Dict [int , float ] = None ,
21- t2_times : Dict [int , float ] = None ,
20+ t1_times : Optional [ Dict [int , float ] ] = None ,
21+ t2_times : Optional [ Dict [int , float ] ] = None ,
2222 p_ex : float = 0.0 ,
23- per_qubit_noise_channels : Dict [int , List [NoiseChannel ]] = None ):
23+ per_qubit_noise_channels : Optional [ Dict [int , List [NoiseChannel ] ]] = None ):
2424 """
2525 Initializes the backend with hardware-specific noise parameters.
2626 """
@@ -57,12 +57,16 @@ def run_circuit(self, circuit: QuantumCircuit) -> np.ndarray:
5757 current_rho_tensor = self ._create_initial_density_matrix (circuit .num_qubits )
5858 qubit_map = {q_id : q_id for q_id in range (circuit .num_qubits )}
5959
60- for component in circuit ._components :
60+ # Ensure circuit._components is handled by type checking or
61+ # defined in the QuantumCircuit class.
62+ for component in getattr (circuit , "_components" , []):
6163 gate_duration = 0.0
6264 if isinstance (component , GateOperation ):
6365 gate_duration = component .gate .duration
6466 elif isinstance (component , QuantumCircuit ):
65- durations = [op .gate .duration for op in component ._components if isinstance (op , GateOperation )]
67+ # Safely access internal components of sub-circuits
68+ sub_comps = getattr (component , "_components" , [])
69+ durations = [op .gate .duration for op in sub_comps if isinstance (op , GateOperation )]
6670 gate_duration = max (durations ) if durations else 0.0
6771
6872 # --- Apply IDLE Noise BEFORE operation ---
@@ -99,7 +103,8 @@ def run_circuit(self, circuit: QuantumCircuit) -> np.ndarray:
99103 current_rho_tensor , q_id , circuit .num_qubits , dt = dt_final
100104 )
101105
102- return current_rho_tensor .reshape ((2 ** circuit .num_qubits , 2 ** circuit .num_qubits ))
106+ final_dim = 2 ** circuit .num_qubits
107+ return current_rho_tensor .reshape ((final_dim , final_dim ))
103108
104109 def get_probabilities (self , rho_matrix : np .ndarray ) -> np .ndarray :
105110 return np .diag (rho_matrix ).real
@@ -108,8 +113,10 @@ def get_measurements(self, rho_matrix: np.ndarray, num_shots: int) -> Dict[str,
108113 probabilities = self .get_probabilities (rho_matrix )
109114 num_qubits = int (np .log2 (rho_matrix .shape [0 ]))
110115 outcomes = np .random .choice (len (rho_matrix ), size = num_shots , p = probabilities )
111- counts = {}
116+
117+ # Fixed: Explicit type annotation for the dictionary to satisfy mypy
118+ counts : Dict [str , int ] = {}
112119 for outcome in outcomes :
113- bitstring = bin (outcome )[2 :].zfill (num_qubits )
120+ bitstring = bin (int ( outcome ) )[2 :].zfill (num_qubits )
114121 counts [bitstring ] = counts .get (bitstring , 0 ) + 1
115122 return counts
0 commit comments