def _generate_fit_guesses(
        self,
        user_opt: curve.FitOptions,
        curve_data: curve.CurveData,
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Create algorithmic guess with analysis options and curve data.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.
            curve_data: Formatted data collection to fit.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        max_abs_y, _ = curve.guess.max_height(curve_data.y, absolute=True)

        user_opt.bounds.set_if_empty(
            amp=(-2 * max_abs_y, 2 * max_abs_y),
            tau=(0, np.inf),
            base=(-max_abs_y, max_abs_y),
            phase=(-np.pi, np.pi),
        )

        # Default guess values
        freq_guesses, base_guesses = [], []
        for series in ["X", "Y"]:
            data = curve_data.get_subset_of(series)
            freq_guesses.append(curve.guess.frequency(data.x, data.y))
            base_guesses.append(curve.guess.constant_sinusoidal_offset(data.y))

        freq_val = float(np.average(freq_guesses))
        user_opt.p0.set_if_empty(base=np.average(base_guesses))

        # Guess the exponential decay by combining both curves
        data_x = curve_data.get_subset_of("X")
        data_y = curve_data.get_subset_of("Y")
        decay_data = (data_x.y - user_opt.p0["base"]) ** 2 + (data_y.y - user_opt.p0["base"]) ** 2

        user_opt.p0.set_if_empty(
            tau=-curve.guess.exp_decay(data_x.x, decay_data),
            amp=0.5,
            phase=0.0,
        )

        opt_fp = user_opt.copy()
        opt_fp.p0.set_if_empty(freq=freq_val)

        opt_fm = user_opt.copy()
        opt_fm.p0.set_if_empty(freq=-freq_val)

        return [opt_fp, opt_fm]
Ejemplo n.º 2
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        curve_data = self._data()
        max_abs_y, _ = curve.guess.max_height(curve_data.y, absolute=True)

        user_opt.bounds.set_if_empty(
            amp=(-2 * max_abs_y, 2 * max_abs_y),
            freq=(0, np.inf),
            phase=(-np.pi, np.pi),
            base=(-max_abs_y, max_abs_y),
        )
        user_opt.p0.set_if_empty(
            freq=curve.guess.frequency(curve_data.x, curve_data.y),
            base=curve.guess.constant_sinusoidal_offset(curve_data.y),
        )
        user_opt.p0.set_if_empty(
            amp=curve.guess.max_height(curve_data.y - user_opt.p0["base"], absolute=True)[0],
        )

        options = []
        for phase_guess in np.linspace(0, np.pi, 5):
            new_opt = user_opt.copy()
            new_opt.p0.set_if_empty(phase=phase_guess)
            options.append(new_opt)

        return options
Ejemplo n.º 3
0
    def _generate_fit_guesses(
        self,
        user_opt: curve.FitOptions,
        curve_data: curve.CurveData,
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Create algorithmic guess with analysis options and curve data.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.
            curve_data: Formatted data collection to fit.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        # Use a fast Fourier transform to guess the frequency.
        x_data = curve_data.get_subset_of("series-0").x
        min_beta, max_beta = min(x_data), max(x_data)

        # Use the highest-frequency curve to estimate the oscillation frequency.
        series_label, reps_label = max(
            ("series-0", "reps0"),
            ("series-1", "reps1"),
            ("series-2", "reps2"),
            key=lambda x: self.options.fixed_parameters[x[1]],
        )
        curve_data = curve_data.get_subset_of(series_label)
        reps2 = self.options.fixed_parameters[reps_label]
        freqs_guess = curve.guess.frequency(curve_data.x, curve_data.y) / reps2
        user_opt.p0.set_if_empty(freq=freqs_guess)

        avg_x = (max(x_data) + min(x_data)) / 2
        span_x = max(x_data) - min(x_data)
        beta_bound = max(5 / user_opt.p0["freq"], span_x)

        ptp_y = np.ptp(curve_data.y)
        user_opt.bounds.set_if_empty(
            amp=(-2 * ptp_y, 0),
            freq=(0, np.inf),
            beta=(avg_x - beta_bound, avg_x + beta_bound),
            base=(min(curve_data.y) - ptp_y, max(curve_data.y) + ptp_y),
        )
        base_guess = (max(curve_data.y) - min(curve_data.y)) / 2
        user_opt.p0.set_if_empty(base=(user_opt.p0["amp"] or base_guess))

        # Drag curves can sometimes be very flat, i.e. averages of y-data
        # and min-max do not always make good initial guesses. We therefore add
        # 0.5 to the initial guesses. Note that we also set amp=-0.5 because the cosine function
        # becomes +1 at zero phase, i.e. optimal beta, in which y data should become zero
        # in discriminated measurement level.
        options = []
        for amp_factor in (-1, -0.5, -0.25):
            for beta_guess in np.linspace(min_beta, max_beta, 20):
                new_opt = user_opt.copy()
                new_opt.p0.set_if_empty(amp=ptp_y * amp_factor,
                                        beta=beta_guess)
                options.append(new_opt)

        return options
Ejemplo n.º 4
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        user_opt.bounds.set_if_empty(
            a=(0, 1),
            alpha=(0, 1),
            alpha_c=(0, 1),
            b=(0, 1),
        )

        # for standard RB curve
        std_curve = self._data(series_name="Standard")
        opt_std = user_opt.copy()
        opt_std = self._initial_guess(opt_std, std_curve.x, std_curve.y,
                                      self._num_qubits)

        # for interleaved RB curve
        int_curve = self._data(series_name="Interleaved")
        opt_int = user_opt.copy()
        if opt_int.p0["alpha_c"] is not None:
            opt_int.p0["alpha"] = opt_std.p0["alpha"] * opt_int.p0["alpha_c"]
        opt_int = self._initial_guess(opt_int, int_curve.x, int_curve.y,
                                      self._num_qubits)

        user_opt.p0.set_if_empty(
            a=np.mean([opt_std.p0["a"], opt_int.p0["a"]]),
            alpha=opt_std.p0["alpha"],
            alpha_c=min(opt_int.p0["alpha"] / opt_std.p0["alpha"], 1),
            b=np.mean([opt_std.p0["b"], opt_int.p0["b"]]),
        )

        return user_opt
Ejemplo n.º 5
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        # Use a fast Fourier transform to guess the frequency.
        x_data = self._data("series-0").x
        min_beta, max_beta = min(x_data), max(x_data)

        freqs_guesses = {}
        for i in range(3):
            curve_data = self._data(f"series-{i}")
            freqs_guesses[f"freq{i}"] = curve.guess.frequency(
                curve_data.x, curve_data.y)
        user_opt.p0.set_if_empty(**freqs_guesses)

        max_abs_y, _ = curve.guess.max_height(self._data().y, absolute=True)
        freq_bound = max(10 / user_opt.p0["freq0"], max(x_data))

        user_opt.bounds.set_if_empty(
            amp=(-2 * max_abs_y, 0),
            freq0=(0, np.inf),
            freq1=(0, np.inf),
            freq2=(0, np.inf),
            beta=(-freq_bound, freq_bound),
            base=(-max_abs_y, max_abs_y),
        )
        user_opt.p0.set_if_empty(base=0.5)

        # Drag curves can sometimes be very flat, i.e. averages of y-data
        # and min-max do not always make good initial guesses. We therefore add
        # 0.5 to the initial guesses. Note that we also set amp=-0.5 because the cosine function
        # becomes +1 at zero phase, i.e. optimal beta, in which y data should become zero
        # in discriminated measurement level.
        options = []
        for amp_guess in (0.5, -0.5):
            for beta_guess in np.linspace(min_beta, max_beta, 20):
                new_opt = user_opt.copy()
                new_opt.p0.set_if_empty(amp=amp_guess, beta=beta_guess)
                options.append(new_opt)

        return options
Ejemplo n.º 6
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.

        Raises:
            CalibrationError: When ``angle_per_gate`` is missing.
        """
        n_guesses = self._get_option("number_guesses")

        curve_data = self._data()
        max_abs_y, _ = curve.guess.max_height(curve_data.y, absolute=True)
        max_y, min_y = np.max(curve_data.y), np.min(curve_data.y)

        user_opt.bounds.set_if_empty(d_theta=(-np.pi, np.pi),
                                     base=(-max_abs_y, max_abs_y))
        user_opt.p0.set_if_empty(base=(max_y + min_y) / 2)

        if "amp" in user_opt.p0:
            user_opt.p0.set_if_empty(amp=max_y - min_y)
            user_opt.bounds.set_if_empty(amp=(-2 * max_abs_y, 2 * max_abs_y))

        # Base the initial guess on the intended angle_per_gate.
        angle_per_gate = self._get_option("angle_per_gate")

        if angle_per_gate is None:
            raise CalibrationError(
                "The angle_per_gate was not specified in the analysis options."
            )

        guess_range = max(abs(angle_per_gate), np.pi / 2)
        options = []
        for d_theta_guess in np.linspace(-guess_range, guess_range, n_guesses):
            new_opt = user_opt.copy()
            new_opt.p0.set_if_empty(d_theta=d_theta_guess)
            options.append(new_opt)

        return options
    def _generate_fit_guesses(
        self,
        user_opt: curve.FitOptions,
        curve_data: curve.CurveData,
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Create algorithmic guess with analysis options and curve data.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.
            curve_data: Formatted data collection to fit.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        fixed_params = self.options.fixed_parameters

        max_abs_y, _ = curve.guess.max_height(curve_data.y, absolute=True)
        max_y, min_y = np.max(curve_data.y), np.min(curve_data.y)

        user_opt.bounds.set_if_empty(d_theta=(-0.8 * np.pi, 0.8 * np.pi),
                                     base=(-max_abs_y, max_abs_y))
        user_opt.p0.set_if_empty(base=(max_y + min_y) / 2)

        if "amp" in user_opt.p0:
            user_opt.p0.set_if_empty(amp=max_y - min_y)
            user_opt.bounds.set_if_empty(amp=(0, 2 * max_abs_y))
            amp = user_opt.p0["amp"]
        else:
            # Fixed parameter
            amp = fixed_params.get("amp", 1.0)

        # Base the initial guess on the intended angle_per_gate and phase offset.
        apg = user_opt.p0.get("angle_per_gate",
                              fixed_params.get("angle_per_gate", 0.0))
        phi = user_opt.p0.get("phase_offset",
                              fixed_params.get("phase_offset", 0.0))

        # Prepare logical guess for specific condition (often satisfied)
        d_theta_guesses = []

        offsets = apg * curve_data.x + phi
        for i in range(curve_data.x.size):
            xi = curve_data.x[i]
            yi = curve_data.y[i]
            if np.isclose(offsets[i] % np.pi, np.pi / 2) and xi > 0:
                # Condition satisfied: i.e. cos(apg x - phi) = 0
                err = -np.sign(np.sin(offsets[i])) * (
                    yi - user_opt.p0["base"]) / (0.5 * amp)
                # Validate estimate. This is just the first order term of Maclaurin expansion.
                if np.abs(err) < 0.5:
                    d_theta_guesses.append(err / xi)
                else:
                    # Terminate guess generation because larger d_theta x will start to
                    # reduce net y value and underestimate the rotation.
                    break

        # Add naive guess for more coverage
        guess_range = max(abs(apg), np.pi / 2)
        d_theta_guesses.extend(np.linspace(-guess_range, guess_range, 11))

        options = []
        for d_theta_guess in d_theta_guesses:
            new_opt = user_opt.copy()
            new_opt.p0.set_if_empty(d_theta=d_theta_guess)
            options.append(new_opt)

        return options
Ejemplo n.º 8
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        curve_data = self._data()

        user_opt.p0.set_if_empty(
            amp=0.5,
            base=curve.guess.constant_sinusoidal_offset(curve_data.y),
        )

        # frequency resolution of this scan
        df = 1 / ((curve_data.x[1] - curve_data.x[0]) * len(curve_data.x))

        if user_opt.p0["freq"] is not None:
            # If freq guess is provided
            freq_guess = user_opt.p0["freq"]

            freqs = [freq_guess]
        else:
            freq_guess = curve.guess.frequency(curve_data.x, curve_data.y - user_opt.p0["base"])

            # The FFT might be up to 1/2 bin off
            if freq_guess > df:
                freqs = [freq_guess - df, freq_guess, freq_guess + df]
            else:
                freqs = [0.0, freq_guess]

        # Set guess for decay parameter based on estimated frequency
        if freq_guess > df:
            alpha = curve.guess.oscillation_exp_decay(
                curve_data.x, curve_data.y - user_opt.p0["base"], freq_guess=freq_guess
            )
        else:
            # Very low frequency. Assume standard exponential decay
            alpha = curve.guess.exp_decay(curve_data.x, curve_data.y)

        if alpha != 0.0:
            user_opt.p0.set_if_empty(tau=-1 / alpha)
        else:
            # Likely there is no slope. Cannot fit constant line with this model.
            # Set some large enough number against to the scan range.
            user_opt.p0.set_if_empty(tau=100 * np.max(curve_data.x))

        user_opt.bounds.set_if_empty(
            amp=[0, 1.5],
            base=[0, 1.5],
            tau=(0, np.inf),
            freq=(0, 10 * freq_guess),
            phi=(-np.pi, np.pi),
        )

        # more robust estimation
        options = []
        for freq in freqs:
            for phi in np.linspace(-np.pi, np.pi, 5)[:-1]:
                new_opt = user_opt.copy()
                new_opt.p0.set_if_empty(freq=freq, phi=phi)
                options.append(new_opt)

        return options
Ejemplo n.º 9
0
    def _generate_fit_guesses(
        self, user_opt: curve.FitOptions
    ) -> Union[curve.FitOptions, List[curve.FitOptions]]:
        """Compute the initial guesses.

        Args:
            user_opt: Fit options filled with user provided guess and bounds.

        Returns:
            List of fit options that are passed to the fitter function.
        """
        user_opt.bounds.set_if_empty(t_off=(0, np.inf), b=(-1, 1))

        user_opt.p0.set_if_empty(t_off=self._t_off_initial_guess(), b=1e-9)

        guesses = defaultdict(list)
        for control in (0, 1):
            x_data = self._data(series_name=f"x|c={control}")
            y_data = self._data(series_name=f"y|c={control}")
            z_data = self._data(series_name=f"z|c={control}")

            omega_xyz = []
            for data in (x_data, y_data, z_data):
                ymin, ymax = np.percentile(data.y, [10, 90])
                if ymax - ymin < 0.2:
                    # oscillation amplitude might be almost zero,
                    # then exclude from average because of lower SNR
                    continue
                fft_freq = curve.guess.frequency(data.x, data.y)
                omega_xyz.append(fft_freq)
            if omega_xyz:
                omega = 2 * np.pi * np.average(omega_xyz)
            else:
                omega = 1e-3

            zmin, zmax = np.percentile(z_data.y, [10, 90])
            theta = np.arccos(np.sqrt((zmax - zmin) / 2))

            # The FFT might be up to 1/2 bin off
            df = 2 * np.pi / ((z_data.x[1] - z_data.x[0]) * len(z_data.x))
            for omega_shifted in [omega, omega - df / 2, omega + df / 2]:
                for phi in np.linspace(-np.pi, np.pi, 5):
                    px = omega_shifted * np.cos(theta) * np.cos(phi)
                    py = omega_shifted * np.cos(theta) * np.sin(phi)
                    pz = omega_shifted * np.sin(theta)
                    guesses[control].append(
                        {
                            f"px{control}": px,
                            f"py{control}": py,
                            f"pz{control}": pz,
                        }
                    )
            if omega < df:
                # empirical guess for low frequency case
                guesses[control].append(
                    {
                        f"px{control}": omega,
                        f"py{control}": omega,
                        f"pz{control}": 0,
                    }
                )

        fit_options = []
        # combine all guesses in Cartesian product
        for p0s, p1s in product(guesses[0], guesses[1]):
            new_opt = user_opt.copy()
            new_opt.p0.set_if_empty(**p0s, **p1s)
            fit_options.append(new_opt)

        return fit_options