use deq_runtime::bin::{self, instruction};
use deq_runtime::coordinator::coordinator_server::Coordinator;
use deq_runtime::coordinator::monolithic_coordinator::MonolithicCoordinator;
use deq_runtime::decoder::{BlackBoxDecoderClient, MockDecoder};
use deq_runtime::util::{BitMatrix, BitVector};
use std::sync::Arc;
use tonic::Request;
fn make_mock_decoder() -> Arc<MockDecoder> {
Arc::new(MockDecoder::new())
}
fn make_decoder_client(mock: Arc<MockDecoder>) -> BlackBoxDecoderClient {
BlackBoxDecoderClient::from_mock(mock)
}
fn make_coordinator(mock: Arc<MockDecoder>) -> MonolithicCoordinator {
let config = serde_json::json!({
"persistent_decoder": false,
"merge_hyperedges": false
});
MonolithicCoordinator::new(config, make_decoder_client(mock))
}
fn make_gadget(gid: u64, gtype: u64, connectors: Vec<(u64, u64)>) -> bin::Gadget {
bin::Gadget {
gid,
gtype,
connectors: connectors
.into_iter()
.map(|(gid, port)| bin::gadget::Connector { gid, port })
.collect(),
..Default::default()
}
}
fn make_check_model(cid: u64, ctype: u64, gid: u64) -> bin::CheckModel {
bin::CheckModel {
cid,
ctype,
gid,
..Default::default()
}
}
fn make_error_model(eid: u64, etype: u64, cid: u64) -> bin::ErrorModel {
bin::ErrorModel {
eid,
etype,
cid,
..Default::default()
}
}
fn make_canonical_library() -> bin::Library {
let port_type = bin::PortType {
ptype: 1,
observables: vec![], ..Default::default()
};
let gadget_type = bin::GadgetType {
gtype: 1,
inputs: vec![],
outputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
measurements: vec![bin::gadget_type::Measurement::default()],
readouts: vec![bin::gadget_type::Readout {
measurement_indices: vec![0],
..Default::default()
}],
correction_propagation: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
readout_propagation: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
logical_correction: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
..Default::default()
};
let check_model_type = bin::CheckModelType {
ctype: 1,
gtype: 1,
checks: vec![bin::check_model_type::Check {
measurements: vec![bin::check_model_type::RemoteMeasurement {
measurement_index: 0,
remote_gadget: None,
}],
naturally_flipped: false,
..Default::default()
}],
remote_gadgets: vec![],
..Default::default()
};
let error_model_type = bin::ErrorModelType {
etype: 1,
ctype: 1,
errors: vec![bin::error_model_type::Error {
probability: 0.1,
checks: vec![bin::error_model_type::RemoteCheck {
check_index: 0,
remote_check_model: None,
}],
residual: vec![],
readout_flips: vec![],
..Default::default()
}],
remote_check_models: vec![],
..Default::default()
};
bin::Library {
port_types: vec![port_type],
gadget_types: vec![gadget_type],
check_model_types: vec![check_model_type],
error_model_types: vec![error_model_type],
..Default::default()
}
}
#[tokio::test]
async fn test_monolithic_coordinator_load_library() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget_types = coordinator.gadget_types.read().await;
assert!(gadget_types.contains_key(&1));
let check_model_types = coordinator.check_model_types.read().await;
assert!(check_model_types.contains_key(&1));
let error_model_types = coordinator.error_model_types.read().await;
assert!(error_model_types.contains_key(&1));
}
#[tokio::test]
async fn test_monolithic_coordinator_reset() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
Coordinator::reset(
&coordinator,
Request::new(deq_runtime::coordinator::ResetRequest {
reset_library: true,
reset_decoder_service: true,
..Default::default()
}),
)
.await
.unwrap();
let gadget_types = coordinator.gadget_types.read().await;
assert!(gadget_types.is_empty());
let next_gid = *coordinator.next_gid.lock().await;
assert_eq!(next_gid, 1);
let state = mock.state.read().await;
assert_eq!(state.reset_count, 1);
}
#[tokio::test]
async fn test_monolithic_coordinator_auto_assigned_gid() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget = make_gadget(0, 1, vec![]);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget)),
}),
)
.await
.unwrap();
let gid = response.into_inner().id;
assert_eq!(gid, 1);
let gadgets = coordinator.gadgets.read().await;
assert!(gadgets.contains_key(&1));
}
#[tokio::test]
async fn test_monolithic_coordinator_user_provided_gid() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget = make_gadget(100, 1, vec![]);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget)),
}),
)
.await
.unwrap();
assert_eq!(response.into_inner().id, 100);
let gadgets = coordinator.gadgets.read().await;
assert!(gadgets.contains_key(&100));
}
#[tokio::test]
async fn test_monolithic_coordinator_mixed_gid_assignment() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget1 = make_gadget(1, 1, vec![]);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget1)),
}),
)
.await
.unwrap();
assert_eq!(response.into_inner().id, 1);
let gadget2 = make_gadget(0, 1, vec![]);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget2)),
}),
)
.await
.unwrap();
assert_eq!(response.into_inner().id, 2);
let gadgets = coordinator.gadgets.read().await;
assert!(gadgets.contains_key(&1));
assert!(gadgets.contains_key(&2));
}
#[tokio::test]
async fn test_monolithic_coordinator_cid_assignment() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget = make_gadget(0, 1, vec![]);
let gid_response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget)),
}),
)
.await
.unwrap();
let gid = gid_response.into_inner().id;
let check_model = make_check_model(50, 1, gid);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(check_model)),
}),
)
.await
.unwrap();
assert_eq!(response.into_inner().id, 50);
let check_models = coordinator.check_models.read().await;
assert!(check_models.contains_key(&50));
}
#[tokio::test]
async fn test_monolithic_coordinator_eid_assignment() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_canonical_library()))
.await
.unwrap();
let gadget = make_gadget(0, 1, vec![]);
let gid = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(gadget)),
}),
)
.await
.unwrap()
.into_inner()
.id;
let check_model = make_check_model(0, 1, gid);
let cid = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(check_model)),
}),
)
.await
.unwrap()
.into_inner()
.id;
let error_model = make_error_model(99, 1, cid);
let response = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(error_model)),
}),
)
.await
.unwrap();
assert_eq!(response.into_inner().id, 99);
let error_models = coordinator.error_models.read().await;
assert!(error_models.contains_key(&99));
}
fn make_default_library() -> bin::Library {
let port_type_rep = bin::PortType {
ptype: 1,
name: "rep-code".to_string(),
observables: vec![bin::port_type::Observable {
tag: "logical_z".to_string(),
..Default::default()
}],
..Default::default()
};
let port_type_surface = bin::PortType {
ptype: 2,
name: "surface-code".to_string(),
observables: vec![
bin::port_type::Observable {
tag: "logical_x".to_string(),
..Default::default()
},
bin::port_type::Observable {
tag: "logical_z".to_string(),
..Default::default()
},
],
..Default::default()
};
let gadget_type_initialize = bin::GadgetType {
gtype: 1,
name: "initialize".to_string(),
inputs: vec![],
outputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
measurements: vec![
bin::gadget_type::Measurement {
tag: "m1".to_string(),
..Default::default()
},
bin::gadget_type::Measurement {
tag: "m2".to_string(),
..Default::default()
},
],
correction_propagation: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
logical_correction: Some(BitMatrix {
rows: 1,
cols: 0,
..Default::default()
}),
readout_propagation: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 1,
cols: 2,
..Default::default()
}),
..Default::default()
};
let gadget_type_cnot = bin::GadgetType {
gtype: 2,
name: "cnot".to_string(),
inputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
outputs: vec![bin::gadget_type::Port {
ptype: 2,
..Default::default()
}],
measurements: vec![
bin::gadget_type::Measurement {
tag: "m3".to_string(),
..Default::default()
},
bin::gadget_type::Measurement {
tag: "m4".to_string(),
..Default::default()
},
],
correction_propagation: Some(BitMatrix {
rows: 2,
cols: 2,
i: vec![0, 0],
j: vec![0, 1],
}),
logical_correction: Some(BitMatrix {
rows: 2,
cols: 0,
..Default::default()
}),
readout_propagation: Some(BitMatrix {
rows: 0,
cols: 2,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 2,
cols: 2,
..Default::default()
}),
..Default::default()
};
let gadget_type_measure = bin::GadgetType {
gtype: 3,
name: "measure".to_string(),
inputs: vec![bin::gadget_type::Port {
ptype: 2,
..Default::default()
}],
outputs: vec![],
measurements: vec![
bin::gadget_type::Measurement {
tag: "m5".to_string(),
..Default::default()
},
bin::gadget_type::Measurement {
tag: "m6".to_string(),
..Default::default()
},
bin::gadget_type::Measurement {
tag: "m7".to_string(),
..Default::default()
},
],
readouts: vec![bin::gadget_type::Readout {
tag: "r1".to_string(),
measurement_indices: vec![0, 2],
..Default::default()
}],
readout_propagation: Some(BitMatrix {
rows: 1,
cols: 3,
i: vec![0, 0],
j: vec![0, 2],
}),
correction_propagation: Some(BitMatrix {
rows: 0,
cols: 3,
..Default::default()
}),
logical_correction: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 0,
cols: 3,
..Default::default()
}),
..Default::default()
};
let check_model_type_1 = bin::CheckModelType {
ctype: 1,
gtype: 2,
remote_gadgets: vec![
bin::check_model_type::RemoteGadget {
port: Some(bin::check_model_type::remote_gadget::Port::Input(0)),
expecting_gtype: 1,
measurement_bias: 1,
..Default::default()
},
bin::check_model_type::RemoteGadget {
port: Some(bin::check_model_type::remote_gadget::Port::Output(0)),
previous_remote_gadget: Some(0),
expecting_gtype: 2,
..Default::default()
},
bin::check_model_type::RemoteGadget {
port: Some(bin::check_model_type::remote_gadget::Port::Output(0)),
previous_remote_gadget: None,
expecting_gtype: 3,
..Default::default()
},
],
checks: vec![
bin::check_model_type::Check {
tag: "c1".to_string(),
measurements: vec![
bin::check_model_type::RemoteMeasurement {
measurement_index: 1,
remote_gadget: None,
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 0,
remote_gadget: Some(0),
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 0,
remote_gadget: Some(1),
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 1,
remote_gadget: Some(2),
},
],
..Default::default()
},
bin::check_model_type::Check {
tag: "c2".to_string(),
..Default::default()
},
bin::check_model_type::Check {
tag: "c3".to_string(),
..Default::default()
},
],
..Default::default()
};
let check_model_type_2 = bin::CheckModelType {
ctype: 2,
gtype: 3,
remote_gadgets: vec![bin::check_model_type::RemoteGadget {
port: Some(bin::check_model_type::remote_gadget::Port::Input(0)),
expecting_gtype: 2,
..Default::default()
}],
checks: vec![
bin::check_model_type::Check {
tag: "c4".to_string(),
measurements: vec![
bin::check_model_type::RemoteMeasurement {
measurement_index: 0,
remote_gadget: None,
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 1,
remote_gadget: None,
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 2,
remote_gadget: None,
},
bin::check_model_type::RemoteMeasurement {
measurement_index: 0,
remote_gadget: Some(0),
},
],
..Default::default()
},
bin::check_model_type::Check {
tag: "c5".to_string(),
..Default::default()
},
bin::check_model_type::Check {
tag: "c6".to_string(),
..Default::default()
},
],
..Default::default()
};
let error_model_type_1 = bin::ErrorModelType {
etype: 1,
ctype: 1,
remote_check_models: vec![
bin::error_model_type::RemoteCheckModel {
port: Some(bin::error_model_type::remote_check_model::Port::Output(0)),
expecting_ctype: 2,
..Default::default()
},
bin::error_model_type::RemoteCheckModel {
port: Some(bin::error_model_type::remote_check_model::Port::Input(0)),
previous_remote_check_model: Some(0),
expecting_ctype: 1,
..Default::default()
},
bin::error_model_type::RemoteCheckModel {
port: Some(bin::error_model_type::remote_check_model::Port::Output(0)),
expecting_ctype: 2,
..Default::default()
},
],
errors: vec![bin::error_model_type::Error {
probability: 0.1,
residual: vec![0],
checks: vec![
bin::error_model_type::RemoteCheck {
check_index: 0,
remote_check_model: None,
},
bin::error_model_type::RemoteCheck {
check_index: 1,
remote_check_model: Some(1),
},
bin::error_model_type::RemoteCheck {
check_index: 1,
remote_check_model: Some(2),
},
],
..Default::default()
}],
..Default::default()
};
let error_model_type_2 = bin::ErrorModelType {
etype: 2,
ctype: 2,
remote_check_models: vec![bin::error_model_type::RemoteCheckModel {
port: Some(bin::error_model_type::remote_check_model::Port::Input(0)),
expecting_ctype: 1,
..Default::default()
}],
errors: vec![bin::error_model_type::Error {
probability: 0.1,
readout_flips: vec![0],
..Default::default()
}],
..Default::default()
};
bin::Library {
port_types: vec![port_type_rep, port_type_surface],
gadget_types: vec![gadget_type_initialize, gadget_type_cnot, gadget_type_measure],
check_model_types: vec![check_model_type_1, check_model_type_2],
error_model_types: vec![error_model_type_1, error_model_type_2],
..Default::default()
}
}
#[tokio::test]
async fn test_hypergraph_construction_with_default_library() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_default_library()))
.await
.unwrap();
let gid1 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 1,
tag: "initialize".to_string(),
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(gid1, 1);
let gid2 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 2,
tag: "idle".to_string(),
connectors: vec![bin::gadget::Connector { gid: 1, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(gid2, 2);
let cid1 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 1,
gid: 2,
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(cid1, 1);
let eid1 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 1,
cid: 1,
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(eid1, 1);
let gid3 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 3,
tag: "measure".to_string(),
connectors: vec![bin::gadget::Connector { gid: 2, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(gid3, 3);
let cid2 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 2,
gid: 3,
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(cid2, 2);
let eid2 = Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 2,
cid: 2,
..Default::default()
})),
}),
)
.await
.unwrap()
.into_inner()
.id;
assert_eq!(eid2, 2);
let gadgets = coordinator.gadgets.read().await;
assert_eq!(gadgets.len(), 3, "Expected 3 gadgets");
assert!(gadgets.contains_key(&1), "gid=1 should exist");
assert!(gadgets.contains_key(&2), "gid=2 should exist");
assert!(gadgets.contains_key(&3), "gid=3 should exist");
assert_eq!(gadgets.get(&1).unwrap().instance.gtype, 1);
assert_eq!(gadgets.get(&2).unwrap().instance.gtype, 2);
assert_eq!(gadgets.get(&3).unwrap().instance.gtype, 3);
assert!(gadgets.get(&1).unwrap().instance.connectors.is_empty());
assert_eq!(gadgets.get(&2).unwrap().instance.connectors.len(), 1);
assert_eq!(gadgets.get(&2).unwrap().instance.connectors[0].gid, 1);
assert_eq!(gadgets.get(&3).unwrap().instance.connectors.len(), 1);
assert_eq!(gadgets.get(&3).unwrap().instance.connectors[0].gid, 2);
drop(gadgets);
let check_models = coordinator.check_models.read().await;
assert_eq!(check_models.len(), 2, "Expected 2 check models");
assert!(check_models.contains_key(&1), "cid=1 should exist");
assert!(check_models.contains_key(&2), "cid=2 should exist");
assert_eq!(check_models.get(&1).unwrap().instance.gid, 2);
assert_eq!(check_models.get(&1).unwrap().instance.ctype, 1);
assert_eq!(check_models.get(&2).unwrap().instance.gid, 3);
assert_eq!(check_models.get(&2).unwrap().instance.ctype, 2);
drop(check_models);
let error_models = coordinator.error_models.read().await;
assert_eq!(error_models.len(), 2, "Expected 2 error models");
assert!(error_models.contains_key(&1), "eid=1 should exist");
assert!(error_models.contains_key(&2), "eid=2 should exist");
assert_eq!(error_models.get(&1).unwrap().instance.cid, 1);
assert_eq!(error_models.get(&1).unwrap().instance.etype, 1);
assert_eq!(error_models.get(&2).unwrap().instance.cid, 2);
assert_eq!(error_models.get(&2).unwrap().instance.etype, 2);
drop(error_models);
let check_model_types = coordinator.check_model_types.read().await;
let cmt1 = check_model_types.get(&1).unwrap();
let cmt2 = check_model_types.get(&2).unwrap();
assert_eq!(cmt1.checks.len(), 3, "ctype=1 should have 3 checks");
assert_eq!(cmt2.checks.len(), 3, "ctype=2 should have 3 checks");
drop(check_model_types);
let error_model_types = coordinator.error_model_types.read().await;
let emt1 = error_model_types.get(&1).unwrap();
let emt2 = error_model_types.get(&2).unwrap();
assert_eq!(emt1.errors.len(), 1, "etype=1 should have 1 error");
assert_eq!(emt2.errors.len(), 1, "etype=2 should have 1 error");
assert!(
(emt1.errors[0].probability - 0.1).abs() < 1e-9,
"etype=1 error probability should be 0.1"
);
assert!(
(emt2.errors[0].probability - 0.1).abs() < 1e-9,
"etype=2 error probability should be 0.1"
);
assert_eq!(emt1.errors[0].checks.len(), 3, "etype=1 error should reference 3 checks");
assert_eq!(
emt2.errors[0].readout_flips.len(),
1,
"etype=2 error should have 1 readout flip"
);
}
#[tokio::test]
async fn test_decode_triggers_hypergraph_construction() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_default_library()))
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 2,
connectors: vec![bin::gadget::Connector { gid: 1, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 1,
gid: 2,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 1,
cid: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 3,
connectors: vec![bin::gadget::Connector { gid: 2, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 2,
gid: 3,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 2,
cid: 2,
..Default::default()
})),
}),
)
.await
.unwrap();
let coordinator_clone1 = &coordinator;
let coordinator_clone2 = &coordinator;
let coordinator_clone3 = &coordinator;
let (result1, result2, result3) = tokio::join!(
async {
Coordinator::decode(
coordinator_clone1,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 1,
outcomes: Some(BitVector { data: vec![0], size: 2 }),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
coordinator_clone2,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 2,
outcomes: Some(BitVector { data: vec![0], size: 2 }),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
coordinator_clone3,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 3,
outcomes: Some(BitVector { data: vec![0], size: 3 }),
..Default::default()
}),
)
.await
}
);
result1.unwrap();
result2.unwrap();
result3.unwrap();
let state = mock.state.read().await;
assert!(
!state.decode_calls.is_empty() || !state.loaded_hypergraphs.is_empty(),
"Expected either decode_calls or loaded_hypergraphs"
);
if !state.decode_calls.is_empty() {
let hypergraph = &state.decode_calls[0].hypergraph;
assert_eq!(hypergraph.vertex_num, 6, "Expected 6 vertices (checks) in the hypergraph");
assert_eq!(
hypergraph.hyperedges.len(),
1,
"Expected 1 hyperedge (error with checks) in the hypergraph"
);
let first_error = &hypergraph.hyperedges[0];
assert!(
(first_error.probability - 0.1).abs() < 1e-9,
"Expected probability 0.1, got {}",
first_error.probability
);
assert_eq!(first_error.vertices.len(), 3, "First error should touch 3 checks");
assert!(first_error.vertices.contains(&0), "Error should touch check 0");
assert!(first_error.vertices.contains(&1), "Error should touch check 1");
assert!(first_error.vertices.contains(&4), "Error should touch check 4");
}
}
#[tokio::test]
async fn test_remote_conditional_correction_affects_residual() {
let mock = make_mock_decoder();
let coordinator = make_coordinator(mock.clone());
let port_type = bin::PortType {
ptype: 1,
observables: vec![bin::port_type::Observable {
tag: "Z".to_string(),
..Default::default()
}],
..Default::default()
};
let gadget_type_1 = bin::GadgetType {
gtype: 1,
inputs: vec![],
outputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
measurements: vec![bin::gadget_type::Measurement::default()],
readouts: vec![bin::gadget_type::Readout {
measurement_indices: vec![0],
..Default::default()
}],
correction_propagation: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
readout_propagation: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
logical_correction: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
..Default::default()
};
let gadget_type_2 = bin::GadgetType {
gtype: 2,
inputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
outputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
measurements: vec![bin::gadget_type::Measurement::default()],
readouts: vec![bin::gadget_type::Readout {
measurement_indices: vec![0],
..Default::default()
}],
correction_propagation: Some(BitMatrix {
rows: 1,
cols: 2,
i: vec![0],
j: vec![0],
}),
readout_propagation: Some(BitMatrix {
rows: 1,
cols: 2,
..Default::default()
}),
logical_correction: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 1,
cols: 1,
..Default::default()
}),
..Default::default()
};
let gadget_type_3 = bin::GadgetType {
gtype: 3,
inputs: vec![bin::gadget_type::Port {
ptype: 1,
..Default::default()
}],
outputs: vec![],
measurements: vec![bin::gadget_type::Measurement::default()],
readouts: vec![bin::gadget_type::Readout {
measurement_indices: vec![0],
..Default::default()
}],
correction_propagation: Some(BitMatrix {
rows: 0,
cols: 2,
..Default::default()
}),
readout_propagation: Some(BitMatrix {
rows: 1,
cols: 2,
i: vec![0],
j: vec![0],
}),
logical_correction: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
physical_correction: Some(BitMatrix {
rows: 0,
cols: 1,
..Default::default()
}),
..Default::default()
};
let check_model_type = bin::CheckModelType {
ctype: 1,
gtype: 0, checks: vec![],
..Default::default()
};
let error_model_type = bin::ErrorModelType {
etype: 1,
ctype: 0, errors: vec![],
..Default::default()
};
let library = bin::Library {
port_types: vec![port_type],
gadget_types: vec![gadget_type_1, gadget_type_2, gadget_type_3],
check_model_types: vec![check_model_type],
error_model_types: vec![error_model_type],
..Default::default()
};
Coordinator::load_library(&coordinator, Request::new(library)).await.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 1,
gtype: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 1,
ctype: 1,
gid: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 1,
etype: 1,
cid: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 2,
gtype: 2,
connectors: vec![bin::gadget::Connector { gid: 1, port: 0 }],
modifier: Some(bin::GadgetModifier {
remote_conditional_correction: Some(bin::RemoteConditionalCorrection {
remote_readouts: vec![bin::remote_conditional_correction::RemoteReadout {
gid: 1,
readout_index: 0,
}],
correction: Some(BitMatrix {
rows: 1,
cols: 1,
i: vec![0],
j: vec![0],
}),
}),
..Default::default()
}),
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 2,
ctype: 1,
gid: 2,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 2,
etype: 1,
cid: 2,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 3,
gtype: 3,
connectors: vec![bin::gadget::Connector { gid: 2, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 3,
ctype: 1,
gid: 3,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
&coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 3,
etype: 1,
cid: 3,
..Default::default()
})),
}),
)
.await
.unwrap();
let (result1, result2, result3) = tokio::join!(
async {
Coordinator::decode(
&coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 1,
outcomes: Some(BitVector {
data: vec![0x80], size: 1,
}),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
&coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 2,
outcomes: Some(BitVector {
data: vec![0x00], size: 1,
}),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
&coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 3,
outcomes: Some(BitVector {
data: vec![0x00], size: 1,
}),
..Default::default()
}),
)
.await
}
);
let readouts1 = result1.unwrap().into_inner().readouts.unwrap();
let readouts2 = result2.unwrap().into_inner().readouts.unwrap();
let readouts3 = result3.unwrap().into_inner().readouts.unwrap();
assert_eq!(readouts1.size, 1, "Gadget 1 should have 1 readout");
assert_eq!(readouts2.size, 1, "Gadget 2 should have 1 readout");
assert_eq!(readouts3.size, 1, "Gadget 3 should have 1 readout");
assert_eq!(
readouts1.data[0] & 0x80,
0x80,
"Gadget 1 readout should be 1 (from measurement)"
);
assert_eq!(
readouts2.data[0] & 0x80,
0,
"Gadget 2 readout should be 0 (measurement=0, no readout_propagation)"
);
assert_eq!(
readouts3.data[0] & 0x80,
0x80,
"Gadget 3 readout should be 1 (0 XOR input_observable from gadget 2's residual)"
);
}
fn make_persistent_coordinator(mock: Arc<MockDecoder>) -> MonolithicCoordinator {
let config = serde_json::json!({
"persistent_decoder": true,
"merge_hyperedges": false
});
MonolithicCoordinator::new(config, make_decoder_client(mock))
}
async fn run_canonical_shot(
coordinator: &MonolithicCoordinator,
modifier_for_etype_1: Option<bin::ProbabilityModifier>,
modifier_for_etype_2: Option<bin::ProbabilityModifier>,
) {
let wrap_modifier = |pm: Option<bin::ProbabilityModifier>| {
pm.map(|p| bin::error_model::ErrorModelModifier {
probability_modifier: Some(p),
reroute_remote_check_models: vec![],
})
};
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 1,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 2,
connectors: vec![bin::gadget::Connector { gid: 1, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 1,
gid: 2,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 1,
cid: 1,
modifier: wrap_modifier(modifier_for_etype_1),
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::Gadget(bin::Gadget {
gid: 0,
gtype: 3,
connectors: vec![bin::gadget::Connector { gid: 2, port: 0 }],
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::CheckModel(bin::CheckModel {
cid: 0,
ctype: 2,
gid: 3,
..Default::default()
})),
}),
)
.await
.unwrap();
Coordinator::execute(
coordinator,
Request::new(bin::Instruction {
create: Some(instruction::Create::ErrorModel(bin::ErrorModel {
eid: 0,
etype: 2,
cid: 2,
modifier: wrap_modifier(modifier_for_etype_2),
..Default::default()
})),
}),
)
.await
.unwrap();
let (r1, r2, r3) = tokio::join!(
async {
Coordinator::decode(
coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 1,
outcomes: Some(BitVector { data: vec![0], size: 2 }),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 2,
outcomes: Some(BitVector { data: vec![0], size: 2 }),
..Default::default()
}),
)
.await
},
async {
Coordinator::decode(
coordinator,
Request::new(deq_runtime::coordinator::Outcomes {
gid: 3,
outcomes: Some(BitVector { data: vec![0], size: 3 }),
..Default::default()
}),
)
.await
}
);
r1.unwrap();
r2.unwrap();
r3.unwrap();
}
async fn reset_keeping_library_and_decoder(coordinator: &MonolithicCoordinator) {
Coordinator::reset(
coordinator,
Request::new(deq_runtime::coordinator::ResetRequest {
reset_library: false,
reset_decoder_service: false,
..Default::default()
}),
)
.await
.unwrap();
}
#[tokio::test]
async fn test_persistent_decoder_distinguishes_probability_modifier_across_shots() {
let mock = make_mock_decoder();
let coordinator = make_persistent_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_default_library()))
.await
.unwrap();
run_canonical_shot(
&coordinator,
Some(bin::ProbabilityModifier {
probabilities: vec![0.1],
..Default::default()
}),
None,
)
.await;
reset_keeping_library_and_decoder(&coordinator).await;
run_canonical_shot(
&coordinator,
Some(bin::ProbabilityModifier {
probabilities: vec![0.2],
..Default::default()
}),
None,
)
.await;
let mock_state = mock.state.read().await;
assert_eq!(
mock_state.loaded_hypergraphs.len(),
2,
"Expected 2 distinct loaded hypergraphs (one per modifier), got {}; old cache key \
would reuse the first one and report 1",
mock_state.loaded_hypergraphs.len(),
);
let loaded_decoders = coordinator.loaded_decoders.read().await;
assert_eq!(
loaded_decoders.len(),
2,
"Expected 2 distinct DecoderCacheKey entries (one per modifier), got {}",
loaded_decoders.len(),
);
}
#[tokio::test]
async fn test_persistent_decoder_reuses_cache_when_modifier_unchanged() {
let mock = make_mock_decoder();
let coordinator = make_persistent_coordinator(mock.clone());
Coordinator::load_library(&coordinator, Request::new(make_default_library()))
.await
.unwrap();
let modifier = bin::ProbabilityModifier {
probabilities: vec![0.1],
..Default::default()
};
run_canonical_shot(&coordinator, Some(modifier.clone()), None).await;
reset_keeping_library_and_decoder(&coordinator).await;
run_canonical_shot(&coordinator, Some(modifier), None).await;
let mock_state = mock.state.read().await;
assert_eq!(
mock_state.loaded_hypergraphs.len(),
1,
"Expected 1 cached hypergraph reused across both shots, got {}",
mock_state.loaded_hypergraphs.len(),
);
assert!(
!mock_state.decode_loaded_calls.is_empty(),
"Expected the second shot to hit the cache and call decode_loaded",
);
let loaded_decoders = coordinator.loaded_decoders.read().await;
assert_eq!(loaded_decoders.len(), 1, "Expected a single cache entry");
}