# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
# Copyright (c) 2025 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

from enum import Enum
from typing import Any, overload

from mqt.core.dd import MatrixDD, VectorDD
from mqt.core.ir import QuantumComputation

__all__ = [
    "CircuitSimulator",
    "DeterministicNoiseSimulator",
    "HybridSimulator",
    "HybridSimulatorMode",
    "PathSimulator",
    "PathSimulatorConfiguration",
    "PathSimulatorMode",
    "StochasticNoiseSimulator",
    "UnitarySimulator",
    "UnitarySimulatorMode",
]

class CircuitSimulator:
    def __init__(
        self,
        circ: QuantumComputation,
        approximation_step_fidelity: float = 1.0,
        approximation_steps: int = 1,
        approximation_strategy: str = "fidelity",
        seed: int = -1,
    ) -> None: ...
    def expectation_value(self, observable: QuantumComputation) -> float: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def get_constructed_dd(self) -> VectorDD: ...
    def set_tolerance(self, tol: float) -> None: ...
    def simulate(self, shots: int) -> dict[str, int]: ...
    def statistics(self) -> dict[str, str]: ...

class StochasticNoiseSimulator:
    def __init__(
        self,
        circ: QuantumComputation,
        approximation_step_fidelity: float = 1.0,
        approximation_steps: int = 1,
        approximation_strategy: str = "fidelity",
        seed: int = -1,
        noise_effects: str = "APD",
        noise_probability: float = 0.01,
        amp_damping_probability: float | None = 0.02,
        multi_qubit_gate_factor: float = 2,
    ) -> None: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def get_constructed_dd(self) -> VectorDD: ...
    def set_tolerance(self, tol: float) -> None: ...
    def simulate(self, shots: int) -> dict[str, int]: ...
    def statistics(self) -> dict[str, str]: ...

class DeterministicNoiseSimulator:
    def __init__(
        self,
        circ: QuantumComputation,
        approximation_step_fidelity: float = 1.0,
        approximation_steps: int = 1,
        approximation_strategy: str = "fidelity",
        seed: int = -1,
        noise_effects: str = "APD",
        noise_probability: float = 0.01,
        amp_damping_probability: float | None = 0.02,
        multi_qubit_gate_factor: float = 2,
    ) -> None: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def get_constructed_dd(self) -> VectorDD: ...
    def set_tolerance(self, tol: float) -> None: ...
    def simulate(self, shots: int) -> dict[str, int]: ...
    def statistics(self) -> dict[str, str]: ...

class HybridSimulatorMode(Enum):
    """Enumeration of modes for the :class:`~HybridSimulator`."""

    DD = ...
    amplitude = ...

class HybridSimulator:
    def __init__(
        self,
        circ: QuantumComputation,
        approximation_step_fidelity: float = 1.0,
        approximation_steps: int = 1,
        approximation_strategy: str = "fidelity",
        seed: int = -1,
        mode: HybridSimulatorMode = ...,
        nthreads: int = 2,
    ) -> None: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_final_amplitudes(self) -> list[complex]: ...
    def get_mode(self) -> HybridSimulatorMode: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def get_constructed_dd(self) -> VectorDD: ...
    def set_tolerance(self, tol: float) -> None: ...
    def simulate(self, shots: int) -> dict[str, int]: ...
    def statistics(self) -> dict[str, str]: ...

class PathSimulatorMode(Enum):
    """Enumeration of modes for the :class:`~PathCSimulator`."""

    alternating = ...
    bracket = ...
    gate_cost = ...
    pairwise_recursive = ...
    sequential = ...

class PathSimulatorConfiguration:
    def __init__(self) -> None: ...
    def json(self) -> dict[str, Any]: ...
    @property
    def bracket_size(self) -> int: ...
    @bracket_size.setter
    def bracket_size(self, arg0: int) -> None: ...
    @property
    def gate_cost(self) -> list[int]: ...
    @gate_cost.setter
    def gate_cost(self, arg0: list[int]) -> None: ...
    @property
    def mode(self) -> PathSimulatorMode: ...
    @mode.setter
    def mode(self, arg0: PathSimulatorMode) -> None: ...
    @property
    def seed(self) -> int: ...
    @seed.setter
    def seed(self, arg0: int) -> None: ...
    @property
    def starting_point(self) -> int: ...
    @starting_point.setter
    def starting_point(self, arg0: int) -> None: ...

class PathSimulator:
    @overload
    def __init__(self, circ: QuantumComputation, config: PathSimulatorConfiguration = ...) -> None: ...
    @overload
    def __init__(
        self,
        circ: QuantumComputation,
        mode: PathSimulatorMode = ...,
        bracket_size: int = 2,
        starting_point: int = 0,
        gate_cost: list[int] = ...,
        seed: int = ...,
    ) -> None: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def get_constructed_dd(self) -> VectorDD: ...
    def set_simulation_path(self, path: list[tuple[int, int]], assume_correct_order: bool = False) -> None: ...
    def set_tolerance(self, tol: float) -> None: ...
    def simulate(self, shots: int) -> dict[str, int]: ...
    def statistics(self) -> dict[str, str]: ...

class UnitarySimulatorMode(Enum):
    """Enumeration of modes for the :class:`~UnitarySimulator`."""

    recursive = ...
    sequential = ...

class UnitarySimulator:
    def __init__(
        self,
        circ: QuantumComputation,
        approximation_step_fidelity: float = 1.0,
        approximation_steps: int = 1,
        approximation_strategy: str = "fidelity",
        seed: int = -1,
        mode: UnitarySimulatorMode = ...,
    ) -> None: ...
    def construct(self) -> None: ...
    def get_active_matrix_node_count(self) -> int: ...
    def get_active_vector_node_count(self) -> int: ...
    def get_construction_time(self) -> float: ...
    def get_final_node_count(self) -> int: ...
    def get_max_node_count(self) -> int: ...
    def get_mode(self) -> UnitarySimulatorMode: ...
    def get_name(self) -> str: ...
    def get_number_of_qubits(self) -> int: ...
    def get_tolerance(self) -> float: ...
    def set_tolerance(self, tol: float) -> None: ...
    def statistics(self) -> dict[str, str]: ...
    def get_constructed_dd(self) -> MatrixDD: ...
