use crate::decoder::BlackBoxDecoderClient;
use crate::decoder::blackbox_decoder::{self, DecodingHypergraph, ParityFactor};
use crate::decoder::blackbox_util::is_parity_factor;
use crate::decoder::test_problems::{StandardTestProblem, TestCase, case_id, standard_test_problems};
use crate::util::BitVector;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Path {
Decode,
DecodeLoaded,
}
impl Path {
pub fn as_str(self) -> &'static str {
match self {
Path::Decode => "decode",
Path::DecodeLoaded => "decode_loaded",
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Outcome {
Pass,
InvalidSubgraph { returned: Vec<u64> },
RpcError(String),
}
impl Outcome {
pub fn is_pass(&self) -> bool {
matches!(self, Outcome::Pass)
}
}
#[derive(Clone, Debug)]
pub struct CaseResult {
pub problem: &'static str,
pub case: &'static str,
pub path: Path,
pub outcome: Outcome,
}
#[derive(Clone, Debug, Default)]
pub struct SuiteReport {
pub results: Vec<CaseResult>,
}
impl SuiteReport {
pub fn get(&self, problem: &str, case: &str, path: Path) -> Option<&Outcome> {
self.results
.iter()
.find(|r| r.problem == problem && r.case == case && r.path == path)
.map(|r| &r.outcome)
}
pub fn pass_count(&self) -> usize {
self.results.iter().filter(|r| r.outcome.is_pass()).count()
}
pub fn total(&self) -> usize {
self.results.len()
}
pub fn summary_lines(&self) -> Vec<String> {
self.results
.iter()
.map(|r| {
let id = case_id(r.problem, r.case);
match &r.outcome {
Outcome::Pass => format!("[PASS] {}/{}", id, r.path.as_str()),
Outcome::InvalidSubgraph { returned } => {
format!("[FAIL] {}/{} invalid subgraph: returned {:?}", id, r.path.as_str(), returned)
}
Outcome::RpcError(msg) => {
format!("[FAIL] {}/{} rpc error: {}", id, r.path.as_str(), msg)
}
}
})
.collect()
}
}
fn classify(hypergraph: &DecodingHypergraph, syndrome: &BitVector, response: &ParityFactor) -> Outcome {
if is_parity_factor(hypergraph, response, syndrome) {
Outcome::Pass
} else {
Outcome::InvalidSubgraph {
returned: response.subgraph.clone(),
}
}
}
async fn run_one_problem(client: &mut BlackBoxDecoderClient, problem: &StandardTestProblem, out: &mut Vec<CaseResult>) {
for case in &problem.cases {
out.push(run_decode_path(client, problem, case).await);
}
let load_outcome = client.load_hypergraph(problem.hypergraph.clone()).await;
let hid = match load_outcome {
Ok(response) => Some(response.hid),
Err(status) => {
let msg = format!("load_hypergraph failed: {status}");
for case in &problem.cases {
out.push(CaseResult {
problem: problem.name,
case: case.name,
path: Path::DecodeLoaded,
outcome: Outcome::RpcError(msg.clone()),
});
}
None
}
};
if let Some(hid) = hid {
for case in &problem.cases {
out.push(run_decode_loaded_path(client, problem, case, hid).await);
}
}
if let Err(status) = client
.reset(blackbox_decoder::ResetRequest {
reset_hypergraphs: true,
..Default::default()
})
.await
{
out.push(CaseResult {
problem: problem.name,
case: "reset",
path: Path::DecodeLoaded,
outcome: Outcome::RpcError(format!("reset failed: {status}")),
});
}
}
async fn run_decode_path(client: &mut BlackBoxDecoderClient, problem: &StandardTestProblem, case: &TestCase) -> CaseResult {
let problem_payload = blackbox_decoder::DecodingProblem {
hypergraph: Some(problem.hypergraph.clone()),
syndrome: Some(case.syndrome.clone()),
};
let outcome = match client.decode(problem_payload).await {
Ok(response) => classify(&problem.hypergraph, &case.syndrome, &response),
Err(status) => Outcome::RpcError(status.to_string()),
};
CaseResult {
problem: problem.name,
case: case.name,
path: Path::Decode,
outcome,
}
}
async fn run_decode_loaded_path(
client: &mut BlackBoxDecoderClient,
problem: &StandardTestProblem,
case: &TestCase,
hid: u64,
) -> CaseResult {
let problem_payload = blackbox_decoder::LoadedDecodingProblem {
hid,
syndrome: Some(case.syndrome.clone()),
};
let outcome = match client.decode_loaded(problem_payload).await {
Ok(response) => classify(&problem.hypergraph, &case.syndrome, &response),
Err(status) => Outcome::RpcError(status.to_string()),
};
CaseResult {
problem: problem.name,
case: case.name,
path: Path::DecodeLoaded,
outcome,
}
}
pub async fn run_standard_suite(client: &mut BlackBoxDecoderClient) -> SuiteReport {
let mut results: Vec<CaseResult> = Vec::new();
for problem in standard_test_problems() {
run_one_problem(client, &problem, &mut results).await;
}
SuiteReport { results }
}