Skip to content

Commit

Permalink
Added Hamiltonian reps for gates
Browse files Browse the repository at this point in the history
No hamiltonians are provided from the default lib (for now at least).
This is a basic mechanism for adding noise on a gate level by providing
hamiltonians with interference. A solver won't be provided in this
library (probably)
  • Loading branch information
Haadi-Khan committed Aug 2, 2024
1 parent c716f61 commit c5cec5c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 90 deletions.
51 changes: 27 additions & 24 deletions operation_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,41 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemMod};

#[proc_macro_attribute]
pub fn call_all_functions(
_attr: TokenStream,
item: TokenStream,
) -> TokenStream {
let input = parse_macro_input!(item as ItemMod);

#[proc_macro]
pub fn generate_insert_gates(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemMod);
let mod_name = &input.ident;
let mut call_statements = Vec::new();

if let Some((_, ref items)) = input.content {
for item in items.iter() {
if let syn::Item::Fn(f) = item {
let fn_name = &f.sig.ident;
call_statements.push(quote! {
#mod_name::#fn_name();
});
// Extract function names from the module
let mut gate_names = Vec::new();

if let Some((_, items)) = input.content {
for item in items {
if let syn::Item::Fn(func) = item {
let func_name = func.sig.ident.to_string();
gate_names.push(func_name);
}
}
}

let calls = quote! {
fn call_all() {
#(#call_statements)*
// Generate the map insertion code
let insertions = gate_names.iter().map(|name| {
let ident = syn::Ident::new(name, proc_macro2::Span::call_site());
quote! {
mtx_map.insert(
singleton::#ident().name().to_string(),
singleton::#ident().to_matrix(),
);
}
};
});

let code = quote! {
#input

#calls
let expanded = quote! {
{
let mut mtx_map = std::collections::HashMap::new();
#(#insertions)*
mtx_map
}
};

TokenStream::from(code)
TokenStream::from(expanded)
}
3 changes: 0 additions & 3 deletions src/gates.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// use operation_macro::call_all_functions;
pub mod singleton;

// #[call_all_functions]
// /// This module contains predefined singleton gates. This may be useful for
// /// noiseless simulations, or as a starting point for more complex gates.
// pub mod singleton {
// use ndarray::Array2;
// use numpy::Complex64;
Expand Down
35 changes: 31 additions & 4 deletions src/gates/singleton.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub fn hadamard() -> Gate {
],
)
.unwrap();
Gate::new("Hadamard".to_string(), vec![], None, TimeUnit::DT, matrix)
Gate::new("h".to_string(), vec![], None, TimeUnit::DT, matrix, None)
}

pub fn x() -> Gate {
Expand All @@ -35,7 +35,7 @@ pub fn x() -> Gate {
],
)
.unwrap();
Gate::new("Pauli-X".to_string(), vec![], None, TimeUnit::DT, matrix)
Gate::new("x".to_string(), vec![], None, TimeUnit::DT, matrix, None)
}

pub fn y() -> Gate {
Expand All @@ -49,7 +49,7 @@ pub fn y() -> Gate {
],
)
.unwrap();
Gate::new("Pauli-Y".to_string(), vec![], None, TimeUnit::DT, matrix)
Gate::new("y".to_string(), vec![], None, TimeUnit::DT, matrix, None)
}

pub fn z() -> Gate {
Expand All @@ -63,5 +63,32 @@ pub fn z() -> Gate {
],
)
.unwrap();
Gate::new("Pauli-Z".to_string(), vec![], None, TimeUnit::DT, matrix)
Gate::new("z".to_string(), vec![], None, TimeUnit::DT, matrix, None)
}

pub fn cx() -> Gate {
let matrix = Array2::from_shape_vec(
(4, 4),
vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
],
)
.unwrap();

Gate::new("cx".to_string(), vec![], None, TimeUnit::DT, matrix, None)
}
66 changes: 66 additions & 0 deletions src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ use ndarray::Array2;
use numpy::Complex64;
use std::fmt::Debug;

pub type TimeDependentFn = fn(f64) -> Complex64;

/// Parts of a total Hamiltonian for a gate. This breaks up terms to easily
/// determine commutativity and other properties.
#[derive(Debug, PartialEq, Clone)]
pub struct HamiltonianComponent {
time_fn: Option<TimeDependentFn>,
constant: Option<Complex64>,
operator: Array2<Complex64>,
}

#[derive(Debug, PartialEq, Clone)]
pub struct Hamiltonian {
components: Vec<HamiltonianComponent>,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TimeUnit {
DT,
Expand All @@ -26,6 +42,7 @@ pub struct Gate {
duration: Option<f64>,
unit: TimeUnit,
matrix: Array2<Complex64>,
hamiltonian: Option<Hamiltonian>,
}

/// GateBuilder enables custom gate creation
Expand All @@ -36,6 +53,7 @@ pub struct GateBuilder {
duration: Option<f64>,
unit: Option<TimeUnit>,
matrix: Option<Array2<Complex64>>,
hamiltonian: Option<Hamiltonian>,
}

#[derive(Debug, PartialEq, Clone)]
Expand All @@ -60,13 +78,15 @@ impl Gate {
duration: Option<f64>,
unit: TimeUnit,
matrix: Array2<Complex64>,
hamiltonian: Option<Hamiltonian>,
) -> Self {
Gate {
name,
params,
duration,
unit,
matrix,
hamiltonian,
}
}

Expand All @@ -79,6 +99,7 @@ impl Gate {
.duration(self.duration.unwrap())
.unit(self.unit)
.matrix(self.matrix.clone())
.hamiltonian(self.hamiltonian.clone().unwrap())
}

pub fn name(&self) -> &String {
Expand Down Expand Up @@ -131,6 +152,7 @@ impl GateBuilder {
duration: None,
unit: None,
matrix: None,
hamiltonian: None,
}
}

Expand Down Expand Up @@ -159,13 +181,57 @@ impl GateBuilder {
self
}

fn hamiltonian(mut self, hamiltonian: Hamiltonian) -> Self {
self.hamiltonian = Some(hamiltonian);
self
}

pub fn build(self) -> Gate {
Gate {
name: self.name.expect("Gate name not set"),
params: self.params.unwrap(),
duration: self.duration,
unit: self.unit.unwrap(),
matrix: self.matrix.expect("Gate matrix not set"),
hamiltonian: self.hamiltonian,
}
}
}

impl HamiltonianComponent {
pub fn new(
time_fn: TimeDependentFn,
constant: Complex64,
operator: Array2<Complex64>,
) -> Self {
HamiltonianComponent {
time_fn: Some(time_fn),
constant: Some(constant),
operator,
}
}

pub fn calculate(&self, t: f64) -> Array2<Complex64> {
let time_fn = self.time_fn.expect("Time function not set");
let constant = self.constant.expect("Constant not set");

let time_dep = time_fn(t);
let operator = self.operator.clone();

operator * time_dep + constant
}
}

impl Hamiltonian {
pub fn new(components: Vec<HamiltonianComponent>) -> Self {
Hamiltonian { components }
}

pub fn components(&self) -> &Vec<HamiltonianComponent> {
&self.components
}

pub fn is_commutative(&self) -> bool {
todo!()
}
}
75 changes: 16 additions & 59 deletions src/quantum_circuit/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@ use numpy::Complex64;
/// TODO: Migrate to a standard parser library instead of a custom one (didn't realized these existed before lol)
use crate::{
bit::{AncillaQubit, Bit, BitOps, Clbit, Qubit},
circuit_instruction::CircuitInstruction,
operations::{Delay, Gate, Operation, TimeUnit},
bit::{AncillaQubit, Bit, BitOps, Clbit, Qubit}, circuit_instruction::CircuitInstruction, operations::{Delay, Gate, Operation, TimeUnit}
};

use crate::gates::singleton as singleton;

use super::tokenizer::{Token, Tokenizer};

macro_rules! insert_gates {
($map:expr, $($gate:ident),*) => {
$(
$map.insert(
singleton::$gate().name().to_string(),
singleton::$gate().to_matrix(),
);
)*
};
}

/// Reads in the tokenized Qiskit circuit data and parses it into a QuantumCircuit object.
/// This should not be instantiated by itself, but rather through the QuantumCircuit::new() method
pub struct Parser {
Expand All @@ -28,61 +39,7 @@ impl Parser {

let mut mtx_map = HashMap::new();

// Eventually this will pull every function from the gates module
mtx_map.insert(
"x".to_string(),
Array2::from_shape_vec(
(2, 2),
vec![
Complex64::new(0.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(1.0, 0.0),
Complex64::new(-0.0, 0.0),
],
)
.unwrap(),
);
mtx_map.insert(
"y".to_string(),
Array2::from_shape_vec(
(2, 2),
vec![
Complex64::new(0.0, 0.0),
Complex64::new(0.0, -1.0),
Complex64::new(0.0, 1.0),
Complex64::new(-0.0, 0.0),
],
)
.unwrap(),
);
mtx_map.insert(
"z".to_string(),
Array2::from_shape_vec(
(2, 2),
vec![
Complex64::new(1.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(0.0, 0.0),
Complex64::new(-1.0, 0.0),
],
)
.unwrap(),
);

let hadamard = 1.0 / 2.0_f64.sqrt();
mtx_map.insert(
"h".to_string(),
Array2::from_shape_vec(
(2, 2),
vec![
Complex64::new(hadamard, 0.0),
Complex64::new(hadamard, 0.0),
Complex64::new(hadamard, 0.0),
Complex64::new(-hadamard, 0.0),
],
)
.unwrap(),
);
insert_gates!(mtx_map, hadamard, x, y, z, cx);

Self {
tokens,
Expand Down Expand Up @@ -181,7 +138,7 @@ impl Parser {
}
};
let operation =
Operation::Gate(Gate::new(name.clone(), params, None, TimeUnit::DT, mtx));
Operation::Gate(Gate::new(name.clone(), params, None, TimeUnit::DT, mtx, None));
operations.push(operation);
operations.last().unwrap().clone()
}
Expand Down

0 comments on commit c5cec5c

Please sign in to comment.