ruqu_vq_nas/
lib.rs

1//! # ruqu-vq-nas - Variational Quantum Neural Architecture Search
2//!
3//! This crate provides tools for automated quantum circuit architecture discovery
4//! using neural architecture search techniques. It enables finding optimal
5//! variational quantum circuit designs for quantum machine learning tasks.
6//!
7//! ## Features
8//!
9//! - **Search Space Definition**: Define quantum gate primitives, layer templates,
10//!   and connectivity constraints for circuit architectures.
11//!
12//! - **Architecture Encoding**: Encode architectures as discrete choices, continuous
13//!   relaxations, or embedding vectors for different search strategies.
14//!
15//! - **Search Algorithms**:
16//!   - Random search (baseline)
17//!   - Evolutionary search with mutation and crossover
18//!   - Regularized evolution (aging-based)
19//!   - Bayesian optimization interface
20//!   - Differentiable architecture search
21//!
22//! - **Evaluation Metrics**:
23//!   - Circuit expressibility (KL divergence from Haar random)
24//!   - Entanglement capability (Meyer-Wallach measure proxy)
25//!   - Training convergence proxy
26//!   - Hardware cost estimation
27//!
28//! - **Circuit Builder**: Build variational quantum circuits from architecture
29//!   specifications with parameter initialization and gradient computation helpers.
30//!
31//! ## Quick Start
32//!
33//! ```rust
34//! use ruqu_vq_nas::{
35//!     search_space::{SearchSpace, GateType},
36//!     search::{SearchAlgorithm, SearchConfig},
37//! };
38//! use rand::SeedableRng;
39//! use rand_chacha::ChaCha8Rng;
40//!
41//! // Create a search space
42//! let space = SearchSpace::hardware_efficient(4, 5).unwrap();
43//!
44//! // Configure search
45//! let config = SearchConfig::quick();
46//!
47//! // Run random search
48//! let mut rng = ChaCha8Rng::seed_from_u64(42);
49//! let mut search = SearchAlgorithm::random(space, config);
50//! let result = search.search(&mut rng).unwrap();
51//!
52//! println!("Best fitness: {}", result.best_evaluation.fitness);
53//! println!("Evaluations: {}", result.num_evaluations);
54//! ```
55//!
56//! ## Search Space Example
57//!
58//! ```rust
59//! use ruqu_vq_nas::search_space::{
60//!     SearchSpaceBuilder, GateType, ConnectivityGraph, LayerTemplate
61//! };
62//!
63//! // Build a custom search space
64//! let space = SearchSpaceBuilder::new(4)
65//!     .depth_range(2, 8)
66//!     .gate_set(vec![GateType::RY, GateType::RZ, GateType::CNOT])
67//!     .connectivity(ConnectivityGraph::linear(4))
68//!     .build()
69//!     .unwrap();
70//! ```
71//!
72//! ## Circuit Building Example
73//!
74//! ```rust
75//! use ruqu_vq_nas::circuit::{
76//!     CircuitBuilder, HardwareEfficientAnsatz, InitializationStrategy
77//! };
78//! use ruqu_vq_nas::search_space::GateType;
79//! use rand::SeedableRng;
80//! use rand_chacha::ChaCha8Rng;
81//!
82//! // Build a hardware-efficient ansatz
83//! let ansatz = HardwareEfficientAnsatz::new(4, 3);
84//! let circuit = ansatz.build();
85//!
86//! println!("Qubits: {}", circuit.num_qubits);
87//! println!("Parameters: {}", circuit.num_parameters);
88//! println!("Two-qubit gates: {}", circuit.two_qubit_gate_count());
89//!
90//! // Or use the builder pattern
91//! let mut rng = ChaCha8Rng::seed_from_u64(42);
92//! let circuit = CircuitBuilder::new(4)
93//!     .rotation_layer(&[ruqu_vq_nas::search_space::GateType::RY, ruqu_vq_nas::search_space::GateType::RZ])
94//!     .entangling_layer_linear(ruqu_vq_nas::search_space::GateType::CNOT)
95//!     .rotation_layer(&[ruqu_vq_nas::search_space::GateType::RY, ruqu_vq_nas::search_space::GateType::RZ])
96//!     .build_with_strategy(InitializationStrategy::Small { scale: 0.1 }, &mut rng);
97//! ```
98//!
99//! ## Evaluation Example
100//!
101//! ```rust
102//! use ruqu_vq_nas::circuit::HardwareEfficientAnsatz;
103//! use ruqu_vq_nas::evaluation::{CircuitEvaluator, EvaluationConfig};
104//! use rand::SeedableRng;
105//! use rand_chacha::ChaCha8Rng;
106//!
107//! let mut rng = ChaCha8Rng::seed_from_u64(42);
108//! let circuit = HardwareEfficientAnsatz::new(4, 2).build();
109//!
110//! let evaluator = CircuitEvaluator::with_config(EvaluationConfig::fast());
111//! let result = evaluator.evaluate(&circuit, &mut rng).unwrap();
112//!
113//! println!("Expressibility: {:.4}", result.expressibility);
114//! println!("Entanglement: {:.4}", result.entanglement_capability);
115//! println!("Fitness: {:.4}", result.fitness);
116//! ```
117//!
118//! ## Tier 3 Capability (Score 74)
119//!
120//! This crate implements Tier 3 quantum capabilities for automated circuit
121//! architecture discovery. The two-week validation tests are:
122//!
123//! 1. Search space definition works correctly
124//! 2. Architecture search runs and produces valid circuits
125//!
126//! ## Crate Features
127//!
128//! - `default` - Includes parallel processing via rayon
129//! - `parallel` - Enable parallel evaluation and search
130//! - `ruqu-integration` - Integration with the ruQu quantum simulation crate
131//! - `full` - All features enabled
132
133#![warn(missing_docs)]
134#![warn(rustdoc::missing_doc_code_examples)]
135#![deny(unsafe_code)]
136
137pub mod circuit;
138pub mod encoding;
139pub mod error;
140pub mod evaluation;
141pub mod search;
142pub mod search_space;
143
144// Re-export commonly used types at crate root
145pub use circuit::{
146    CircuitBuilder, GradientComputer, HardwareEfficientAnsatz, InitializationStrategy,
147    QuantumCircuit, StronglyEntanglingLayers,
148};
149pub use encoding::{
150    ArchitectureEmbedding, ArchitectureEncoder, ContinuousEncoding, DiscreteEncoding,
151};
152pub use error::{
153    CircuitError, EncodingError, EvaluationError, Result, SearchError, SearchSpaceError,
154    VqNasError,
155};
156pub use evaluation::{
157    BatchEvaluator, CircuitEvaluator, EntanglementCalculator, EvaluationConfig, EvaluationResult,
158    ExpressibilityCalculator, FitnessWeights, HardwareCost,
159};
160pub use search::{
161    BayesianOptimization, DifferentiableSearch, EvolutionaryConfig, EvolutionarySearch,
162    RandomSearch, RegularizedEvolution, RegularizedEvolutionConfig, SearchAlgorithm, SearchConfig,
163    SearchResult,
164};
165pub use search_space::{
166    ConnectivityGraph, EntanglementPattern, GateOperation, GateType, LayerTemplate, SearchSpace,
167    SearchSpaceBuilder,
168};
169
170/// Crate version
171pub const VERSION: &str = env!("CARGO_PKG_VERSION");
172
173/// Prelude module for convenient imports.
174pub mod prelude {
175    pub use crate::circuit::{
176        CircuitBuilder, HardwareEfficientAnsatz, InitializationStrategy, QuantumCircuit,
177    };
178    pub use crate::encoding::{DiscreteEncoding, ArchitectureEncoder};
179    pub use crate::error::{Result, VqNasError};
180    pub use crate::evaluation::{CircuitEvaluator, EvaluationConfig, EvaluationResult};
181    pub use crate::search::{SearchAlgorithm, SearchConfig, SearchResult};
182    pub use crate::search_space::{GateType, SearchSpace, SearchSpaceBuilder};
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use rand::SeedableRng;
189    use rand_chacha::ChaCha8Rng;
190
191    #[test]
192    fn test_version() {
193        assert!(!VERSION.is_empty());
194    }
195
196    #[test]
197    fn test_prelude_imports() {
198        use crate::prelude::*;
199
200        let space = SearchSpace::hardware_efficient(4, 3).unwrap();
201        let config = SearchConfig::quick();
202        let mut rng = ChaCha8Rng::seed_from_u64(42);
203
204        let mut alg = SearchAlgorithm::random(space, config);
205        let result = alg.search(&mut rng).unwrap();
206
207        assert!(result.num_evaluations > 0);
208    }
209
210    #[test]
211    fn test_full_workflow() {
212        // 1. Define search space
213        let space = SearchSpaceBuilder::new(3)
214            .depth_range(1, 4)
215            .gate_set(vec![GateType::RY, GateType::RZ, GateType::CNOT])
216            .build()
217            .unwrap();
218
219        // 2. Configure search
220        let config = SearchConfig::quick();
221
222        // 3. Run search
223        let mut rng = ChaCha8Rng::seed_from_u64(42);
224        let mut search = SearchAlgorithm::random(space.clone(), config);
225        let result = search.search(&mut rng).unwrap();
226
227        // 4. Build circuit from best architecture
228        let circuit = QuantumCircuit::from_encoding(&result.best_encoding, &space).unwrap();
229
230        // 5. Verify circuit properties
231        assert_eq!(circuit.num_qubits, 3);
232        assert!(circuit.validate().is_ok());
233    }
234
235    #[test]
236    fn test_evolutionary_workflow() {
237        let space = SearchSpace::hardware_efficient(3, 3).unwrap();
238        let config = SearchConfig::quick();
239        let evo_config = EvolutionaryConfig {
240            population_size: 10,
241            num_parents: 5,
242            ..Default::default()
243        };
244
245        let mut rng = ChaCha8Rng::seed_from_u64(42);
246        let mut search =
247            SearchAlgorithm::evolutionary(space, config, evo_config).unwrap();
248        let result = search.search(&mut rng).unwrap();
249
250        assert!(result.num_evaluations > 0);
251        assert!(result.best_evaluation.fitness.is_finite());
252    }
253
254    #[test]
255    fn test_circuit_builder_workflow() {
256        let mut rng = ChaCha8Rng::seed_from_u64(42);
257
258        let circuit = CircuitBuilder::new(4)
259            .h(0)
260            .h(1)
261            .h(2)
262            .h(3)
263            .rotation_layer(&[GateType::RY, GateType::RZ])
264            .entangling_layer_linear(GateType::CNOT)
265            .rotation_layer(&[GateType::RY])
266            .build_with_random_params(&mut rng);
267
268        assert_eq!(circuit.num_qubits, 4);
269        assert!(circuit.num_parameters > 0);
270        assert!(circuit.two_qubit_gate_count() > 0);
271    }
272
273    #[test]
274    fn test_evaluation_workflow() {
275        let mut rng = ChaCha8Rng::seed_from_u64(42);
276        let circuit = HardwareEfficientAnsatz::new(3, 2).build();
277
278        let evaluator = CircuitEvaluator::with_config(EvaluationConfig::fast());
279        let result = evaluator.evaluate(&circuit, &mut rng).unwrap();
280
281        // Check all metrics are in expected ranges
282        assert!(result.expressibility >= 0.0);
283        assert!(result.entanglement_capability >= 0.0 && result.entanglement_capability <= 1.0);
284        assert!(result.convergence_proxy >= 0.0 && result.convergence_proxy <= 1.0);
285        assert!(result.hardware_cost.gate_count > 0);
286    }
287
288    #[test]
289    fn test_encoding_workflow() {
290        let space = SearchSpace::hardware_efficient(4, 5).unwrap();
291        let mut rng = ChaCha8Rng::seed_from_u64(42);
292
293        // Create random discrete encoding
294        let discrete = DiscreteEncoding::random(&space, &mut rng);
295        assert!(discrete.validate(&space).is_ok());
296
297        // Convert to embedding
298        let encoder = ArchitectureEncoder::new(space.clone(), 32);
299        let embedding = encoder.embed(&discrete).unwrap();
300        assert_eq!(embedding.dimension, 32);
301
302        // Decode to operations
303        let operations = encoder.decode_to_operations(&discrete);
304        assert_eq!(operations.len(), discrete.depth);
305    }
306
307    #[test]
308    fn test_continuous_encoding_workflow() {
309        let space = SearchSpace::hardware_efficient(3, 3).unwrap();
310        let mut rng = ChaCha8Rng::seed_from_u64(42);
311
312        // Create continuous encoding
313        let continuous = ContinuousEncoding::random(&space, &mut rng);
314
315        // Sample discrete encodings
316        for _ in 0..5 {
317            let discrete = continuous.sample(&space, &mut rng);
318            assert!(discrete.validate(&space).is_ok());
319        }
320
321        // Get argmax discrete
322        let best_discrete = continuous.to_discrete(&space);
323        assert!(best_discrete.validate(&space).is_ok());
324    }
325
326    #[test]
327    fn test_gradient_computation() {
328        let circuit = CircuitBuilder::new(2).ry(0).ry(1).cnot(0, 1).build();
329
330        let computer = GradientComputer::new();
331
332        // Quadratic loss function
333        let loss = |params: &ndarray::Array1<f64>| -> f64 {
334            params.iter().map(|x| x.powi(2)).sum()
335        };
336
337        let grads = computer.compute_gradients(&circuit, loss).unwrap();
338        assert_eq!(grads.len(), 2);
339    }
340}