"""""
模块作用：定义量子电路的抽象结构，支持指令追加、执行、采样与转换为Stim电路。
"""""
import copy
from multiprocessing import Pool
import pymatching
import qiskit
import numpy as np
import stim
import stimbposd
from qiskit.circuit import CircuitError
from qiskit.circuit.library import XGate, ZGate
from extendedstim.Physics.MajoranaOperator import MajoranaOperator
from extendedstim.Physics.PauliOperator import PauliOperator
from extendedstim.Platform.Frame import Frame
from extendedstim.Platform.Platform import Platform
from extendedstim.tools.TypingTools import isinteger, islist


#%%  KEY：===尝试导入Tesseract类===
try:
    from tesseract_decoder import tesseract
except ImportError:
    print('当前环境不支持tesseract_decoder')
    class tesseract:
        def __init__(self):
            pass

        @staticmethod
        def TesseractConfig(dem, det_beam=50):
            return Config(dem, det_beam)

    class Config:
        def __init__(self, dem: stim.DetectorErrorModel, det_beam):
            self.dem=dem
            self.det_beam=det_beam
        def compile_decoder(self):
            return stimbposd.bp_osd.BPOSD(model=self.dem, bp_method='min_sum', max_bp_iters=self.det_beam)


class Circuit:

    #%%  USER：===构造方法===
    def __init__(self):
        self.majorana_number=0  # fermionic sites的数目
        self.pauli_number=0  # qubits的数目
        self.sequence=[]  # 量子线路的操作序列
        self.noise=[]  # 量子线路中噪声的索引
        self._sequence:list[dict]=[]  # 量子线路的操作序列（真实计算使用）
        self._measurements=[]  # 量子线路中测量的索引
        self._detectors=[]  # 量子线路中测量的结果探测器对self.measurements的索引
        self._observables=[]  # 量子线路中可观测量的对self.measurements的索引
        self._dem=None  # 量子线路错误模型
        self._dem_str_list=[]  # 量子线路错误模型的字符串表示
        self._reference_circuit=None
        self._reference_measurement_sample=None  # 参考样本的测量值
        self._reference_detector_sample=None  # 参考样本的检测值
        self._reference_observable_sample=None  # 参考样本的可观测量值

    #%%  USER：===重载运算符===
    ##  USER：---获取序列中的元素---
    def __getitem__(self, item: int) -> dict:
        return self.sequence[item]

    #%%  USER：===属性方法===
    ##  KEY：---生成无噪声的线路---
    @property
    def reference_circuit(self):
        if self._reference_circuit is not None:
            return self._reference_circuit
        else:
            sequence=copy.deepcopy(self._sequence)  # 深拷贝操作序列
            for i in range(len(self.noise)):
                gate=sequence[self.noise[i]]
                assert isinstance(gate, dict)
                gate['p']=0
            ideal_circuit=copy.deepcopy(self)
            ideal_circuit._sequence=sequence
            return ideal_circuit

    ##  USER：获取参考样本
    @property
    def reference_sample(self):
        if self._reference_measurement_sample is None:
            reference_measurement_sample, reference_detector_sample, reference_observable_sample=self.reference_circuit.execute()
            self._reference_measurement_sample=reference_measurement_sample
            self._reference_detector_sample=reference_detector_sample
            self._reference_observable_sample=reference_observable_sample
            return reference_measurement_sample, reference_detector_sample, reference_observable_sample
        else:
            return self._reference_measurement_sample, self._reference_detector_sample, self._reference_observable_sample

    ##  USER：--生成检测错误模型--
    def detector_error_model(self) -> stim.DetectorErrorModel:
        if self._dem is not None:
            return self._dem
        measurement_sample_origin, detector_sample_origin, observable_sample_origin=self.reference_sample

        ##  执行检验线路的稳定性
        for time in range(5):
            measurement_sample, detector_sample, observable_sample=self.noiseless_sample()
            assert np.all(detector_sample==detector_sample_origin), f'原始线路的detector不是稳定的'
            assert np.all(observable_sample==observable_sample_origin), f'原始线路的observable不是稳定的'

        errors=[]
        dem_str=''
        sample_number=len(self.noise)//20
        noise_group=[self.noise[temp*sample_number:(temp+1)*sample_number] for temp in range(20)]
        if len(self.noise[20*sample_number:])>0:
            noise_group.append(self.noise[20*sample_number:])

        with Pool(processes=len(noise_group)) as pool:
            results=[pool.apply_async(self.noise_sample_batch,args=(noise_group[i],)) for i in range(len(noise_group))]
            final_results=[result.get() for result in results]
        flag=0
        for i in range(len(final_results)):
            for j in range(len(final_results[i])):
                ##  计算错误位置
                measurement_sample, detector_sample, observable_sample=final_results[i][j]
                detector_sample_diff=[detector_sample_origin[j]^detector_sample[j] for j in range(len(detector_sample))]
                observable_sample_diff=[observable_sample_origin[j]^observable_sample[j] for j in range(len(observable_sample))]
                errors.append(len(errors))
                detectors_trigger=np.where(np.array(detector_sample_diff)==True)[0]
                observables_trigger=np.where(np.array(observable_sample_diff)==True)[0]

                ##  合成错误语句
                if len(detectors_trigger)>0 or len(observables_trigger)>0:
                    temp_error=f'error({self._sequence[self.noise[flag]]['p']}) '
                    temp_trigger=''
                    for index in detectors_trigger:
                        temp_trigger=temp_trigger+f' D{index}'
                    for index in observables_trigger:
                        temp_trigger=temp_trigger+f' L{index}'
                    temp=temp_error+temp_trigger
                    if self._sequence[self.noise[flag]]['name']=='M_ERROR':
                        self._dem_str_list.append([temp_error, temp_trigger, 'M_ERROR'])
                    else:
                        self._dem_str_list.append([temp_error, temp_trigger, 'G_ERROR'])
                    dem_str+=('\n'+temp)
                    flag+=1

        ##  返回错误模型
        dem=stim.DetectorErrorModel(dem_str)
        self._dem=dem
        return dem

    #%%  USER：===对象方法===
    ##  USER：---添加量子线路组分操作---
    def append(self, params):
        """""
        {
        'name': str，线路操作的名称
        'target': list or int，操作作用的对象
        'p': float，操作对应的概率，不一定有
        'index': int，测量对应平台上的stabilizers的序号
        'majorana_state': list of MajoranaOperator，强制初始化的majorana state
        'pauli_state': list of PauliOperator，强制初始化的pauli state
        'pauli_number': int，强制初始化的qubits数目
        'majorana_number': int，强制初始化的fermionic sites数目
        }
        支持的线路操作名称：
        'X', 'Y', 'Z', 'H', 'S'：{'name':str,'target':int or list}, single qubit上的qugate
        'X_ERROR', 'Y_ERROR', 'Z_ERROR', 'DEPOLARIZE': {'name':str,'target':int or list}, single qubit上的噪声
        'U', 'V', 'N', 'P'：{'name':str,'target':int or list}, single fermionic site上的fgate
        'U_ERROR', 'V_ERROR', 'N_ERROR', 'FDEPOLARIZE1': {'name':str,'target':int or list}, single fermionic site上的噪声
        'CX', 'CNX', 'CNN', 'BRAID': {'name':str,'target':list}, two qubit or fermionic sites上的gates
        'R','FR': {'name':str,'target':int or list}, single qubit or single fermionic sites上的重置到空态或0态
        'MZ', 'MN': {'name':str,'target':int or list}, single qubit or single fermionic sites上的measurement
        'MPP': {'name':str,'target':list or Operator}, Pauli string operators or Majorana string operators的measurement
        'FORCE'：{'name':str,'pauli_number':int,'majorana_number':int,'pauli_state':list of PauliOperator,'majorana_state':list of MajoranaOperator}, 强制初始化
        'DETECTOR': {'name':str,'target':list of int}，探测器
        'OBSERVABLE_INCLUDE': {'name':str,'target':list of int}，可观测量
        """""

        ##  ---数据预处理---
        assert isinstance(params, dict)
        assert 'name' in params
        name=params["name"]  # 线路操作的名称

        ##  ---添加量子线路操作---
        ##  添加single gate
        if name in ['X', 'Y', 'Z', 'H', 'S', 'X_ERROR', 'Y_ERROR', 'Z_ERROR', 'U', 'V', 'N', 'P', 'U_ERROR', 'V_ERROR', 'N_ERROR']:

            ##  作用在整数index对应目标上
            if isinteger(params['target']):
                self._sequence.append(params.copy())
                self.sequence.append(self._sequence[-1])

            ##  作用在一系列目标上
            elif islist(params['target']):
                for temp in params['target']:
                    params_temp={'name': name, 'target': temp}
                    self.append(params_temp)

        ##  添加two gate
        elif name in ['CX', 'CNX', 'CNN']:

            ##  添加单个two gate
            if islist(params['target']) and len(params['target'])==2 and isinteger(params['target'][0]) and isinteger(params['target'][1]):
                self._sequence.append(params.copy())
                self.sequence.append(self._sequence[-1])

            ##  用单个列表添加多个two gates
            elif islist(params['target']) and isinteger(params['target'][0]):
                for i in range(len(params['target'])//2):
                    params_temp={'name': name, 'target': [params['target'][2*i], params['target'][2*i+1]]}
                    self.append(params_temp)

            ##  用多个列表添加多个two gates
            elif islist(params['target']):
                for temp in params['target']:
                    self.append({'name': name, 'target': temp})

            ##  其他情况抛出异常
            else:
                raise ValueError("CX, CNX, CNN gate must be applied to two")

        ##  添加braid gate
        elif name=='BRAID' or name=='braid':

            if islist(params['target']) and len(params['target'])==2 and isinteger(params['target'][0]) and isinteger(params['target'][1]):
                x=params['target'][0]  # 控制位
                y=params['target'][1]  # 目标位

                ##  控制位等于目标位时，根据verse参数判断是否添加N门
                if x==y and 'verse' not in params:
                    self._sequence.append({'name': 'P', 'target': x})
                    self.sequence.append(self._sequence[-1])
                elif x==y and 'verse' in params and params['verse']==True:
                    self._sequence.append({'name': 'P', 'target': y})
                    self.sequence.append(self._sequence[-1])
                    self._sequence.append({'name': 'N', 'target': y})
                    self.sequence.append(self._sequence[-1])

                ##  一般情况下braid gate作用在两个不同的fermionic sites上
                elif x!=y:
                    self._sequence.append({'name': 'BRAID', 'target': [x, y]})
                    self.sequence.append(self._sequence[-1])

            ##  用单个列表添加多个two gates
            elif islist(params['target']) and isinteger(params['target'][0]):
                for i in range(len(params['target'])//2):
                    params_temp={'name': name, 'target': [params['target'][2*i], params['target'][2*i+1]]}
                    self.append(params_temp)

            ##  用多个列表添加多个two gates
            elif islist(params['target']):
                for temp in params['target']:
                    self.append({'name': name, 'target': temp})

            ##  其他情况抛出异常
            else:
                raise ValueError("CX, CNX, CNN gate must be applied to two")

        ##  添加qubit上的去极化噪声
        elif name=='DEPOLARIZE1':
            assert 'p' in params

            ##  作用在整数index对应目标上
            if isinteger(params['target']):
                self.sequence.append({'name': 'DEPOLARIZE1', 'target': params['target'], 'p': params['p']})
                fix=(1-np.sqrt(1-4*params["p"]/3))/2
                self._sequence.append({'name': 'X_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)
                self._sequence.append({'name': 'Y_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)
                self._sequence.append({'name': 'Z_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)

            ##  作用在一系列目标上
            elif islist(params['target']):
                for temp in params['target']:
                    self.append({'name': params['name'], 'target': temp, 'p': params["p"]})

        ##  添加fermionic site上的去极化噪声
        elif name=='FDEPOLARIZE1':
            assert 'p' in params

            ##  作用在整数index对应目标上
            if isinteger(params['target']):
                self.sequence.append({'name': 'FDEPOLARIZE1', 'target': params['target'], 'p': params['p']})
                fix=(1-np.sqrt(1-4*params["p"]/3))/2
                self._sequence.append({'name': 'U_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)
                self._sequence.append({'name': 'V_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)
                self._sequence.append({'name': 'N_ERROR', 'target': params["target"], 'p': fix})
                self.noise.append(len(self._sequence)-1)

            ##  作用在一系列目标上
            elif islist(params['target']):
                for temp in params['target']:
                    self.append({'name': params['name'], 'target': temp, 'p': params["p"]})

        ##  添加single qubit and single fermionic site上的测量
        elif name=='MZ' or name=='MN':
            target=params['target']

            ##  作用在一系列目标上
            if islist(target):
                for temp in target:
                    dict_temp={'name': name, 'target': temp}
                    self.append(dict_temp)

            ##  作用在整数index对应目标上
            elif isinteger(target):
                if name=='MZ':
                    dict_temp={'name': 'MPP', 'target': PauliOperator([], [target], 1)}
                else:
                    dict_temp={'name': 'MPP', 'target': MajoranaOperator([target], [target], 1j)}
                self.append(dict_temp)

        ##  添加qubit重置
        elif name=='R':
            assert 'target' in params
            target=params['target']

            ##  作用在整数index对应目标上
            if isinteger(target):
                self._sequence.append({'name': name, 'target': target})
                self.sequence.append(self._sequence[-1])
                if target==self.pauli_number:
                    self.pauli_number+=1
                elif target>self.pauli_number or target<0:
                    raise ValueError("R gate target must be consecutive")
                else:
                    pass
            elif islist(target):
                for temp in target:
                    self.append({'name': name, 'target': temp})
            else:
                raise ValueError

        ##  添加fermionic site重置
        elif name=='FR':
            assert 'target' in params
            target=params['target']
            if isinteger(target):
                self._sequence.append({'name': name, 'target': target})
                self.sequence.append(self._sequence[-1])
                if target==self.majorana_number:
                    self.majorana_number=target+1
                elif target>self.majorana_number or target<0:
                    raise ValueError("FR gate target must be consecutive")
                else:
                    pass
            elif islist(target):
                for temp in target:
                    self.append({'name': name, 'target': temp})
            else:
                raise ValueError

        ##  强制初始化
        elif name=='FORCE':
            assert 'majorana_state' in params
            assert 'pauli_state' in params
            assert 'pauli_number' in params
            assert 'majorana_number' in params
            stabilizers_majorana: list[MajoranaOperator]=params["majorana_state"]
            stabilizers_pauli: list[PauliOperator]=params["pauli_state"]
            self.pauli_number=params["pauli_number"]
            self.majorana_number=params["majorana_number"]
            for i in range(len(stabilizers_majorana)):
                assert len(stabilizers_majorana[i].occupy_x)==0 or np.max(stabilizers_majorana[i].occupy_x)<self.majorana_number
                assert len(stabilizers_majorana[i].occupy_z)==0 or np.max(stabilizers_majorana[i].occupy_z)<self.majorana_number
            for i in range(len(stabilizers_pauli)):
                assert len(stabilizers_pauli[i].occupy_x)==0 or np.max(stabilizers_pauli[i].occupy_x)<self.pauli_number
                assert len(stabilizers_pauli[i].occupy_z)==0 or np.max(stabilizers_pauli[i].occupy_z)<self.pauli_number

            self._sequence.append({'name': 'FORCE', 'majorana_state': stabilizers_majorana, 'pauli_state': stabilizers_pauli})
            self.sequence.append(self._sequence[-1])

        ##  添加string算符的测量
        elif name=='MPP':
            assert 'target' in params
            if islist(params['target']):
                for i, temp in enumerate(params['target']):
                    dict_temp={'name': 'MPP', 'target': temp}
                    if 'index' in params:
                        dict_temp['index']=params['index'][i]
                    self.append(dict_temp)
            elif isinstance(params['target'], (PauliOperator, MajoranaOperator)):
                dict_temp={'name': 'MPP', 'target': params['target']}
                if 'index' in params:
                    dict_temp['index']=params['index']
                self._sequence.append(dict_temp)
                self._measurements.append(len(self._sequence)-1)
                if 'p' in params:
                    self._sequence.append({'name': 'M_ERROR', 'p': params["p"]})
                    self.noise.append(len(self._sequence)-1)
                    self.sequence.append({'name': 'MPP', 'target': params['target'], 'p': params["p"]})
                else:
                    self.sequence.append(self._sequence[-1])
            else:
                raise ValueError

        ##  添加监视器
        elif name=='DETECTOR':
            assert 'target' in params
            target=params['target']
            if all(target[i]<0 for i in range(len(target))):
                together=[len(self._measurements)+temp for temp in target]  # 在测量中找到对应索引
                self._detectors.append(together)
                self.sequence.append({'name': 'DETECTOR', 'target': target})
            elif all(target[i]>=0 for i in range(len(target))):
                self._detectors.append([temp for temp in target])
                self.sequence.append({'name': 'DETECTOR', 'target': [-len(self._measurements)+temp for temp in target]})
            else:
                raise ValueError("DETECTOR gate target must be consecutive")

        ##  添加可观测量
        elif name=='OBSERVABLE_INCLUDE':
            assert 'target' in params
            target=params['target']
            if all(target[i]<0 for i in range(len(target))):
                together=[len(self._measurements)+temp for temp in target]  # 在测量中找到索引
                self._observables.append(together)
                self.sequence.append({'name': 'OBSERVABLE_INCLUDE', 'target': target})
            elif all(target[i]>=0 for i in range(len(target))):
                together=[temp for temp in target]  # 在测量中找到索引
                self._observables.append(together)
                self.sequence.append({'name': 'OBSERVABLE_INCLUDE', 'target': [-len(self._measurements)+temp for temp in target]})
            else:
                raise ValueError("OBSERVABLE_INCLUDE gate target must be consecutive")
        else:
            raise NotImplementedError

    ##  USER：---执行线路并返回测量结果---
    def execute(self):

        ##  ---生成量子平台---
        platform=Platform()  # 生成量子平台
        platform.initialize(self.majorana_number, self.pauli_number)  # 初始化一定数目的qubits和fermionic sites
        measurement_sample=np.empty(len(self._measurements), dtype=int)  # 生成测量值的样本数组
        flag_measurement=0

        ##  遍历整个操作序列
        for i, gate in enumerate(self._sequence):
            name=gate['name']

            ##  执行单门
            if name in ['X', 'Y', 'Z', 'H', 'S', 'U', 'V', 'N']:
                target: int=gate['target']
                if name=='X':
                    platform.x(target)
                elif name=='Y':
                    platform.y(target)
                elif name=='Z':
                    platform.z(target)
                elif name=='H':
                    platform.h(target)
                elif name=='S':
                    platform.s(target)
                elif name=='U':
                    platform.u(target)
                elif name=='V':
                    platform.v(target)
                elif name=='N':
                    platform.n(target)

            ##  执行双门
            elif name in ['CX', 'CNX', 'BRAID', 'CNN']:
                target: list=gate['target']
                if name=='CX':
                    platform.cx(target[0], target[1])
                elif name=='CNX':
                    platform.cnx(target[0], target[1])
                elif name=='BRAID':
                    platform.braid(target[0], target[1])
                elif name=='CNN':
                    platform.cnn(target[0], target[1])

            ##  执行重置
            elif name=='R':
                target: int=gate['target']
                platform.reset(target)

            elif name=='FR':
                target: int=gate['target']
                platform.fermionic_reset(target)

            ##  执行误差门
            elif name in ['X_ERROR', 'Y_ERROR', 'Z_ERROR', 'U_ERROR', 'V_ERROR', 'N_ERROR']:
                target: int=gate['target']
                p: float=gate['p']
                if name=='X_ERROR':
                    platform.x_error(target, p)
                elif name=='Y_ERROR':
                    platform.y_error(target, p)
                elif name=='Z_ERROR':
                    platform.z_error(target, p)
                elif name=='U_ERROR':
                    platform.u_error(target, p)
                elif name=='V_ERROR':
                    platform.v_error(target, p)
                elif name=='N_ERROR':
                    platform.n_error(target, p)

            elif name=='M_ERROR':
                p: float=gate['p']
                if np.random.rand()<p:
                    measurement_sample[flag_measurement-1]=-measurement_sample[flag_measurement-1]

            ##  执行测量
            elif name=='MPP':
                target=gate['target']
                measurement_sample[flag_measurement]=platform.measure(target)
                flag_measurement+=1

            ##  执行强制初始化
            elif name=='FORCE':
                assert gate['majorana_state'] is not None
                assert gate['pauli_state'] is not None
                platform.force(gate['majorana_state'], gate['pauli_state'])

            ##  其他类型抛出错误
            else:
                raise ValueError(f"Gate {name} is illegal")

        ##  ---返回可观测的结果---
        detector_sample=diff(measurement_sample, self._detectors)
        observable_sample=diff(measurement_sample, self._observables)
        return measurement_sample, detector_sample, observable_sample

    ##  USER：--修改error model的错误几率--
    def noise_amplitude_fix(self, p_noise, p_measure):
        dem_str=''
        for i, temp in enumerate(self._dem_str_list):
            if temp[2]=='G_ERROR':
                temp[0]=f'error({p_noise/3}) '
            elif temp[2]=='M_ERROR':
                temp[0]=f'error({p_measure}) '
            dem_str+=('\n'+temp[0]+temp[1])
            self._dem=stim.DetectorErrorModel(dem_str)

    ##  USER：--生成解码函数--
    def decoder(self, method):
        dem=self.detector_error_model()
        if method=='bposd':
            decoder=stimbposd.bp_osd.BPOSD(model=dem, bp_method='min_sum', max_bp_iters=100)
        elif method=='tesseract':
            config=tesseract.TesseractConfig(dem=dem, det_beam=50)
            decoder=config.compile_decoder()
        elif method=='matching':
            decoder=pymatching.Matching()
            decoder.from_detector_error_model(dem)
        else:
            raise NotImplementedError
        return decoder

    ##  USER：---获取采样---
    def sample(self):
        frame=Frame()
        frame.initialize(self.majorana_number, self.pauli_number)
        reference_measurement_sample, reference_detector_sample, reference_observable_sample=self.reference_sample
        measurement_sample=np.empty(len(self._measurements), dtype=int)  # 生成测量值的样本数组
        flag_measurement=0
        ##  遍历整个操作序列
        for i, gate in enumerate(self._sequence):
            name=gate['name']

            ##  执行单门
            if name in ['X', 'Y', 'Z', 'H', 'S', 'U', 'V', 'N']:
                pass

            ##  执行双门
            elif name in ['CX', 'CNX', 'BRAID', 'CNN']:
                target: list=gate['target']
                if name=='CX':
                    frame.cx(target[0], target[1])
                elif name=='CNX':
                    frame.cnx(target[0], target[1])
                elif name=='BRAID':
                    frame.braid(target[0], target[1])
                elif name=='CNN':
                    frame.cnn(target[0], target[1])

            ##  执行重置
            elif name=='R':
                target: int=gate['target']
                frame.reset(target)

            elif name=='FR':
                target: int=gate['target']
                frame.fermionic_reset(target)

            ##  执行误差门
            elif name in ['X_ERROR', 'Y_ERROR', 'Z_ERROR', 'U_ERROR', 'V_ERROR', 'N_ERROR']:
                target: int=gate['target']
                p: float=gate['p']
                if name=='X_ERROR':
                    frame.x_error(target, p)
                elif name=='Y_ERROR':
                    frame.y_error(target, p)
                elif name=='Z_ERROR':
                    frame.z_error(target, p)
                elif name=='U_ERROR':
                    frame.u_error(target, p)
                elif name=='V_ERROR':
                    frame.v_error(target, p)
                elif name=='N_ERROR':
                    frame.n_error(target, p)

            elif name=='M_ERROR':
                p: float=gate['p']
                if np.random.rand()<p:
                    measurement_sample[flag_measurement-1]=-measurement_sample[flag_measurement-1]

            ##  执行测量
            elif name=='MPP':
                target=gate['target']
                measurement_sample[flag_measurement]=frame.measure(target, reference_measurement_sample[flag_measurement])
                flag_measurement+=1

            ##  执行强制初始化
            elif name=='FORCE':
                assert gate['majorana_state'] is not None
                assert gate['pauli_state'] is not None
                frame.force(gate['majorana_state'], gate['pauli_state'])

            ##  其他类型抛出错误
            else:
                raise ValueError(f"Gate {name} is illegal")

        ##  ---返回可观测的结果---
        detector_sample=diff(measurement_sample, self._detectors)
        observable_sample=diff(measurement_sample, self._observables)
        return measurement_sample, detector_sample, observable_sample

    def noise_sample_batch(self, noise_index_list):
        results=[self.noise_sample(noise_index) for noise_index in noise_index_list]
        return results

    ##  USER：---获取采样---
    def noiseless_sample(self):
        frame=Frame()
        frame.initialize(self.majorana_number, self.pauli_number)
        reference_measurement_sample, reference_detector_sample, reference_observable_sample=self.reference_sample
        measurement_sample=np.empty(len(self._measurements), dtype=int)  # 生成测量值的样本数组
        flag_measurement=0
        ##  遍历整个操作序列
        for i, gate in enumerate(self._sequence):
            name=gate['name']

            ##  执行单门
            if name in ['X', 'Y', 'Z', 'U', 'V', 'N']:
                pass
            elif name=='H':
                target: int=gate['target']
                frame.h(target)
            elif name=='S':
                target: int=gate['target']
                frame.s(target)
            elif name=='P':
                target: int=gate['target']
                frame.p(target)
            ##  执行双门
            elif name in ['CX', 'CNX', 'BRAID', 'CNN']:
                target: list=gate['target']
                if name=='CX':
                    frame.cx(target[0], target[1])
                elif name=='CNX':
                    frame.cnx(target[0], target[1])
                elif name=='BRAID':
                    frame.braid(target[0], target[1])
                elif name=='CNN':
                    frame.cnn(target[0], target[1])

            ##  执行重置
            elif name=='R':
                target: int=gate['target']
                frame.reset(target)

            elif name=='FR':
                target: int=gate['target']
                frame.fermionic_reset(target)

            ##  执行误差门
            elif name in ['X_ERROR', 'Y_ERROR', 'Z_ERROR', 'U_ERROR', 'V_ERROR', 'N_ERROR','M_ERROR']:
                pass

            ##  执行测量
            elif name=='MPP':
                target=gate['target']
                measurement_sample[flag_measurement]=frame.measure(target, reference_measurement_sample[flag_measurement])
                flag_measurement+=1

            ##  执行强制初始化
            elif name=='FORCE':
                assert gate['majorana_state'] is not None
                assert gate['pauli_state'] is not None
                frame.force(gate['majorana_state'], gate['pauli_state'])

            ##  其他类型抛出错误
            else:
                raise ValueError(f"Gate {name} is illegal")

        ##  ---返回可观测的结果---
        detector_sample=diff(measurement_sample, self._detectors)
        observable_sample=diff(measurement_sample, self._observables)
        return measurement_sample, detector_sample, observable_sample

    ##  USER：---获取采样---
    def noise_sample(self,noise_index):
        frame=Frame()
        frame.initialize(self.majorana_number, self.pauli_number)
        reference_measurement_sample, reference_detector_sample, reference_observable_sample=self.reference_sample
        measurement_sample=np.empty(len(self._measurements), dtype=int)  # 生成测量值的样本数组
        flag_measurement=0
        ##  遍历整个操作序列
        for i, gate in enumerate(self._sequence):
            name=gate['name']

            ##  执行单门
            if name in ['X', 'Y', 'Z','U', 'V', 'N']:
                pass
            elif name=='H':
                target: int=gate['target']
                frame.h(target)
            elif name=='S':
                target: int=gate['target']
                frame.s(target)
            elif name=='P':
                target: int=gate['target']
                frame.p(target)

            ##  执行双门
            elif name in ['CX', 'CNX', 'BRAID', 'CNN']:
                target: list=gate['target']
                if name=='CX':
                    frame.cx(target[0], target[1])
                elif name=='CNX':
                    frame.cnx(target[0], target[1])
                elif name=='BRAID':
                    frame.braid(target[0], target[1])
                elif name=='CNN':
                    frame.cnn(target[0], target[1])

            ##  执行重置
            elif name=='R':
                target: int=gate['target']
                frame.reset(target)

            elif name=='FR':
                target: int=gate['target']
                frame.fermionic_reset(target)

            ##  执行误差门
            elif name in ['X_ERROR', 'Y_ERROR', 'Z_ERROR', 'U_ERROR', 'V_ERROR', 'N_ERROR']:
                if noise_index==i:
                    target: int=gate['target']
                    if name=='X_ERROR':
                        frame.x_error(target, 1)
                    elif name=='Y_ERROR':
                        frame.y_error(target, 1)
                    elif name=='Z_ERROR':
                        frame.z_error(target, 1)
                    elif name=='U_ERROR':
                        frame.u_error(target, 1)
                    elif name=='V_ERROR':
                        frame.v_error(target, 1)
                    elif name=='N_ERROR':
                        frame.n_error(target, 1)

            elif name=='M_ERROR':
                if noise_index==i:
                    measurement_sample[flag_measurement-1]=-measurement_sample[flag_measurement-1]

            ##  执行测量
            elif name=='MPP':
                target=gate['target']
                measurement_sample[flag_measurement]=frame.measure(target, reference_measurement_sample[flag_measurement])
                flag_measurement+=1

            ##  执行强制初始化
            elif name=='FORCE':
                assert gate['majorana_state'] is not None
                assert gate['pauli_state'] is not None
                frame.force(gate['majorana_state'], gate['pauli_state'])

            ##  其他类型抛出错误
            else:
                raise ValueError(f"Gate {name} is illegal")

        ##  ---返回可观测的结果---
        detector_sample=diff(measurement_sample, self._detectors)
        observable_sample=diff(measurement_sample, self._observables)
        return measurement_sample, detector_sample, observable_sample

    ##  USER：--执行线路并返回错误率--
    def experiment(self, sample_number: int, method: str):

        ##  生成线路执行样本和每个样本的预测样本
        dem=self.detector_error_model()  # 错误模型
        sampler=dem.compile_sampler()  # 采样器
        decoder=self.decoder(method)  # 解码器
        detector_data, obs_data, error_data=sampler.sample(shots=sample_number)  # 样本
        predictions=decoder.decode_batch(detector_data)  # 解码器对每个样本的预测
        ##  计算逻辑错误率
        num_errors=0
        for shot in range(sample_number):
            actual_for_shot=obs_data[shot]
            predicted_for_shot=predictions[shot]
            if not np.array_equal(actual_for_shot, predicted_for_shot):
                num_errors+=1

        ##  返回结果
        return num_errors/sample_number

    ##  USER：--生成stim的线路--
    def stim_circuit(self):
        circuit=stim.Circuit()
        flag_measure=0
        for i in range(len(self.sequence)):
            gate=self.sequence[i]
            name=gate['name']
            if name=='X' or name=='Y' or name=='Z' or name=='H' or name=='S' or name=='P':
                circuit.append(name, [gate['target']])

            ##  添加single-qubit上的噪声
            elif name=='X_ERROR' or name=='Y_ERROR' or name=='Z_ERROR':
                circuit.append(name, [gate['target']], gate['p'])

            ##  添加single-fermionic-site gate
            elif name in ['U', 'V', 'N', 'P', 'U_ERROR', 'V_ERROR', 'N_ERROR', 'CNX', 'CNN', 'B', 'braid', 'MN', 'FDEPOLARIZE1', 'FR']:
                raise NotImplementedError('stim只支持pauli circuit')

            ##  添加受控非门
            elif name=='CX':
                target=gate['target']
                circuit.append(name, target)

            ##  添加qubit上的去极化噪声
            elif name=='DEPOLARIZE1':
                target=gate['target']
                circuit.append(name, target, gate['p'])

            ##  强制初始化
            elif name=='FORCE':
                raise NotImplementedError('stim不支持强制初始化')

            ##  添加string算符的测量
            elif name=='MPP':

                ##  求string算符格式化表示
                op: PauliOperator=gate['target']
                occupy_x=op.occupy_x
                occupy_z=op.occupy_z

                ##  简单测量
                if len(occupy_x)==0 and len(occupy_z)==1:
                    if 'p' in gate:
                        circuit.append('MZ', [occupy_z[0]], gate['p'])
                    else:
                        circuit.append('MZ', [occupy_z[0]])
                    continue
                elif len(occupy_x)==1 and len(occupy_z)==0:
                    if 'p' in gate:
                        circuit.append('MX', [occupy_x[0]], gate['p'])
                    else:
                        circuit.append('MX', [occupy_x[0]])
                    continue
                elif len(occupy_x)==1 and len(occupy_z)==1 and occupy_x[0]==occupy_z[0]:
                    if 'p' in gate:
                        circuit.append('MY', [occupy_z[0]], gate['p'])
                    else:
                        circuit.append('MY', [occupy_z[0]])
                    continue

                ##  string operator测量
                op_str=''
                for i in range(self.pauli_number):
                    if i in occupy_x and i in occupy_z:
                        op_str+='Y'
                    elif i in occupy_z:
                        op_str+='Z'
                    elif i in occupy_x:
                        op_str+='X'
                    else:
                        op_str+='_'
                if 'p' in gate:
                    circuit.append('MPP', [stim.PauliString(op_str)], gate['p'])
                else:
                    circuit.append('MPP', [stim.PauliString(op_str)])

            ##  添加qubit重置
            elif name=='R':
                circuit.append('R', [gate['target']])

            ##  检测器
            elif name=='DETECTOR':
                circuit.append(name, [stim.target_rec(temp) for temp in gate['target']])

            ##  添加可观测量
            elif name=='OBSERVABLE_INCLUDE':
                circuit.append(name, [stim.target_rec(temp) for temp in gate['target']], flag_measure)
                flag_measure+=1
            else:
                raise NotImplementedError
        return circuit

    ##  USER：--绘制线路图--
    def draw(self, filename):

        # 绘制一个带有barriers和更多寄存器中，绘制一个新的电路
        F=qiskit.QuantumRegister(self.majorana_number, name='F')  # fermionic sites
        Q=qiskit.QuantumRegister(self.pauli_number, name='Q')  # qubits
        C=qiskit.ClassicalRegister(1, name='C')  # 经典寄存器
        A=qiskit.QuantumRegister(1, name='A')  # 无含义的ancilla
        circuit_qiskit=qiskit.QuantumCircuit(F, Q, C, A)
        braid=qiskit.circuit.ControlledGate(name='braid', num_qubits=2, params=[], label=None, num_ctrl_qubits=1, base_gate=XGate())
        cnn=qiskit.circuit.ControlledGate(name='CNN', num_qubits=2, params=[], label=None, num_ctrl_qubits=1, base_gate=ZGate())
        cnx=qiskit.circuit.ControlledGate(name='CNX', num_qubits=2, params=[], label=None, num_ctrl_qubits=1, base_gate=XGate())
        x_error=qiskit.circuit.Gate('X_ERROR', 1, label='X', params=[])
        y_error=qiskit.circuit.Gate('Y_ERROR', 1, label='Y', params=[])
        z_error=qiskit.circuit.Gate('Z_ERROR', 1, label='Z', params=[])
        u_error=qiskit.circuit.Gate('U_ERROR', 1, label='U', params=[])
        v_error=qiskit.circuit.Gate('V_ERROR', 1, label='V', params=[])
        n_error=qiskit.circuit.Gate('N_ERROR', 1, label='N', params=[])
        reset=qiskit.circuit.Gate('reset', 1, label=None, params=[])
        n=qiskit.circuit.Gate('N', 1, label='N', params=[])
        dep=qiskit.circuit.Gate('DEPOLARIZE', 1, label='D', params=[])

        ##  遍历电路序列，添加到qiskit电路中
        for gate in self.sequence:
            if gate['name']=='R':
                circuit_qiskit.append(reset, [Q[gate['target']]])
            elif gate['name']=='FR':
                circuit_qiskit.append(reset, [F[gate['target']]])
            elif gate['name']=='X':
                circuit_qiskit.x(Q[gate['target']])
            elif gate['name']=='Y':
                circuit_qiskit.y(Q[gate['target']])
            elif gate['name']=='Z':
                circuit_qiskit.z(Q[gate['target']])
            elif gate['name']=='H':
                circuit_qiskit.h(Q[gate['target']])
            elif gate['name']=='S':
                circuit_qiskit.s(Q[gate['target']])
            elif gate['name']=='N':
                circuit_qiskit.append(n, [F[gate['target']]])
            elif gate['name']=='CX':
                circuit_qiskit.cx(Q[gate['target'][0]], Q[gate['target'][1]])
            elif gate['name']=='braid':
                circuit_qiskit.append(braid, [F[gate['target'][0]], F[gate['target'][1]]])
            elif gate['name']=='CNN':
                circuit_qiskit.append(cnn, [F[gate['target'][0]], F[gate['target'][1]]])
            elif gate['name']=='CNX':
                circuit_qiskit.append(cnx, [F[gate['target'][0]], Q[gate['target'][1]]])
            elif gate['name']=='X_ERROR':
                circuit_qiskit.append(x_error, [Q[gate['target']]])
            elif gate['name']=='Y_ERROR':
                circuit_qiskit.append(y_error, [Q[gate['target']]])
            elif gate['name']=='Z_ERROR':
                circuit_qiskit.append(z_error, [Q[gate['target']]])
            elif gate['name']=='U_ERROR':
                circuit_qiskit.append(u_error, [F[gate['target']]])
            elif gate['name']=='V_ERROR':
                circuit_qiskit.append(v_error, [F[gate['target']]])
            elif gate['name']=='N_ERROR':
                circuit_qiskit.append(n_error, [F[gate['target']]])
            elif gate['name']=='DEPOLARIZE1':
                circuit_qiskit.append(dep, [Q[gate['target']]])
            elif gate['name']=='FDEPOLARIZE1':
                circuit_qiskit.append(dep, [F[gate['target']]])
            elif gate['name']=='MPP':
                op=gate['target']
                if isinstance(op, MajoranaOperator):
                    f_flag_x=op.occupy_x
                    f_flag_z=op.occupy_z
                    f_flag_n=np.intersect1d(f_flag_x, f_flag_z)
                    f_flag_x=np.setdiff1d(f_flag_x, f_flag_n)
                    f_flag_z=np.setdiff1d(f_flag_z, f_flag_n)
                    f=np.concatenate([f_flag_x, f_flag_z, f_flag_n])
                    if len(f)>1:
                        mppx=qiskit.circuit.ControlledGate(name='MPPX', num_qubits=len(f)+1, params=[], label=None, num_ctrl_qubits=len(f),
                                                           base_gate=XGate())
                        circuit_qiskit.append(mppx, F[f.tolist()]+[A[0]])
                        circuit_qiskit.measure(A[0], C[0])
                    else:
                        circuit_qiskit.measure(F[f[0]], C[0])
                elif isinstance(op, PauliOperator):
                    p_flag_x=op.occupy_x
                    p_flag_z=op.occupy_z
                    p_flag_y=np.intersect1d(p_flag_x, p_flag_z)
                    p_flag_x=np.setdiff1d(p_flag_x, p_flag_y)
                    p_flag_z=np.setdiff1d(p_flag_z, p_flag_y)
                    p=np.concatenate([p_flag_x, p_flag_y, p_flag_z])
                    if len(p)>1:
                        mppx=qiskit.circuit.ControlledGate(name='MPPX', num_qubits=len(p)+1, params=[], label=None, num_ctrl_qubits=len(p),
                                                           base_gate=XGate())
                        circuit_qiskit.append(mppx, Q[p.tolist()]+[A[0]])
                        circuit_qiskit.measure(A[0], C[0])
                    else:
                        circuit_qiskit.measure(Q[p[0]], C[0])
                else:
                    raise CircuitError("cannot set parameters on immutable base gate")
        ##  格式化颜色
        red='#E77081'
        blue='#5375CD'
        green='#00857B'
        grey='#8C92AC'
        purple='#5D548C'
        orange='#F15D22'
        pink='#FFACC5'
        cyan='#C9DCC4'

        ##  绘制线路
        circuit_qiskit.draw(output='mpl', filename=filename, style={
            'displaycolor': {'cx': None, 'cy': None, 'cz': None,
                             'X_ERROR': red, 'Y_ERROR': red, 'Z_ERROR': red,
                             'U_ERROR': red, 'V_ERROR': red, 'N_ERROR': red,
                             'R': cyan, 'measure': grey,
                             'x': blue, 'y': blue, 'z': blue, 's': blue, 'N': blue, 'U': blue, 'V': blue,
                             'CNN': blue, 'CNX': purple, 'braid': pink,
                             'MPPX': grey
                             },
            'fontsize': 12
        })

    ##  USER：---复制函数---
    def copy(self):
        return copy.deepcopy(self)


def diff(measurement_sample,detectors):
    ##  计算探测器的结果
    detector_sample=np.empty(len(detectors), dtype=bool)
    flag_detector=0
    for i, detector in enumerate(detectors):
        value=measurement_sample[detector][0]
        detector_sample[flag_detector]=False
        for temp in measurement_sample[detector]:
            if value==temp:
                continue
            else:
                detector_sample[flag_detector]=True
                break
        flag_detector+=1
    return detector_sample