def init_operators(self):
        # Create operator matrix in numpy array

        self.ops = []
        for op_c in self.ops_c:
            op = c_to_r_mat(-1j * self.dt * op_c)
            self.ops.append(op)

        self.ops_len = len(self.ops)

        self.H0 = c_to_r_mat(-1j * self.dt * self.H0_c)
        self.identity_c = np.identity(self.state_num)
        self.identity = c_to_r_mat(self.identity_c)

        if self.Taylor_terms is None:
            self.exps = []
            self.scalings = []
            if self.state_transfer or self.no_scaling:
                comparisons = 1
            else:
                comparisons = 6
            d = 0
            while comparisons > 0:

                self.exp_terms = self.Choose_exp_terms(d)
                self.exps.append(self.exp_terms)
                self.scalings.append(self.scaling)
                comparisons = comparisons - 1
                d = d + 1
            self.complexities = np.add(self.exps, self.scalings)
            a = np.argmin(self.complexities)

            self.exp_terms = self.exps[a]
            self.scaling = self.scalings[a]
        else:
            self.exp_terms = self.Taylor_terms[0]
            self.scaling = self.Taylor_terms[1]

        if self.save:
            with H5File(self.file_path) as hf:
                hf.add('taylor_terms', data=self.exp_terms)
                hf.add('taylor_scaling', data=self.scaling)

        print("Using " + str(self.exp_terms) + " Taylor terms and " +
              str(self.scaling) + " Scaling & Squaring terms")

        i_array = np.eye(2 * self.state_num)
        op_matrix_I = i_array.tolist()

        self.H_ops = []
        for op in self.ops:
            self.H_ops.append(op)
        self.matrix_list = [self.H0]
        for ii in range(self.ops_len):
            self.matrix_list = self.matrix_list + [self.H_ops[ii]]
        self.matrix_list = self.matrix_list + [op_matrix_I]

        self.matrix_list = np.array(self.matrix_list)
def get_reg_loss(tfs):

    # Regulizer
    with tf.name_scope('reg_errors'):

        reg_loss = tfs.loss

        # amplitude
        if 'amplitude' in tfs.sys_para.reg_coeffs:
            amp_reg_alpha_coeff = tfs.sys_para.reg_coeffs['amplitude']
            amp_reg_alpha = amp_reg_alpha_coeff / float(tfs.sys_para.steps)
            reg_loss = reg_loss + amp_reg_alpha * tf.nn.l2_loss(tfs.ops_weight)

        # gaussian envelope
        if 'envelope' in tfs.sys_para.reg_coeffs:
            reg_alpha_coeff = tfs.sys_para.reg_coeffs['envelope']
            reg_alpha = reg_alpha_coeff / float(tfs.sys_para.steps)
            reg_loss = reg_loss + reg_alpha * tf.nn.l2_loss(
                tf.multiply(tfs.tf_one_minus_gaussian_envelope,
                            tfs.ops_weight))

        # Limiting the dwdt of control pulse
        if 'dwdt' in tfs.sys_para.reg_coeffs:
            zeros_for_training = tf.zeros([tfs.sys_para.ops_len, 2])
            new_weights = tf.concat([tfs.ops_weight, zeros_for_training], 1)
            new_weights = tf.concat([zeros_for_training, new_weights], 1)
            dwdt_reg_alpha_coeff = tfs.sys_para.reg_coeffs['dwdt']
            dwdt_reg_alpha = dwdt_reg_alpha_coeff / float(tfs.sys_para.steps)
            reg_loss = reg_loss + dwdt_reg_alpha * tf.nn.l2_loss(
                (new_weights[:, 1:] - new_weights[:, :tfs.sys_para.steps + 3])
                / tfs.sys_para.dt)

        # Limiting the d2wdt2 of control pulse
        if 'd2wdt2' in tfs.sys_para.reg_coeffs:
            d2wdt2_reg_alpha_coeff = tfs.sys_para.reg_coeffs['d2wdt2']
            d2wdt2_reg_alpha = d2wdt2_reg_alpha_coeff / float(
                tfs.sys_para.steps)
            reg_loss = reg_loss + d2wdt2_reg_alpha * tf.nn.l2_loss((new_weights[:, 2:] - \
                                                                              2 * new_weights[:,
                                                                                  1:tfs.sys_para.steps + 3] + new_weights[:,
                                                                                                               :tfs.sys_para.steps + 2]) / (
                                                                             tfs.sys_para.dt ** 2))
        # bandpass filter on the control
        if 'bandpass' in tfs.sys_para.reg_coeffs:
            ## currently does not support bandpass reg for CPU (no CPU kernel for FFT)
            if not tfs.sys_para.use_gpu:
                raise ValueError(
                    'currently does not support bandpass reg for CPU (no CPU kernel for FFT)'
                )

            bandpass_reg_alpha_coeff = tfs.sys_para.reg_coeffs['bandpass']
            bandpass_reg_alpha = bandpass_reg_alpha_coeff / float(
                tfs.sys_para.steps)

            tf_u = tf.cast(tfs.ops_weight, dtype=tf.complex64)

            tf_fft = tf.complex_abs(tf.fft(tf_u))

            band = np.array(tfs.sys_para.reg_coeffs['band'])

            band_id = (band * tfs.sys_para.total_time).astype(int)
            half_id = int(tfs.sys_para.steps / 2)

            fft_loss = bandpass_reg_alpha * (
                tf.reduce_sum(tf_fft[:, 0:band_id[0]]) +
                tf.reduce_sum(tf_fft[:, band_id[1]:half_id]))

            reg_loss = reg_loss + fft_loss

        # Limiting the access to forbidden states
        if 'forbidden_coeff_list' in tfs.sys_para.reg_coeffs:

            if tfs.sys_para.is_dressed:
                v_sorted = tf.constant(c_to_r_mat(
                    np.reshape(
                        sort_ev(tfs.sys_para.v_c, tfs.sys_para.dressed_id), [
                            len(tfs.sys_para.dressed_id),
                            len(tfs.sys_para.dressed_id)
                        ])),
                                       dtype=tf.float32)

            for inter_vec in tfs.inter_vecs:
                if tfs.sys_para.is_dressed and (
                        'forbid_dressed' in tfs.sys_para.reg_coeffs
                        and tfs.sys_para.reg_coeffs['forbid_dressed']):
                    inter_vec = tf.matmul(tf.transpose(v_sorted), inter_vec)
                for inter_reg_alpha_coeff, state in zip(
                        tfs.sys_para.reg_coeffs['forbidden_coeff_list'],
                        tfs.sys_para.reg_coeffs['states_forbidden_list']):
                    inter_reg_alpha = inter_reg_alpha_coeff / float(
                        tfs.sys_para.steps)
                    forbidden_state_pop = tf.square(inter_vec[state, :]) + \
                                          tf.square(inter_vec[tfs.sys_para.state_num + state, :])
                    reg_loss = reg_loss + inter_reg_alpha * tf.nn.l2_loss(
                        forbidden_state_pop)

        # Speeding up the gate time
        if 'speed_up' in tfs.sys_para.reg_coeffs:
            speed_up_reg_alpha_coeff = -tfs.sys_para.reg_coeffs['speed_up']
            speed_up_reg_alpha = speed_up_reg_alpha_coeff / float(
                tfs.sys_para.steps)

            target_vecs_all_timestep = tf.tile(
                tf.reshape(
                    tfs.target_vecs,
                    [2 * tfs.sys_para.state_num, 1,
                     len(tfs.inter_vecs)]), [1, tfs.sys_para.steps + 1, 1])

            target_vecs_inner_product = tfs.get_inner_product_3D(
                tfs.inter_vecs_packed, target_vecs_all_timestep)
            reg_loss = reg_loss + speed_up_reg_alpha * tf.nn.l2_loss(
                target_vecs_inner_product)

        return reg_loss
    def __init__(self, H0, Hops, Hnames, U, U0, total_time, steps,
                 states_concerned_list, dressed_info, maxA, draw,
                 initial_guess, show_plots, Unitary_error, state_transfer,
                 no_scaling, reg_coeffs, save, file_path, Taylor_terms,
                 use_gpu, use_inter_vecs, sparse_H, sparse_U, sparse_K):
        # Input variable
        self.sparse_U = sparse_U
        self.sparse_H = sparse_H
        self.sparse_K = sparse_K
        self.use_inter_vecs = use_inter_vecs
        self.use_gpu = use_gpu
        self.Taylor_terms = Taylor_terms
        self.dressed_info = dressed_info
        self.reg_coeffs = reg_coeffs
        self.file_path = file_path
        self.state_transfer = state_transfer
        self.no_scaling = no_scaling
        self.save = save
        self.H0_c = H0
        self.ops_c = Hops
        self.ops_max_amp = maxA
        self.Hnames = Hnames
        self.Hnames_original = Hnames  #because we might rearrange them later if we have different timescales
        self.total_time = total_time
        self.steps = steps
        self.show_plots = show_plots
        self.Unitary_error = Unitary_error

        if initial_guess is not None:
            # transform initial_guess to its corresponding base value
            self.u0 = initial_guess
            self.u0_base = np.zeros_like(self.u0)
            for ii in range(len(self.u0_base)):
                self.u0_base[ii] = self.u0[ii] / self.ops_max_amp[ii]
                if max(self.u0_base[ii]) > 1.0:
                    raise ValueError(
                        'Initial guess has strength > max_amp for op %d' %
                        (ii))
            self.u0_base = np.arcsin(
                self.u0_base)  #because we take the sin of weights later

        else:
            self.u0 = []
        self.states_concerned_list = states_concerned_list

        self.is_dressed = False
        self.U0_c = U0
        self.initial_unitary = c_to_r_mat(
            U0
        )  #CtoRMat is converting complex matrices to their equivalent real (double the size) matrices
        if self.state_transfer == False:
            self.target_unitary = c_to_r_mat(U)
        else:
            self.target_vectors = []

            for target_vector_c in U:
                self.target_vector = c_to_r_vec(target_vector_c)
                self.target_vectors.append(self.target_vector)

        if draw is not None:
            self.draw_list = draw[0]
            self.draw_names = draw[1]
        else:
            self.draw_list = []
            self.draw_names = []

        if dressed_info != None:
            self.v_c = dressed_info['eigenvectors']
            self.dressed_id = dressed_info['dressed_id']
            self.w_c = dressed_info['eigenvalues']
            self.is_dressed = dressed_info['is_dressed']
            self.H0_diag = np.diag(self.w_c)

        self.init_system()
        self.init_vectors()
        self.init_operators()
        self.init_one_minus_gaussian_envelope()
        self.init_guess()