use dqcsim::{ common::{ error::err, log::{thread::LogThread, LoglevelFilter}, types::{ ArbCmd, ArbData, Gate, Matrix, PluginMetadata, PluginType, QubitMeasurementResult, QubitMeasurementValue, QubitRef, }, }, host::{ accelerator::Accelerator, configuration::{ PluginLogConfiguration, PluginThreadConfiguration, Seed, SimulatorConfiguration, }, plugin::Plugin, simulation::Simulation, simulator::Simulator, }, plugin::definition::PluginDefinition, }; use num_complex::Complex64; use std::{ collections::HashMap, sync::{Arc, Mutex}, }; pub fn fe_op_be() -> (PluginDefinition, PluginDefinition, PluginDefinition) { let 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"), ); let backend = PluginDefinition::new( PluginType::Backend, PluginMetadata::new("backend", "dqcsim", "0.1.0"), ); (frontend, operator, backend) } // TODO(mb): fix this test // #[test] // // This tests bad initialization by plugin type mismatch. // fn bad_plugin_type() { // let frontend = PluginDefinition::new( // PluginType::Frontend, // PluginMetadata::new("frontend", "dqcsim", "0.1.0"), // ); // let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // d.push("../target/debug/examples/plugin"); // let not_operator = PluginProcessConfiguration::new( // "frontend", // PluginProcessSpecification::from_sugar(d, PluginType::Operator).unwrap(), // ); // let backend = PluginDefinition::new( // PluginType::Backend, // PluginMetadata::new("backend", "dqcsim", "0.1.0"), // ); // let ptc = |definition| { // PluginThreadConfiguration::new( // definition, // PluginLogConfiguration::new("", LoglevelFilter::Off), // ) // }; // let configuration = SimulatorConfiguration::default() // .without_reproduction() // .without_logging() // .with_plugin(ptc(frontend)) // .with_plugin(not_operator) // .with_plugin(ptc(backend)); // let simulator = Simulator::new(configuration); // assert!(simulator.is_err()); // assert_eq!(simulator.unwrap_err().to_string(), "Failed to initialize plugin(s): Invalid operation: host is expecting a plugin of type Operator, but we\'re a plugin of type Frontend"); // } #[test] // This tests basic plugin arb command propagation. fn plugin_to_plugin_arb() { let (mut frontend, mut operator, mut backend) = fe_op_be(); frontend.initialize = Box::new(|state, _| { let res = state.arb(ArbCmd::new("id", "op_id", ArbData::default())); assert!(res.is_ok()); let mut msg = ArbData::default(); msg.set_json("{ \"a\": \"b\" }").unwrap(); assert_eq!(res.unwrap(), msg); Ok(()) }); operator.upstream_arb = Box::new(|state, cmd| { assert_eq!(cmd, ArbCmd::new("id", "op_id", ArbData::default())); state.arb(cmd) }); backend.upstream_arb = Box::new(|_, cmd| { assert_eq!(cmd, ArbCmd::new("id", "op_id", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"a\": \"b\" }").unwrap(); Ok(msg) }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(operator)) .with_plugin(ptc(backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); } #[test] // This tests basic simulator to plugin arb command sending. fn simulator_to_plugin_arb() { let (mut frontend, mut operator, mut backend) = fe_op_be(); frontend.host_arb = Box::new(|_, cmd| { assert_eq!(cmd, ArbCmd::new("front", "1", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"front\": 1 }").unwrap(); Ok(msg) }); operator.host_arb = Box::new(|_, cmd| { assert_eq!(cmd, ArbCmd::new("operator", "2", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"operator\": 2 }").unwrap(); Ok(msg) }); backend.host_arb = Box::new(|_, cmd| { assert_eq!(cmd, ArbCmd::new("backend", "3", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"backend\": 3 }").unwrap(); Ok(msg) }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(operator)) .with_plugin(ptc(backend)); let mut simulator = Simulator::new(configuration).unwrap(); let simulation = &mut simulator.simulation; let res = simulation.arb_idx(0, ArbCmd::new("front", "1", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"front\": 1 }").unwrap(); assert!(res.is_ok()); assert_eq!(res.unwrap(), msg); let res = simulation.arb("front", ArbCmd::new("front", "1", ArbData::default())); assert!(res.is_ok()); assert_eq!(res.unwrap(), msg); let res = simulation.arb("asdf", ArbCmd::new("front", "1", ArbData::default())); assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), "Invalid argument: plugin asdf not found" ); let res = simulation.arb_idx(3, ArbCmd::new("front", "1", ArbData::default())); assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), "Invalid argument: index 3 out of range" ); let res = simulation.arb_idx(-5, ArbCmd::new("front", "1", ArbData::default())); assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), "Invalid argument: index -5 out of range" ); let res = simulation.arb_idx(1, ArbCmd::new("operator", "2", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"operator\": 2 }").unwrap(); assert!(res.is_ok()); assert_eq!(res.unwrap(), msg); let res = simulation.arb_idx(2, ArbCmd::new("backend", "3", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"backend\": 3 }").unwrap(); assert!(res.is_ok()); assert_eq!(res.unwrap(), msg); let res = simulation.arb_idx(-1, ArbCmd::new("backend", "3", ArbData::default())); let mut msg = ArbData::default(); msg.set_json("{ \"backend\": 3 }").unwrap(); assert!(res.is_ok()); assert_eq!(res.unwrap(), msg); } #[test] // This tests send/recv to accelerator via simulation api. fn send_recv_accelerator() { let (mut frontend, _, backend) = fe_op_be(); frontend.run = Box::new(|state, _| { assert!(state.send(ArbData::default()).is_ok()); let res = state.recv(); assert!(res.is_ok()); assert_eq!(res.unwrap(), ArbData::default()); Ok(ArbData::default()) }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); let mut simulator = simulator.unwrap(); assert!(simulator.simulation.send(ArbData::default()).is_ok()); // test deadlock let res = simulator.simulation.recv(); assert!(res.is_err()); assert_eq!( res.unwrap_err().to_string(), "Deadlock: recv() called while queue is empty and accelerator is idle" ); // start a program assert!(simulator.simulation.start(ArbData::default()).is_ok()); let res = simulator.simulation.recv(); assert!(res.is_ok()); } #[test] // This tests basic plugin arb command propagation. fn plugin_user_init_fail() { let (mut frontend, _, backend) = fe_op_be(); frontend.initialize = Box::new(|_, _| err("please help")); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_err()); assert_eq!(simulator.unwrap_err().to_string(), "please help"); } #[test] // This should not get stuck. fn debug_deadlock() { let (mut frontend, _, backend) = fe_op_be(); frontend.run = Box::new(|state, _| { // This recv should unblock if the simulation ends. let res = state.recv(); assert_eq!(res.unwrap_err().to_string(), "Simulation aborted"); Ok(ArbData::default()) }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(backend)); let mut simulator = Simulator::new(configuration).unwrap(); simulator.simulation.start(ArbData::default()).unwrap(); } fn thread_config_type(plugin_type: PluginType) -> PluginThreadConfiguration { PluginThreadConfiguration::new( PluginDefinition::new(plugin_type, PluginMetadata::new("", "", "")), PluginLogConfiguration::new("", LoglevelFilter::Off), ) } fn minimal_simulator() -> Simulator { let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)) .with_plugin(thread_config_type(PluginType::Frontend)); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); simulator.unwrap() } #[allow(unused)] fn verbose_simulator_configuration() -> SimulatorConfiguration { let mut configuration = SimulatorConfiguration::default().without_reproduction(); configuration.stderr_level = LoglevelFilter::Trace; configuration.dqcsim_level = LoglevelFilter::Trace; configuration } #[test] fn simulation_default_config() { let configuration = SimulatorConfiguration::default().without_reproduction(); let simulator = Simulator::new(configuration); assert!(simulator.is_err()); assert_eq!( simulator.unwrap_err().to_string(), "Invalid argument: missing frontend" ); } #[test] fn simulation_missing_backend() { let configuration = SimulatorConfiguration::default() .without_reproduction() .with_plugin(thread_config_type(PluginType::Frontend)); let simulator = Simulator::new(configuration); assert!(simulator.is_err()); assert_eq!( simulator.unwrap_err().to_string(), "Invalid argument: missing backend" ); } #[test] fn simulation_missing_frontend() { let configuration = SimulatorConfiguration::default() .without_reproduction() .with_plugin(thread_config_type(PluginType::Backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_err()); assert_eq!( simulator.unwrap_err().to_string(), "Invalid argument: missing frontend" ); } #[test] fn simulation_minimal_setup() { minimal_simulator(); } #[test] fn simulation_wait_without_start() { let simulation = &mut minimal_simulator().simulation; let wait = simulation.wait(); assert!(wait.is_err()); assert_eq!( wait.unwrap_err().to_string(), "Invalid operation: accelerator is not running; call start() first" ); } #[test] fn simulation_metadata() { let plugin_metadata = PluginMetadata::new("name", "author", "version"); let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)) .with_plugin(PluginThreadConfiguration::new( PluginDefinition::new(PluginType::Frontend, plugin_metadata.clone()), PluginLogConfiguration::new("test", LoglevelFilter::Off), )); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); let simulation = &mut simulator.unwrap().simulation; let metadata = simulation.get_metadata("test"); assert!(metadata.is_ok()); assert_eq!(&plugin_metadata, metadata.unwrap()); let metadata = simulation.get_metadata("asdf"); assert!(metadata.is_err()); assert_eq!( metadata.unwrap_err().to_string(), "Invalid argument: plugin asdf not found" ); let metadata = simulation.get_metadata_idx(0); assert!(metadata.is_ok()); assert_eq!(&plugin_metadata, metadata.unwrap()); let metadata = simulation.get_metadata_idx(2); assert!(metadata.is_err()); } #[test] fn simulation_initial_state() { let simulation = &mut minimal_simulator().simulation; assert!(simulation.yield_to_accelerator().is_ok()); } #[test] // Tests if initilize commands from a Plugin arrive in the intialize callback // of a PluginDefinition. fn simulation_init_cmds() { let mut definition = PluginDefinition::new(PluginType::Frontend, PluginMetadata::new("", "", "")); definition.initialize = Box::new(|_state, init_cmds| { assert_eq!(init_cmds.len(), 1); assert_eq!(ArbCmd::new("a", "b", ArbData::default()), init_cmds[0]); Ok(()) }); let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)) .with_plugin( PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) .with_init_cmd(ArbCmd::new("a", "b", ArbData::default())), ); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); } #[test] // Attempt recv outside of run callbacks. fn simulation_bad_recv() { let mut definition = PluginDefinition::new(PluginType::Frontend, PluginMetadata::new("", "", "")); definition.initialize = Box::new(|state, _| { let recv = state.recv(); assert!(recv.is_err()); assert_eq!( recv.unwrap_err().to_string(), "Invalid operation: recv() can only be called from inside the run() callback" ); Ok(()) }); definition.drop = Box::new(|state| { let recv = state.recv(); assert!(recv.is_err()); assert_eq!( recv.unwrap_err().to_string(), "Invalid operation: recv() can only be called from inside the run() callback" ); Ok(()) }); let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)) .with_plugin(PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), )); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); } #[test] #[allow(clippy::redundant_closure)] fn bad_simulation_pipeline_too_short() { let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)); let log_thread = LogThread::spawn( "dqcsim", configuration.dqcsim_level, configuration.stderr_level, configuration.log_callback, configuration.tee_files, ) .unwrap(); let pipeline: Vec> = configuration .plugins .into_iter() .map(|plugin| plugin.instantiate()) .collect(); let simulation = Simulation::new(pipeline, Seed::default(), None, &log_thread); assert!(simulation.is_err()); assert_eq!( simulation.unwrap_err().to_string(), "Invalid argument: Simulation must consist of at least a frontend and backend" ); } #[test] #[allow(clippy::redundant_closure)] fn bad_simulation_pipeline_fe_op_only() { let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Frontend)) .with_plugin(thread_config_type(PluginType::Operator)); let log_thread = LogThread::spawn( "dqcsim", configuration.dqcsim_level, configuration.stderr_level, configuration.log_callback, configuration.tee_files, ) .unwrap(); let pipeline: Vec> = configuration .plugins .into_iter() .map(|plugin| plugin.instantiate()) .collect(); let simulation = Simulation::new(pipeline, Seed::default(), None, &log_thread); assert!(simulation.is_err()); assert_eq!( simulation.unwrap_err().to_string(), "Interprocess communication error: disconnected" ); } #[test] #[allow(clippy::redundant_closure)] fn bad_simulation_pipeline_be_op_only() { let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(thread_config_type(PluginType::Backend)) .with_plugin(thread_config_type(PluginType::Operator)); let log_thread = LogThread::spawn( "dqcsim", configuration.dqcsim_level, configuration.stderr_level, configuration.log_callback, configuration.tee_files, ) .unwrap(); let pipeline: Vec> = configuration .plugins .into_iter() .map(|plugin| plugin.instantiate()) .collect(); let simulation = Simulation::new(pipeline, Seed::default(), None, &log_thread); assert!(simulation.is_err()); assert_eq!( simulation.unwrap_err().to_string(), "Interprocess communication error: disconnected" ); } #[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()], Matrix::new_identity(2), ) .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, 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(), ], Matrix::new_identity(2), ); assert!(bad_measure.is_err()); assert_eq!( bad_measure.unwrap_err().to_string(), "Invalid argument: qubit 2 is measured more than once" ); state .gate( Gate::new_measurement( vec![QubitRef::from_foreign(3).unwrap()], Matrix::new_identity(2), ) .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(); } #[test] // This tests whether host arbs are properly synchronized with the gatestream fn host_arb_sync_with_gatestream() { let (mut frontend, _, mut backend) = fe_op_be(); frontend.run = Box::new(|state, _| { state.allocate(1, vec![]).unwrap(); let gate = Gate::new_unitary( vec![QubitRef::from_foreign(1).unwrap()], vec![], 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(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); // To work around issue #90; //state.arb(ArbCmd::new("a", "b", ArbData::default())).unwrap(); state.recv().unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate.clone()).unwrap(); state.gate(gate).unwrap(); // To work around issue #90; //state.arb(ArbCmd::new("a", "b", ArbData::default())).unwrap(); Ok(ArbData::default()) }); let gates_executed = Arc::new(Mutex::new(Box::new(0u8))); let ge_gate = Arc::clone(&gates_executed); backend.gate = Box::new(move |_, _| { let ge: &mut u8 = &mut ge_gate.lock().unwrap(); *ge += 1; std::thread::sleep(std::time::Duration::from_millis(100)); Ok(vec![]) }); let ge_arb = Arc::clone(&gates_executed); backend.host_arb = Box::new(move |_, _| { let ge: &u8 = &ge_arb.lock().unwrap(); Ok(ArbData::from_args(vec![vec![*ge]])) }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); let mut simulator = simulator.unwrap(); simulator.simulation.start(ArbData::default()).unwrap(); simulator.simulation.yield_to_accelerator().unwrap(); // Should have processed the first batch of the gates here (5). let gates_executed = simulator .simulation .arb_idx(1, ArbCmd::new("a", "b", ArbData::default())) .unwrap() .get_args()[0][0]; println!("after yield: {} ?= 5", gates_executed); assert_eq!(gates_executed, 5); simulator.simulation.send(ArbData::default()).unwrap(); simulator.simulation.wait().unwrap(); // Should have processed all the gates here (10). let gates_executed = simulator .simulation .arb_idx(1, ArbCmd::new("a", "b", ArbData::default())) .unwrap() .get_args()[0][0]; println!("after yield: {} ?= 10", gates_executed); assert_eq!(gates_executed, 10); } #[test] fn plugin_thread_panic() { let (mut frontend, _, backend) = fe_op_be(); frontend.run = Box::new(|_, _| { panic!("boom"); }); let ptc = |definition| { PluginThreadConfiguration::new( definition, PluginLogConfiguration::new("", LoglevelFilter::Off), ) }; let configuration = SimulatorConfiguration::default() .without_reproduction() .without_logging() .with_plugin(ptc(frontend)) .with_plugin(ptc(backend)); let simulator = Simulator::new(configuration); assert!(simulator.is_ok()); let mut simulator = simulator.unwrap(); let start = simulator.simulation.start(ArbData::default()); assert!(start.is_ok()); let wait = simulator.simulation.wait(); assert!(wait.is_err()); }