quantrs2_sim/quantum_chemistry/
types.rs1use crate::error::{Result, SimulatorError};
6use crate::fermionic_simulation::{FermionicHamiltonian, FermionicOperator, FermionicString};
7use crate::pauli::{PauliOperator, PauliOperatorSum, PauliString};
8use scirs2_core::ndarray::{Array1, Array2, Array4};
9use scirs2_core::random::prelude::*;
10use scirs2_core::Complex64;
11use std::collections::HashMap;
12use std::f64::consts::PI;
13
14#[derive(Debug, Clone)]
16pub struct MolecularHamiltonian {
17 pub one_electron_integrals: Array2<f64>,
19 pub two_electron_integrals: Array4<f64>,
21 pub nuclear_repulsion: f64,
23 pub num_orbitals: usize,
25 pub num_electrons: usize,
27 pub fermionic_hamiltonian: FermionicHamiltonian,
29 pub pauli_hamiltonian: Option<PauliOperatorSum>,
31}
32#[derive(Debug, Clone)]
34pub struct MolecularOrbitals {
35 pub coefficients: Array2<f64>,
37 pub energies: Array1<f64>,
39 pub occupations: Array1<f64>,
41 pub num_basis: usize,
43 pub num_orbitals: usize,
45}
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum FermionMapping {
49 JordanWigner,
51 Parity,
53 BravyiKitaev,
55 SymmetryConservingBK,
57 FenwickTree,
59}
60#[derive(Debug, Clone, Default)]
62pub struct ChemistryStats {
63 pub total_time_ms: f64,
65 pub hamiltonian_time_ms: f64,
67 pub vqe_time_ms: f64,
69 pub circuit_evaluations: usize,
71 pub parameter_updates: usize,
73 pub memory_usage_mb: f64,
75 pub hamiltonian_terms: usize,
77}
78#[derive(Debug, Clone)]
80pub struct FermionMapper {
81 pub(super) method: FermionMapping,
83 pub(crate) num_spin_orbitals: usize,
85 mapping_cache: HashMap<String, PauliString>,
87}
88impl FermionMapper {
89 #[must_use]
90 pub fn new(method: FermionMapping, num_spin_orbitals: usize) -> Self {
91 Self {
92 method,
93 num_spin_orbitals,
94 mapping_cache: HashMap::new(),
95 }
96 }
97 pub(super) fn map_fermionic_string(
98 &self,
99 fermionic_string: &FermionicString,
100 ) -> Result<PauliString> {
101 let mut paulis = HashMap::new();
102 for (i, operator) in fermionic_string.operators.iter().enumerate() {
103 match operator {
104 FermionicOperator::Creation(site) => {
105 paulis.insert(*site, PauliOperator::X);
106 }
107 FermionicOperator::Annihilation(site) => {
108 paulis.insert(*site, PauliOperator::X);
109 }
110 _ => {
111 paulis.insert(i, PauliOperator::Z);
112 }
113 }
114 }
115 let mut operators_vec = vec![PauliOperator::I; self.num_spin_orbitals];
116 for (qubit, op) in paulis {
117 if qubit < operators_vec.len() {
118 operators_vec[qubit] = op;
119 }
120 }
121 let num_qubits = operators_vec.len();
122 Ok(PauliString {
123 operators: operators_vec,
124 coefficient: fermionic_string.coefficient,
125 num_qubits,
126 })
127 }
128 pub(super) fn calculate_dipole_moment(
130 &self,
131 density_matrix: &Array2<f64>,
132 ) -> Result<Array1<f64>> {
133 let mut dipole = Array1::zeros(3);
134 let num_orbitals = density_matrix.nrows();
135 for i in 0..num_orbitals {
136 for j in 0..num_orbitals {
137 let density_element = density_matrix[[i, j]];
138 if i == j {
139 let orbital_pos = i as f64 / num_orbitals as f64;
140 dipole[0] -= density_element * orbital_pos;
141 dipole[1] -= density_element * orbital_pos * 0.5;
142 dipole[2] -= density_element * orbital_pos * 0.3;
143 }
144 }
145 }
146 Ok(dipole)
147 }
148 #[must_use]
150 pub const fn get_method(&self) -> &FermionMapping {
151 &self.method
152 }
153 #[must_use]
155 pub const fn get_num_spin_orbitals(&self) -> usize {
156 self.num_spin_orbitals
157 }
158}
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum ChemistryOptimizer {
162 COBYLA,
164 SLSQP,
166 Powell,
168 GradientDescent,
170 Adam,
172 QuantumNaturalGradient,
174}
175#[derive(Debug, Clone)]
177pub struct VQEOptimizer {
178 method: ChemistryOptimizer,
180 pub(super) parameters: Array1<f64>,
182 pub(crate) bounds: Vec<(f64, f64)>,
184 pub(super) history: Vec<f64>,
186 gradients: Array1<f64>,
188 pub(super) learning_rate: f64,
190}
191impl VQEOptimizer {
192 #[must_use]
193 pub fn new(method: ChemistryOptimizer) -> Self {
194 Self {
195 method,
196 parameters: Array1::zeros(0),
197 bounds: Vec::new(),
198 history: Vec::new(),
199 gradients: Array1::zeros(0),
200 learning_rate: 0.01,
201 }
202 }
203 pub(super) fn initialize_parameters(&mut self, num_parameters: usize) {
204 self.parameters = Array1::from_vec(
205 (0..num_parameters)
206 .map(|_| (thread_rng().random::<f64>() - 0.5) * 0.1)
207 .collect(),
208 );
209 self.bounds = vec![(-PI, PI); num_parameters];
210 self.gradients = Array1::zeros(num_parameters);
211 }
212 pub fn initialize_parameters_public(&mut self, num_parameters: usize) {
214 self.initialize_parameters(num_parameters);
215 }
216 #[must_use]
218 pub const fn get_parameters(&self) -> &Array1<f64> {
219 &self.parameters
220 }
221 #[must_use]
223 pub fn get_bounds(&self) -> &[(f64, f64)] {
224 &self.bounds
225 }
226 #[must_use]
228 pub const fn get_method(&self) -> &ChemistryOptimizer {
229 &self.method
230 }
231}
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum ElectronicStructureMethod {
235 HartreeFock,
237 VQE,
239 QuantumCI,
241 QuantumCC,
243 QPE,
245}
246#[derive(Debug, Clone)]
248pub struct HartreeFockResult {
249 pub scf_energy: f64,
251 pub molecular_orbitals: MolecularOrbitals,
253 pub density_matrix: Array2<f64>,
255 pub fock_matrix: Array2<f64>,
257 pub converged: bool,
259 pub scf_iterations: usize,
261}
262#[derive(Debug, Clone)]
264pub struct VQEConfig {
265 pub ansatz: ChemistryAnsatz,
267 pub optimizer: ChemistryOptimizer,
269 pub max_iterations: usize,
271 pub energy_threshold: f64,
273 pub gradient_threshold: f64,
275 pub shots: usize,
277 pub enable_noise_mitigation: bool,
279}
280#[derive(Debug, Clone)]
282pub struct ElectronicStructureResult {
283 pub ground_state_energy: f64,
285 pub molecular_orbitals: MolecularOrbitals,
287 pub density_matrix: Array2<f64>,
289 pub dipole_moment: Array1<f64>,
291 pub converged: bool,
293 pub iterations: usize,
295 pub quantum_state: Array1<Complex64>,
297 pub vqe_history: Vec<f64>,
299 pub stats: ChemistryStats,
301}
302#[derive(Debug, Clone)]
304pub struct ElectronicStructureConfig {
305 pub method: ElectronicStructureMethod,
307 pub convergence_threshold: f64,
309 pub max_scf_iterations: usize,
311 pub active_space: Option<ActiveSpace>,
313 pub enable_second_quantization_optimization: bool,
315 pub fermion_mapping: FermionMapping,
317 pub enable_orbital_optimization: bool,
319 pub vqe_config: VQEConfig,
321}
322#[derive(Debug, Clone)]
324pub struct ActiveSpace {
325 pub num_electrons: usize,
327 pub num_orbitals: usize,
329 pub orbital_indices: Vec<usize>,
331 pub frozen_core: Vec<usize>,
333 pub frozen_virtual: Vec<usize>,
335}
336#[derive(Debug, Clone)]
338pub struct Molecule {
339 pub atomic_numbers: Vec<u32>,
341 pub positions: Array2<f64>,
343 pub charge: i32,
345 pub multiplicity: u32,
347 pub basis_set: String,
349}
350#[derive(Debug, Clone, Copy, PartialEq, Eq)]
352pub enum ChemistryAnsatz {
353 UCCSD,
355 HardwareEfficient,
357 SymmetryPreserving,
359 LowDepth,
361 Adaptive,
363}