Skip to main content

intrico/
lib.rs

1//! # 🚀 Intrico
2//!
3//! ### High-performance quantum computing library for Rust
4//! **Simulate quantum circuits with precision and speed**
5//!
6
7pub mod encoder;
8pub mod library;
9pub mod visualisations;
10
11// Re-exports from intrico-core — users only need to depend on `intrico`
12pub use intrico_core::{
13    ClassicalRegister,
14    Complex,
15    ExecutionContext,
16    ExecutionTime,
17    GateKind,
18    MeasurementResult,
19    Operation,
20    QuantumCircuit,
21    QuantumGate,
22    QuantumNode,
23    QuantumState,
24    SamplingResult,
25};
26
27pub use visualisations::plot_histogram;
28
29/// Execute a circuit once, returning the statevector and classical register.
30pub fn execute(circuit: &QuantumCircuit) -> MeasurementResult {
31    use std::time::Instant;
32    let start = Instant::now();
33    let mut ctx = ExecutionContext::new(circuit.num_qubits(), circuit.classical_regs());
34    ctx.run(circuit);
35    let (statevector, classical_register) = ctx.into_inner();
36    MeasurementResult {
37        statevector,
38        classical_register,
39        shots: 1,
40        execution_time: ExecutionTime::from_duration(start.elapsed()),
41    }
42}
43
44/// Sample a circuit over `shots` executions, returning outcome counts.
45pub fn sample(circuit: &QuantumCircuit, shots: usize) -> SamplingResult {
46    use std::collections::HashMap;
47    use std::time::Instant;
48    use rand::RngExt;
49
50    let start = Instant::now();
51    let mut counts: HashMap<String, usize> = HashMap::new();
52    let creg_size = circuit.classical_regs();
53
54    if circuit.has_terminal_measurements_only() {
55        // Optimised: run gates once, sample from the statevector probability distribution
56        let mut ctx = ExecutionContext::new(circuit.num_qubits(), 0);
57        ctx.run_gates_only(circuit);
58
59        let probs: Vec<f64> = ctx
60            .state()
61            .statevector()
62            .iter()
63            .map(|a| a.norm_squared())
64            .collect();
65
66        let measurements: Vec<(usize, usize)> = circuit
67            .nodes()
68            .iter()
69            .filter_map(|n| match &n.operation {
70                Operation::Measure { qubit, classical_bit } => Some((*qubit, *classical_bit)),
71                _ => None,
72            })
73            .collect();
74
75        let dim = 1 << circuit.num_qubits();
76        let mut rng = rand::rng();
77
78        for _ in 0..shots {
79            let r: f64 = rng.random();
80            let mut cumulative = 0.0;
81            let mut sampled_index = dim - 1;
82            for (i, &p) in probs.iter().enumerate() {
83                cumulative += p;
84                if r < cumulative {
85                    sampled_index = i;
86                    break;
87                }
88            }
89
90            let mut creg = ClassicalRegister::new(creg_size);
91            for &(qubit, classical_bit) in &measurements {
92                creg.set(classical_bit, ((sampled_index >> qubit) & 1) as u8);
93            }
94            *counts.entry(creg.bitstring()).or_insert(0) += 1;
95        }
96    } else {
97        // Full re-execution each shot (required for mid-circuit measurement)
98        for _ in 0..shots {
99            let mut ctx = ExecutionContext::new(circuit.num_qubits(), creg_size);
100            ctx.run(circuit);
101            *counts
102                .entry(ctx.classical_register().bitstring())
103                .or_insert(0) += 1;
104        }
105    }
106
107    SamplingResult {
108        counts,
109        shots,
110        execution_time: ExecutionTime::from_duration(start.elapsed()),
111    }
112}