diff --git a/dqcsim/src/common/log/tee_file.rs b/dqcsim/src/common/log/tee_file.rs index e1b22403b..3d8a97c54 100644 --- a/dqcsim/src/common/log/tee_file.rs +++ b/dqcsim/src/common/log/tee_file.rs @@ -83,7 +83,7 @@ impl ::std::str::FromStr for TeeFileConfiguration { let file: PathBuf = splitter .next() .ok_or_else(|| { - TeeFileError::ParseError("Expected a colon in tee file description.".to_string()) + TeeFileError::ParseError("expected a colon in tee file description".to_string()) })? .into(); Ok(TeeFileConfiguration { filter, file }) @@ -111,7 +111,17 @@ mod test { TeeFileConfiguration::from_str("info:/tmp/hello:/there").unwrap(), TeeFileConfiguration::new(LoglevelFilter::Info, "/tmp/hello:/there"), ); - assert!(TeeFileConfiguration::from_str("hello").is_err()); + + let tfc = TeeFileConfiguration::from_str("hello"); + assert!(tfc.is_err()); + assert_eq!(tfc.unwrap_err().to_string(), "hello is not a valid loglevel filter, valid values are off, fatal, error, warn, note, info, debug, or trace"); + + let tfc = TeeFileConfiguration::from_str("info"); + assert!(tfc.is_err()); + assert_eq!( + tfc.unwrap_err().to_string(), + "expected a colon in tee file description" + ); } #[test] @@ -129,6 +139,13 @@ mod test { format!("{:?}", tf), "TeeFileConfiguration { filter: Info, file: \"hello:/there\" }" ); + + let tfc = TeeFileConfiguration::new(LoglevelFilter::Trace, "/dev/zero"); + let tf = TeeFile::new(tfc); + + assert!( + format!("{:?}", tf.unwrap()).starts_with("TeeFile { configuration: TeeFileConfiguration { filter: Trace, file: \"/dev/zero\" }, buffer: Some(File {") + ); } #[test] diff --git a/dqcsim/src/common/types/cycle.rs b/dqcsim/src/common/types/cycle.rs index 0143e922d..d3b3c55bf 100644 --- a/dqcsim/src/common/types/cycle.rs +++ b/dqcsim/src/common/types/cycle.rs @@ -69,6 +69,7 @@ impl Cycle { impl Into for Cycle { fn into(self) -> u64 { + assert!(self.0 >= 0, "Cycle count negative"); self.0 as u64 } } @@ -78,3 +79,64 @@ impl Into for Cycle { self.0 } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn zero() { + let c = Cycle::t_zero(); + assert_eq!(c, Cycle(0i64)); + } + + #[test] + fn advance() { + let c = Cycle::t_zero(); + let c = c.advance(1234u64); + assert_eq!(c, Cycle(1234)); + let c = c.advance(1u64); + assert_eq!(c, Cycle(1235)); + } + + #[test] + #[should_panic] + fn overflow() { + let c = Cycle::t_zero(); + c.advance(std::u64::MAX); + } + + #[test] + #[should_panic] + fn convert_negative() { + let a = 18446744073709551574u64; + let c = Cycle(-42); + let c: u64 = c.into(); + assert_eq!(a, c); + } + + #[test] + fn convert() { + let a = 1234u64; + let c = Cycle(1234); + let c: u64 = c.into(); + assert_eq!(a, c); + + let a = 1234i64; + let c = Cycle(1234); + let c: i64 = c.into(); + assert_eq!(a, c); + } + + #[test] + fn ops() { + let a = Cycle(21); + let b: CycleDelta = 21; + let c: CycleDelta = -2; + assert_eq!(a + b, Cycle(42)); + assert_eq!(a - b, Cycle(0)); + assert_eq!(a + c, Cycle(19)); + assert_eq!(a - c, Cycle(23)); + assert_eq!(a - a, 0 as CycleDelta); + } +} diff --git a/dqcsim/src/common/types/gate.rs b/dqcsim/src/common/types/gate.rs index 236d64ff8..375b46a9f 100644 --- a/dqcsim/src/common/types/gate.rs +++ b/dqcsim/src/common/types/gate.rs @@ -138,7 +138,7 @@ impl Gate { } // Check the size of the matrix. - let expected_size = 4 << (targets.len() - 1); + let expected_size = 2usize.pow(2 * targets.len() as u32); if matrix.len() != expected_size { return inv_arg(format!( "the matrix is expected to be of size {} but was {}", @@ -224,7 +224,7 @@ impl Gate { if targets.is_empty() { return inv_arg("cannot specify a matrix when there are no target qubits"); } else { - let expected_size = 4 << (targets.len() - 1); + let expected_size = 2usize.pow(2 * targets.len() as u32); if m.len() != expected_size { return inv_arg(format!( "the matrix is expected to be of size {} but was {}", @@ -283,3 +283,292 @@ impl Gate { } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn qref(q: u64) -> QubitRef { + QubitRef::from_foreign(q).unwrap() + } + + #[test] + fn new_unitary_no_targets() { + let targets = vec![]; + let controls = vec![]; + let matrix = vec![]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: at least one target qubit is required" + ); + } + + #[test] + fn new_unitary_dup_target() { + let targets = vec![qref(1), qref(1)]; + let controls = vec![]; + let matrix = vec![]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is used more than once" + ); + + let targets = vec![qref(1)]; + let controls = vec![qref(1)]; + let matrix = vec![]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is used more than once" + ); + } + + #[test] + fn new_unitary_bad_matrix_size() { + let targets = vec![qref(2)]; + let controls = vec![qref(1)]; + let matrix = vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: the matrix is expected to be of size 4 but was 3" + ); + + let targets = vec![qref(2), qref(3)]; + let controls = vec![]; + let matrix = vec![Complex64::new(1f64, 1f64)]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: the matrix is expected to be of size 16 but was 1" + ); + + let targets = vec![qref(1), qref(2), qref(3)]; + let controls = vec![]; + let matrix = vec![Complex64::new(1f64, 1f64)]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: the matrix is expected to be of size 64 but was 1" + ); + + let targets = vec![qref(1), qref(2), qref(3), qref(4)]; + let controls = vec![]; + let matrix = vec![Complex64::new(1f64, 1f64)]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: the matrix is expected to be of size 256 but was 1" + ); + } + + #[test] + fn new_unitary() { + let targets = vec![qref(1)]; + let controls = vec![qref(2)]; + let matrix = vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]; + let g = Gate::new_unitary(targets, controls, matrix); + assert!(g.is_ok()); + let g = g.unwrap(); + assert_eq!(g.get_name(), None); + assert_eq!(g.get_targets(), [qref(1)]); + assert_eq!(g.get_controls(), [qref(2)]); + assert_eq!(g.get_measures(), []); + assert_eq!( + g.get_matrix(), + Some(vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]) + ); + } + + #[test] + fn new_measurement_dup_qubit() { + let g = Gate::new_measurement(vec![qref(1), qref(1)]); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is measured more than once" + ); + } + + #[test] + fn new_measurement() { + let g = Gate::new_measurement(vec![qref(1)]); + assert!(g.is_ok()); + let g = g.unwrap(); + assert_eq!(g.get_name(), None); + assert_eq!(g.get_targets(), []); + assert_eq!(g.get_controls(), []); + assert_eq!(g.get_measures(), [qref(1)]); + assert_eq!(g.get_matrix(), None); + } + + #[test] + fn new_custom_dup_qubit() { + let name = ""; + let targets = vec![qref(1)]; + let controls = vec![qref(1)]; + let measures = vec![]; + let matrix = Some(vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is used more than once" + ); + + let name = ""; + let targets = vec![qref(1), qref(1)]; + let controls = vec![]; + let measures = vec![]; + let matrix = Some(vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is used more than once" + ); + } + + #[test] + fn new_custom_dup_measure() { + let name = ""; + let targets = vec![]; + let controls = vec![]; + let measures = vec![qref(1), qref(1)]; + let matrix: Option> = None; + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: qubit 1 is measured more than once" + ); + } + + #[test] + fn new_custom_matrix_no_targets() { + let name = ""; + let targets = vec![]; + let controls = vec![]; + let measures = vec![]; + let matrix = Some(vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: cannot specify a matrix when there are no target qubits" + ); + } + + #[test] + fn new_custom_bad_size() { + let name = ""; + let targets = vec![qref(1)]; + let controls = vec![]; + let measures = vec![]; + let matrix = Some(vec![ + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + Complex64::new(1f64, 1f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid argument: the matrix is expected to be of size 4 but was 3" + ); + } + + #[test] + fn new_custom() { + let name = "I"; + let targets = vec![qref(1)]; + let controls = vec![qref(2)]; + let measures = vec![qref(3)]; + let matrix = Some(vec![ + Complex64::new(1f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(1f64, 0f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_ok()); + let g = g.unwrap(); + assert_eq!(g.get_name(), Some("I")); + assert_eq!(g.get_targets(), [qref(1)]); + assert_eq!(g.get_controls(), [qref(2)]); + assert_eq!(g.get_measures(), [qref(3)]); + assert_eq!( + g.get_matrix(), + Some(vec![ + Complex64::new(1f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(1f64, 0f64), + ]) + ); + } + + #[test] + fn debug() { + let name = "I"; + let targets = vec![qref(1)]; + let controls = vec![qref(2)]; + let measures = vec![qref(3)]; + let matrix = Some(vec![ + Complex64::new(1f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(0f64, 0f64), + Complex64::new(1f64, 0f64), + ]); + let data = ArbData::default(); + let g = Gate::new_custom(name, targets, controls, measures, matrix, data); + assert!(g.is_ok()); + let g = g.unwrap(); + assert_eq!(format!("{:?}", g), "Gate { name: Some(\"I\"), targets: [QubitRef(1)], controls: [QubitRef(2)], measures: [QubitRef(3)], matrix: [InternalComplex64 { re: 1.0, im: 0.0 }, InternalComplex64 { re: 0.0, im: 0.0 }, InternalComplex64 { re: 0.0, im: 0.0 }, InternalComplex64 { re: 1.0, im: 0.0 }], data: ArbData { json: Object({}), args: [] } }"); + } + +} diff --git a/dqcsim/src/common/types/measurement.rs b/dqcsim/src/common/types/measurement.rs index a4de10052..da86feff8 100644 --- a/dqcsim/src/common/types/measurement.rs +++ b/dqcsim/src/common/types/measurement.rs @@ -102,3 +102,58 @@ impl QubitMeasurementResult { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display() { + let m = QubitMeasurementValue::Undefined; + assert_eq!(m.to_string(), "?"); + let m = QubitMeasurementValue::Zero; + assert_eq!(m.to_string(), "0"); + let m = QubitMeasurementValue::One; + assert_eq!(m.to_string(), "1"); + } + + #[test] + fn is_funcs() { + let m = QubitMeasurementValue::Undefined; + assert!(m.is_undefined()); + let m = QubitMeasurementValue::Zero; + assert!(m.is_zero()); + let m = QubitMeasurementValue::One; + assert!(m.is_one()); + } + + #[test] + fn convert() { + let m = QubitMeasurementValue::Undefined; + let o: Option = m.into(); + assert_eq!(o, None); + let t: QubitMeasurementValue = o.into(); + assert!(t.is_undefined()); + + let m = QubitMeasurementValue::One; + let o: Option = m.into(); + assert_eq!(o, Some(true)); + let t: QubitMeasurementValue = o.into(); + assert!(t.is_one()); + + let m = QubitMeasurementValue::Zero; + let o: Option = m.into(); + assert_eq!(o, Some(false)); + let t: QubitMeasurementValue = o.into(); + assert!(t.is_zero()); + + let z = false; + let m: QubitMeasurementValue = z.into(); + assert!(m.is_zero()); + + let o = true; + let m: QubitMeasurementValue = o.into(); + assert!(m.is_one()); + } + +} diff --git a/dqcsim/src/common/types/qubit_ref.rs b/dqcsim/src/common/types/qubit_ref.rs index c08d958a6..6d0aa57b0 100644 --- a/dqcsim/src/common/types/qubit_ref.rs +++ b/dqcsim/src/common/types/qubit_ref.rs @@ -83,3 +83,59 @@ impl QubitRefGenerator { // Intentionally no-op } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construct_generator() { + let mut q1 = QubitRefGenerator::default(); + let mut q2 = QubitRefGenerator::new(); + assert_eq!(q1.allocate(2), q2.allocate(2)); + } + + #[test] + fn alloc_free() { + let mut q = QubitRefGenerator::default(); + let qrefs = q.allocate(1); + assert_eq!(qrefs.len(), 1); + q.free(vec![QubitRef::from_foreign(4).unwrap()]); + let qrefs = q.allocate(1); + assert_eq!(qrefs[0], QubitRef::from_foreign(2).unwrap()); + } + + #[test] + fn convert_qrefs() { + let mut q = QubitRefGenerator::new(); + + let qr = QubitRef::from_foreign(0); + assert_eq!(qr, None); + + let qr = QubitRef::from_foreign(1).unwrap(); + assert_eq!(qr, (q.allocate(1))[0]); + + assert_eq!(42, QubitRef::from_foreign(42).unwrap().to_foreign()); + + assert_eq!(0, QubitRef::option_to_foreign(None)); + assert_eq!( + 42, + QubitRef::option_to_foreign(Some(QubitRef::from_foreign(42).unwrap())) + ); + } + + #[test] + #[should_panic] + fn convert_zero() { + let _ = QubitRef(0).to_foreign(); + } + + #[test] + fn display_qref() { + let qubits = QubitRefGenerator::default().allocate(1); + assert_eq!(qubits[0].to_string(), "1"); + let s: String = qubits[0].into(); + assert_eq!(s, "1".to_string()); + } + +} diff --git a/dqcsim/src/common/types/sequence_number.rs b/dqcsim/src/common/types/sequence_number.rs index 292c44a68..1548ae071 100644 --- a/dqcsim/src/common/types/sequence_number.rs +++ b/dqcsim/src/common/types/sequence_number.rs @@ -20,7 +20,8 @@ impl SequenceNumber { } /// Returns true if this sequence number comes after the given sequence - /// number. Every sequence number comes after `SequenceNumber::none()`. + /// number. Every sequence number comes after `SequenceNumber::none()`, + /// except for `SequenceNumber::none()` which never comes after anything. pub fn after(self, other: SequenceNumber) -> bool { self.0 > other.0 } @@ -76,3 +77,62 @@ impl SequenceNumberGenerator { self.previous } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn construct_generator() { + let mut s1 = SequenceNumberGenerator::default(); + let mut s2 = SequenceNumberGenerator::new(); + assert_eq!(s1.get_next(), s2.get_next()); + } + + #[test] + fn gets() { + let mut s = SequenceNumberGenerator::default(); + assert_eq!(s.get_previous(), SequenceNumber(0)); + assert_eq!(s.get_previous(), SequenceNumber(0)); + assert_eq!(s.get_next(), SequenceNumber(1)); + assert_eq!(s.get_next(), SequenceNumber(2)); + assert_eq!(s.get_previous(), SequenceNumber(2)); + assert_eq!(s.get_next(), SequenceNumber(3)); + assert_eq!(s.get_previous(), SequenceNumber(3)); + } + + #[test] + fn seqs() { + let mut s = SequenceNumberGenerator::default(); + let sn = s.get_previous(); + + assert_eq!(sn.preceding(), SequenceNumber(0)); + assert!(!sn.after(SequenceNumber(0))); + assert!(!sn.after(SequenceNumber(1))); + assert!(sn.acknowledges(SequenceNumber(0))); + assert!(!sn.acknowledges(SequenceNumber(1))); + + let sn = s.get_next(); + assert_eq!(sn.preceding(), SequenceNumber(0)); + assert!(sn.after(SequenceNumber(0))); + assert!(!sn.after(SequenceNumber(1))); + assert!(sn.acknowledges(SequenceNumber(0))); + assert!(sn.acknowledges(SequenceNumber(1))); + + let sn = s.get_next(); + assert_eq!(sn.preceding(), SequenceNumber(1)); + assert!(sn.after(SequenceNumber(0))); + assert!(sn.after(SequenceNumber(1))); + assert!(!sn.after(SequenceNumber(2))); + assert!(sn.acknowledges(SequenceNumber(0))); + assert!(sn.acknowledges(SequenceNumber(1))); + assert!(sn.acknowledges(SequenceNumber(2))); + assert!(!sn.acknowledges(SequenceNumber(3))); + } + + #[test] + fn display() { + assert_eq!(SequenceNumber(123).to_string(), "123"); + } + +} diff --git a/dqcsim/src/lib.rs b/dqcsim/src/lib.rs index f450a2a9b..4b91c0df6 100755 --- a/dqcsim/src/lib.rs +++ b/dqcsim/src/lib.rs @@ -1,8 +1,6 @@ //! Delft Quantum Classical Simulator //! -#![recursion_limit = "128"] - pub mod common; pub mod host; pub mod plugin; diff --git a/dqcsim/tests/quantum.rs b/dqcsim/tests/quantum.rs new file mode 100644 index 000000000..2b41cd366 --- /dev/null +++ b/dqcsim/tests/quantum.rs @@ -0,0 +1,597 @@ +use dqcsim::{ + common::{ + log::LoglevelFilter, + types::{ + ArbCmd, ArbData, Gate, PluginMetadata, PluginType, QubitMeasurementResult, + QubitMeasurementValue, QubitRef, + }, + }, + host::{ + accelerator::Accelerator, + configuration::{ + PluginLogConfiguration, PluginThreadConfiguration, SimulatorConfiguration, + }, + simulator::Simulator, + }, + plugin::definition::PluginDefinition, +}; +use num_complex::Complex64; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +#[test] +fn backend_allocate() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let q = state.allocate(1, vec![]); + assert!(q.is_err()); + assert_eq!( + q.unwrap_err().to_string(), + "Invalid operation: allocate() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_free() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let f = state.free(vec![]); + assert!(f.is_err()); + assert_eq!( + f.unwrap_err().to_string(), + "Invalid operation: free() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_gate() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let g = + state.gate(Gate::new_measurement(vec![QubitRef::from_foreign(1u64).unwrap()]).unwrap()); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid operation: gate() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_get_measurement() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let g = state.get_measurement(QubitRef::from_foreign(1).unwrap()); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid operation: get_measurement() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_get_cycles_since_measure() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let g = state.get_cycles_since_measure(QubitRef::from_foreign(1).unwrap()); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid operation: get_cycles_since_measure() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_get_cycles_between_measures() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let g = state.get_cycles_between_measures(QubitRef::from_foreign(1).unwrap()); + assert!(g.is_err()); + assert_eq!( + g.unwrap_err().to_string(), + "Invalid operation: get_cycles_between_measures() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_advance() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let a = state.advance(3u64); + assert!(a.is_err()); + assert_eq!( + a.unwrap_err().to_string(), + "Invalid operation: advance() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_get_cycle() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let a = state.get_cycle(); + assert!(a.is_err()); + assert_eq!( + a.unwrap_err().to_string(), + "Invalid operation: get_cycle() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn backend_arb() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + let frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + backend.initialize = Box::new(|state, _| { + let a = state.arb(ArbCmd::new("a", "b", ArbData::default())); + assert!(a.is_err()); + assert_eq!( + a.unwrap_err().to_string(), + "Invalid operation: arb() is not available for backends" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn measurement_non_alloc() { + let mut frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + frontend.initialize = Box::new(|state, _| { + let m = state.get_measurement(QubitRef::from_foreign(1).unwrap()); + assert!(m.is_err()); + assert_eq!( + m.unwrap_err().to_string(), + "Invalid argument: qubit 1 is not allocated" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ), + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn measurement_not_measured() { + let mut frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + frontend.initialize = Box::new(|state, _| { + let q = state.allocate(1, vec![]).expect("alloc fail"); + let m = state.get_measurement(q[0]); + assert!(m.is_err()); + assert_eq!( + m.unwrap_err().to_string(), + "Invalid argument: qubit 1 has not been measured yet" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ), + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +fn free_non_alloc() { + let mut frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + frontend.initialize = Box::new(|state, _| { + let m = state.free(vec![QubitRef::from_foreign(1).unwrap()]); + assert!(m.is_err()); + assert_eq!( + m.unwrap_err().to_string(), + "Invalid argument: qubit 1 is not allocated" + ); + Ok(()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + .without_logging() + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ), + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); +} + +#[test] +// This attempts to simulate the quantum specific methods. +fn quantum_minimal() { + let mut backend = PluginDefinition::new( + PluginType::Backend, + PluginMetadata::new("backend", "dqcsim", "0.1.0"), + ); + + #[derive(Debug)] + struct Backend { + pub qubits: HashMap, + } + + let backend_data = Arc::new(Mutex::new(Backend { + qubits: HashMap::new(), + })); + + let bd_allocate = Arc::clone(&backend_data); + backend.allocate = Box::new(move |state, qubits, _| { + dqcsim::debug!("Allocating {} qubits", qubits.len()); + let mut data = bd_allocate.lock().unwrap(); + for qubit in qubits { + data.qubits.insert( + qubit, + Complex64::new(state.random_f64(), state.random_f64()), + ); + } + Ok(()) + }); + + let bd_free = Arc::clone(&backend_data); + backend.free = Box::new(move |_, qubits| { + dqcsim::debug!("Freeing {} qubits", qubits.len()); + let mut data = bd_free.lock().unwrap(); + for qubit in qubits { + data.qubits.remove(&qubit); + } + Ok(()) + }); + + // let bd_gate = Arc::clone(&backend_data); + backend.gate = Box::new(move |_, gate| { + let mut measurement = Vec::with_capacity(gate.get_measures().len()); + for q in gate.get_measures() { + measurement.push(QubitMeasurementResult::new( + q.clone(), + QubitMeasurementValue::Zero, + ArbData::default(), + )); + } + Ok(measurement) + }); + + let mut frontend = PluginDefinition::new( + PluginType::Frontend, + PluginMetadata::new("frontend", "dqcsim", "0.1.0"), + ); + + let operator = PluginDefinition::new( + PluginType::Operator, + PluginMetadata::new("operator", "dqcsim", "0.1.0"), + ); + + frontend.run = Box::new(|state, _| { + // First allocate some qubits + let qubits = state.allocate(4, vec![]); + assert!(qubits.is_ok()); + let qubits = qubits.unwrap(); + assert_eq!(qubits.len(), 4); + assert_eq!( + format!("{:?}", qubits), + "[QubitRef(1), QubitRef(2), QubitRef(3), QubitRef(4)]" + ); + + let bad_measure = Gate::new_measurement(vec![ + QubitRef::from_foreign(2).unwrap(), + QubitRef::from_foreign(2).unwrap(), + ]); + assert!(bad_measure.is_err()); + assert_eq!( + bad_measure.unwrap_err().to_string(), + "Invalid argument: qubit 2 is measured more than once" + ); + + let _measure = state + .gate(Gate::new_measurement(vec![QubitRef::from_foreign(3).unwrap()]).unwrap()) + .unwrap(); + + let result = state + .get_measurement(QubitRef::from_foreign(3).unwrap()) + .unwrap(); + assert_eq!(result.qubit, QubitRef::from_foreign(3).unwrap()); + + Ok(ArbData::default()) + }); + + let configuration = SimulatorConfiguration::default() + .without_reproduction() + // .without_logging() + .with_stderr_level(LoglevelFilter::Trace) + .with_plugin(PluginThreadConfiguration::new( + frontend, + PluginLogConfiguration::new("front", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + operator, + PluginLogConfiguration::new("operator", LoglevelFilter::Trace), + )) + .with_plugin(PluginThreadConfiguration::new( + backend, + PluginLogConfiguration::new("backend", LoglevelFilter::Trace), + )); + + let simulator = Simulator::new(configuration); + assert!(simulator.is_ok()); + let mut simulator = simulator.unwrap(); + assert!(simulator.simulation.start(ArbData::default()).is_ok()); + let res = simulator.simulation.wait(); + + assert!(res.is_ok()); + backend_data.as_ref(); +}