# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

from dataclasses import KW_ONLY, dataclass, field
from typing import cast, Optional

from ..._architecture import Architecture, ISAContext
from ...instruction_ids import (
    T,
    PREP_X,
    PREP_Z,
    MEAS_XX,
    MEAS_ZZ,
    MEAS_X,
    MEAS_Z,
)
from ..._instruction import ISA


@dataclass
class Majorana(Architecture):
    """
    This class models physical instructions that may be relevant for future
    Majorana qubits. For these qubits, we assume that measurements and the
    physical T gate each take 1 µs. Owing to topological protection in the
    hardware, we assume single and two-qubit measurement error rates (Clifford
    error rates) in $10^{-4}$, $10^{-5}$, and $10^{-6}$ as a range between
    realistic and optimistic targets.  Non-Clifford operations in this
    architecture do not have topological protection, so we assume a 5%, 1.5%,
    and 1% error rate for non-Clifford physical T gates for the three cases,
    respectively.

    Attributes:
        error_rate: The error rate for physical Clifford operations (state
            preparation and measurement).  Defaults to 1e-5, with a domain of
            [1e-4, 1e-5, 1e-6] for use in design space exploration.
        time: The time (in ns) for physical Clifford operations and the T gate.
            Defaults to 1000 ns (1 µs).

    References:

    - Torsten Karzig, Christina Knapp, Roman M. Lutchyn, Parsa Bonderson,
      Matthew B. Hastings, Chetan Nayak, Jason Alicea, Karsten Flensberg,
      Stephan Plugge, Yuval Oreg, Charles M. Marcus, Michael H. Freedman:
      Scalable Designs for Quasiparticle-Poisoning-Protected Topological Quantum
      Computation with Majorana Zero Modes,
      [arXiv:1610.05289](https://arxiv.org/abs/1610.05289)
    - Alexei Kitaev: Unpaired Majorana fermions in quantum wires,
      [arXiv:cond-mat/0010440](https://arxiv.org/abs/cond-mat/0010440)
    - Sankar Das Sarma, Michael Freedman, Chetan Nayak: Majorana Zero Modes and
      Topological Quantum Computation,
      [arXiv:1501.02813](https://arxiv.org/abs/1501.02813)
    """

    _: KW_ONLY
    error_rate: float = field(default=1e-5, metadata={"domain": [1e-4, 1e-5, 1e-6]})
    time: int = 1_000
    t_error_rate: Optional[float] = None

    def __post_init__(self) -> None:
        if self.t_error_rate is None:
            if abs(self.error_rate - 1e-4) <= 1e-8:
                self.t_error_rate = 0.05
            elif abs(self.error_rate - 1e-5) <= 1e-8:
                self.t_error_rate = 0.015
            elif abs(self.error_rate - 1e-6) <= 1e-8:
                self.t_error_rate = 0.01
            else:
                raise ValueError(
                    "Invalid error_rate value. Must be one of [1e-4, 1e-5, 1e-6]."
                )

    def provided_isa(self, ctx: ISAContext) -> ISA:
        return ctx.make_isa(
            ctx.add_instruction(PREP_X, time=self.time, error_rate=self.error_rate),
            ctx.add_instruction(PREP_Z, time=self.time, error_rate=self.error_rate),
            ctx.add_instruction(
                MEAS_XX, arity=2, time=self.time, error_rate=self.error_rate
            ),
            ctx.add_instruction(
                MEAS_ZZ, arity=2, time=self.time, error_rate=self.error_rate
            ),
            ctx.add_instruction(MEAS_X, time=self.time, error_rate=self.error_rate),
            ctx.add_instruction(MEAS_Z, time=self.time, error_rate=self.error_rate),
            # NOTE From __post_init__, t_error_rate is guaranteed to be non-None
            # if error_rate is valid, so we can safely cast it to float here.
            ctx.add_instruction(
                T, time=self.time, error_rate=cast(float, self.t_error_rate)
            ),
        )
