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(¤t_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(¤t_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(¤t_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, ¤t_state, dt)?
159 },
160 TrotterOrder::Second => {
161 second_order_trotter_step(hamiltonian, ¤t_state, dt)?
162 },
163 };
164 }
165
166 Ok(current_state)
167}