"""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