quant_iron/algorithms/
time_evolution.rs

1use crate::{
2    components::{
3        pauli_string::SumOp,
4        state::State,
5    },
6    errors::Error,
7};
8use num_complex::Complex;
9
10/// Trotter decomposition orders.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum TrotterOrder {
13    /// First-order Trotter decomposition.
14    ///
15    /// For H = Σⱼ Hⱼ, approximates exp(-iHt) ≈ Πⱼ exp(-iHⱼt).
16    /// This is the simplest and fastest method but has O(t²) error per step.
17    First,
18    
19    /// Second-order Trotter decomposition.
20    ///
21    /// Uses the symmetric Suzuki formula for improved accuracy with O(t³) error per step.
22    /// Requires twice as many exponential operations as first-order.
23    Second,
24}
25
26/// Performs a single first-order Trotter step for time evolution.
27///
28/// Implements the first-order Trotter formula: exp(-iHdt) ≈ Πⱼ exp(-iHⱼdt),
29/// where H = Σⱼ Hⱼ is the Hamiltonian decomposed into a sum of terms.
30///
31/// # Arguments
32///
33/// * `hamiltonian` - The Hamiltonian operator represented as a `SumOp` (sum of Pauli strings)
34/// * `initial_state` - The quantum state to evolve
35/// * `dt` - The time step for evolution
36///
37/// # Returns
38///
39/// * `Result<State, Error>` - The evolved state after applying the Trotter step, or an error if the operation fails
40///
41/// # Errors
42///
43/// * Returns an error if any Pauli string operations fail (e.g., invalid qubit indices)
44/// * Returns an error if the Hamiltonian contains no terms
45pub fn first_order_trotter_step(
46    hamiltonian: &SumOp,
47    initial_state: &State,
48    dt: f64,
49) -> Result<State, Error> {
50    // Validate inputs
51    if hamiltonian.num_terms() == 0 {
52        return Err(Error::InvalidNumberOfQubits(0));
53    }
54
55    // Complex factor for -i*dt
56    let factor = Complex::new(0.0, -dt);
57    
58    // Apply each term in the Hamiltonian sequentially
59    let mut current_state = initial_state.clone();
60    
61    for term in &hamiltonian.terms {
62        current_state = term.apply_exp_factor(&current_state, factor)?;
63    }
64    
65    Ok(current_state)
66}
67
68/// Performs a single second-order Trotter step for time evolution.
69///
70/// Implements the second-order symmetric Trotter formula for improved accuracy.
71/// The symmetric decomposition reduces the leading error term, providing O(t³)
72/// accuracy compared to O(t²) for first-order.
73///
74/// # Arguments
75///
76/// * `hamiltonian` - The Hamiltonian operator represented as a sum of Pauli strings
77/// * `initial_state` - The quantum state to evolve
78/// * `dt` - The time step for evolution
79///
80/// # Returns
81///
82/// * `Result<State, Error>` - The evolved state after applying the second-order Trotter step,
83///   or an error if the operation fails
84///
85/// # Errors
86///
87/// * Returns an error if any Pauli string operations fail
88/// * Returns an error if the Hamiltonian contains no terms
89pub fn second_order_trotter_step(
90    hamiltonian: &SumOp,
91    initial_state: &State,
92    dt: f64,
93) -> Result<State, Error> {
94    // Validate inputs
95    if hamiltonian.num_terms() == 0 {
96        return Err(Error::InvalidNumberOfQubits(0));
97    }
98
99    // Complex factor for -i*dt/2
100    let half_factor = Complex::new(0.0, -dt / 2.0);
101    
102    let mut current_state = initial_state.clone();
103    
104    // First half: apply each term with dt/2
105    for term in &hamiltonian.terms {
106        current_state = term.apply_exp_factor(&current_state, half_factor)?;
107    }
108    
109    // Second half: apply each term in reverse order with dt/2
110    for term in hamiltonian.terms.iter().rev() {
111        current_state = term.apply_exp_factor(&current_state, half_factor)?;
112    }
113    
114    Ok(current_state)
115}
116
117/// Evolves a quantum state under a given Hamiltonian using Trotter decomposition.
118///
119/// This is the main time evolution function that applies the specified Trotter method
120/// for the given number of steps with the specified time step size.
121/// The total evolution approximates exp(-iH*dt*num_steps)|ψ⟩.
122///
123/// # Arguments
124///
125/// * `hamiltonian` - The Hamiltonian operator as a sum of Pauli strings
126/// * `initial_state` - The initial quantum state to evolve
127/// * `dt` - The time step for each evolution step
128/// * `num_steps` - The number of discrete time steps to use
129/// * `order` - The order of Trotter decomposition to apply
130///
131/// # Returns
132///
133/// * `Result<State, Error>` - The final evolved state after the complete time evolution,
134///   or an error if any step fails
135///
136/// # Errors
137///
138/// * Returns an error if any individual Trotter step fails
139/// * Returns an error if the Hamiltonian does not contain any terms
140pub fn trotter_evolve_state(
141    hamiltonian: &SumOp,
142    initial_state: &State,
143    dt: f64,
144    num_steps: usize,
145    order: TrotterOrder,
146) -> Result<State, Error> {
147    // Validate inputs
148    if hamiltonian.num_terms() == 0 {
149        return Err(Error::InvalidNumberOfQubits(0));
150    }
151    
152    // Apply Trotter steps iteratively
153    let mut current_state = initial_state.clone();
154    
155    for _ in 0..num_steps {
156        current_state = match order {
157            TrotterOrder::First => {
158                first_order_trotter_step(hamiltonian, &current_state, dt)?
159            },
160            TrotterOrder::Second => {
161                second_order_trotter_step(hamiltonian, &current_state, dt)?
162            },
163        };
164    }
165    
166    Ok(current_state)
167}