Source code for uniqc.simulator.originir_simulator

"""OriginIR quantum program simulator.

This module provides simulators for OriginIR-format quantum programs,
supporting both ideal and noisy simulations with topology validation.

Key exports:
    - OriginIR_Simulator: Ideal simulator for OriginIR programs.
    - OriginIR_NoisySimulator: Noisy simulator for OriginIR programs with error models.
"""

__all__ = ["OriginIR_Simulator", "OriginIR_NoisySimulator"]
import random
from typing import Dict, List, Tuple, TYPE_CHECKING, Union
from uniqc.originir.originir_base_parser import OriginIR_BaseParser
import warnings
from .opcode_simulator import OpcodeSimulator
from .base_simulator import BaseNoisySimulator, BaseSimulator
from .error_model import *

if TYPE_CHECKING:
    from .uniqc_cpp import *

[docs] class OriginIR_Simulator(BaseSimulator): """OriginIR quantum program simulator. Simulator for OriginIR-format quantum programs, backed by C++. Args: backend_type: Backend type ("statevector" or "densitymatrix"). available_qubits: List of available qubit indices (optional). available_topology: List of available qubit pairs (optional). **extra_kwargs: Additional arguments passed to BaseSimulator. """ def __init__(self, backend_type = 'statevector', available_qubits : List[int] = None, available_topology : List[List[int]] = None, **extra_kwargs): super().__init__(backend_type, available_qubits, available_topology, **extra_kwargs) self.parser = OriginIR_BaseParser() self.splitted_lines = None def _process_program_body(self): lines = self.parser.originir self.splitted_lines = lines.splitlines() processed_program_body = list() available_topology = self.available_topology program_body = self.parser.program_body for i, opcode in enumerate(program_body): (operation, qubit, cbit, parameter, dagger_flag, control_qubits_set) = opcode if isinstance(qubit, list) and (available_topology): if len(qubit) > 2: # i+2 because QINIT CREG are always excluded. raise ValueError('Real chip does not support gate of 3-qubit or more. ' 'The dummy server does not support either. ' 'You should consider decomposite it. \n' f'Line {i + 2} ({self.splitted_lines[i + 2]}).') if ([int(qubit[0]), int(qubit[1])] not in available_topology) and \ ([int(qubit[1]), int(qubit[0])] not in available_topology): # i+2 because QINIT CREG are always excluded. raise ValueError('Unsupported topology.\n' f'Line {i + 2} ({self.splitted_lines[i + 2]}).') if qubit is not None: if isinstance(qubit, list): mapped_qubit = [self.qubit_mapping[q] for q in qubit] else: mapped_qubit = self.qubit_mapping[qubit] if operation == 'MEASURE': # In fact, I don't know the real implementation # This is a guessed implementation. self.measure_qubit.append((mapped_qubit, cbit)) else: processed_program_body.append((operation, mapped_qubit, cbit, parameter, dagger_flag, control_qubits_set)) return processed_program_body def _clear(self): super()._clear() self.parser = OriginIR_BaseParser() self.splitted_lines = None
[docs] class OriginIR_NoisySimulator(BaseNoisySimulator): """Noisy OriginIR quantum program simulator. Simulator for OriginIR-format quantum programs with noise model support, backed by C++. Args: backend_type: Backend type ("statevector" or "densitymatrix"). available_qubits: List of available qubit indices (optional). available_topology: List of available qubit pairs (optional). error_loader: ErrorLoader instance for gate error injection (optional). readout_error: Dict mapping qubit index to [p0, p1] readout error rates (optional). """ def __init__(self, backend_type = 'statevector', available_qubits : List[int] = None, available_topology : List[List[int]] = None, error_loader : ErrorLoader = None, readout_error : Dict[int, List[float]]={}): super().__init__(backend_type, available_qubits, available_topology, error_loader, readout_error) self.parser = OriginIR_BaseParser() self.splitted_lines = None def _process_program_body(self): lines = self.parser.originir self.splitted_lines = lines.splitlines() processed_program_body = list() available_topology = self.available_topology program_body = self.parser.program_body for i, opcode in enumerate(program_body): (operation, qubit, cbit, parameter, dagger_flag, control_qubits_set) = opcode if isinstance(qubit, list) and (available_topology): if len(qubit) > 2: # i+2 because QINIT CREG are always excluded. raise ValueError('Real chip does not support gate of 3-qubit or more. ' 'The dummy server does not support either. ' 'You should consider decomposite it. \n' f'Line {i + 2} ({self.splitted_lines[i + 2]}).') if ([int(qubit[0]), int(qubit[1])] not in available_topology) and \ ([int(qubit[1]), int(qubit[0])] not in available_topology): # i+2 because QINIT CREG are always excluded. raise ValueError('Unsupported topology.\n' f'Line {i + 2} ({self.splitted_lines[i + 2]}).') if qubit is not None: if isinstance(qubit, list): mapped_qubit = [self.qubit_mapping[q] for q in qubit] else: mapped_qubit = self.qubit_mapping[qubit] if operation == 'MEASURE': # In fact, I don't know the real implementation # This is a guessed implementation. self.measure_qubit.append((mapped_qubit, cbit)) else: processed_program_body.append((operation, mapped_qubit, cbit, parameter, dagger_flag, control_qubits_set)) return processed_program_body def _clear(self): super()._clear() self.parser = OriginIR_BaseParser() self.splitted_lines = None