qec-code 0.3.0

Rust primitives for constructing and analyzing quantum error-correcting codes
Documentation
#![cfg(feature = "cli")]

use clap::Parser;
use qec_code::QecError;
use qec_code::cli::Cli;
use qec_code::cli::run;
use qec_code::codes::random_two_block::{
    RANDOM_TWO_BLOCK_ALGORITHM_V1, RandomTwoBlockSpec, random_two_block_css_checks,
};
use qec_code::css::{CssCode, SparseRowsMatrix};
use qec_code::distance::compute_distance;
use qec_code::family_contract::{
    CssFamilySpec, RequestedFamilyId, construct_css, parse_css_construction_json,
    verify_css_orthogonality,
};
use qec_code::finite_group::{FiniteGroupSpec, MAX_FINITE_GROUP_ORDER};
use tempfile::tempdir;

fn s3_table() -> Vec<Vec<usize>> {
    vec![
        vec![0, 1, 2, 3, 4, 5],
        vec![1, 2, 0, 4, 5, 3],
        vec![2, 0, 1, 5, 3, 4],
        vec![3, 5, 4, 0, 2, 1],
        vec![4, 3, 5, 1, 0, 2],
        vec![5, 4, 3, 2, 1, 0],
    ]
}

fn s3_group() -> FiniteGroupSpec {
    FiniteGroupSpec::new(6, 0, s3_table()).unwrap()
}

fn s3_spec() -> RandomTwoBlockSpec {
    RandomTwoBlockSpec::new(s3_group(), 2, 2, 7, RANDOM_TWO_BLOCK_ALGORITHM_V1).unwrap()
}

fn s3_request_json(include_seed: bool) -> String {
    let seed_field = if include_seed { r#","seed":7"# } else { "" };
    format!(
        r#"{{"schema_version":1,"construction":"random_two_block","group":{{"name":"S3","element_order":"0=e,1=r,2=r^2,3=s,4=rs,5=r^2s","order":6,"identity":0,"multiplication_table":{}}},"support_a_weight":2,"support_b_weight":2{seed_field},"algorithm_version":1}}"#,
        serde_json::to_string(&s3_table()).unwrap()
    )
}

fn run_qec_code_in_process(args: &[&str]) -> Result<String, QecError> {
    let mut argv = vec!["qec-code"];
    argv.extend(args);
    run(Cli::parse_from(argv))
}

fn expected_hx() -> Vec<Vec<usize>> {
    vec![
        vec![3, 5, 6, 10],
        vec![4, 5, 7, 11],
        vec![3, 4, 8, 9],
        vec![0, 2, 8, 9],
        vec![1, 2, 6, 10],
        vec![0, 1, 7, 11],
    ]
}

fn expected_hz() -> Vec<Vec<usize>> {
    vec![
        vec![0, 4, 9, 11],
        vec![1, 5, 10, 11],
        vec![2, 3, 9, 10],
        vec![2, 3, 6, 8],
        vec![0, 4, 7, 8],
        vec![1, 5, 6, 7],
    ]
}

fn css_code_from_sparse(n: usize, h_x: &[Vec<usize>], h_z: &[Vec<usize>]) -> CssCode {
    let hx = SparseRowsMatrix::new(n, h_x.to_vec())
        .unwrap()
        .to_dense_rows();
    let hz = SparseRowsMatrix::new(n, h_z.to_vec())
        .unwrap()
        .to_dense_rows();
    CssCode::from_hx_hz(hx, hz).unwrap()
}

#[test]
fn random_two_block_s3_seed7_matches_fixture() {
    let checks = random_two_block_css_checks(&s3_spec()).unwrap();

    assert_eq!(checks.num_cols, 12);
    assert_eq!(checks.support_a, vec![3, 5]);
    assert_eq!(checks.support_b, vec![0, 4]);
    assert_eq!(checks.h_x, expected_hx());
    assert_eq!(checks.h_z, expected_hz());
    assert_eq!(checks.metadata.seed, 7);
    assert_eq!(checks.metadata.support_a_weight, 2);
    assert_eq!(checks.metadata.support_b_weight, 2);
    assert_eq!(
        checks.metadata.algorithm_version,
        RANDOM_TWO_BLOCK_ALGORITHM_V1
    );
    assert!(checks.metadata.group_digest.starts_with("sha256:"));
    assert_eq!(checks.metadata.group_digest.len(), "sha256:".len() + 64);

    verify_css_orthogonality(checks.num_cols, &checks.h_x, &checks.h_z).unwrap();
    let css = css_code_from_sparse(checks.num_cols, &checks.h_x, &checks.h_z);
    assert_eq!(css.code().n(), 12);
    assert_eq!(css.code().num_logical_qubits(), 2);
    let distance = compute_distance(css.code()).unwrap();
    assert_eq!(distance.distance, 2);

    let common = construct_css(CssFamilySpec::RandomTwoBlock(s3_spec()).into()).unwrap();
    assert_eq!(common.construction_id, "random_two_block");
    assert_eq!(
        common.requested_family_id,
        Some(RequestedFamilyId::RandomTwoBlock)
    );
    assert_eq!(common.stats.n, 12);
    assert_eq!(common.stats.rank_x, 5);
    assert_eq!(common.stats.rank_z, 5);
    assert_eq!(common.stats.k, 2);
    assert_eq!(common.checks.h_x, expected_hx());
    assert_eq!(common.checks.h_z, expected_hz());
    assert_eq!(common.normalized_parameters["seed"], serde_json::json!(7));
    assert_eq!(
        common.normalized_parameters["support_a_weight"],
        serde_json::json!(2)
    );
    assert_eq!(
        common.normalized_parameters["support_b_weight"],
        serde_json::json!(2)
    );
    assert_eq!(
        common.normalized_parameters["algorithm_version"],
        serde_json::json!(RANDOM_TWO_BLOCK_ALGORITHM_V1)
    );
    assert_eq!(
        common.normalized_parameters["support_a"],
        serde_json::json!([3, 5])
    );
    assert_eq!(
        common.normalized_parameters["support_b"],
        serde_json::json!([0, 4])
    );
    assert_eq!(
        common.normalized_parameters["group_digest"],
        serde_json::json!(checks.metadata.group_digest)
    );

    let parsed = parse_css_construction_json(&s3_request_json(true)).unwrap();
    let parsed_common = construct_css(parsed).unwrap();
    assert_eq!(parsed_common.checks, common.checks);
    assert_eq!(
        parsed_common.normalized_parameters,
        common.normalized_parameters
    );

    let dir = tempdir().unwrap();
    let spec_path = dir.path().join("random-two-block-s3.json");
    std::fs::write(&spec_path, s3_request_json(true)).unwrap();
    let cli_hx = run_qec_code_in_process(&[
        "code",
        "css",
        "construct",
        "--spec",
        spec_path.to_str().unwrap(),
        "hx",
    ])
    .unwrap();
    let cli_hz = run_qec_code_in_process(&[
        "code",
        "css",
        "construct",
        "--spec",
        spec_path.to_str().unwrap(),
        "hz",
    ])
    .unwrap();
    assert_eq!(
        serde_json::from_str::<serde_json::Value>(&cli_hx).unwrap()["rows"],
        serde_json::json!(expected_hx())
    );
    assert_eq!(
        serde_json::from_str::<serde_json::Value>(&cli_hz).unwrap()["rows"],
        serde_json::json!(expected_hz())
    );
}

#[test]
fn random_two_block_s3_seed1_uses_swap_sampling() {
    let spec = RandomTwoBlockSpec::new(s3_group(), 2, 3, 1, RANDOM_TWO_BLOCK_ALGORITHM_V1).unwrap();
    let checks = random_two_block_css_checks(&spec).unwrap();

    assert_eq!(checks.support_a, vec![0, 5]);
    assert_eq!(checks.support_b, vec![0, 1, 3]);
}

#[test]
fn random_two_block_rejects_invalid_sampling_specs() {
    assert!(matches!(
        RandomTwoBlockSpec::new(s3_group(), 7, 2, 7, RANDOM_TWO_BLOCK_ALGORITHM_V1),
        Err(QecError::InvalidRandomTwoBlockSpec {
            option: "support_a_weight",
            ..
        })
    ));
    assert!(matches!(
        RandomTwoBlockSpec::new(s3_group(), 2, 7, 7, RANDOM_TWO_BLOCK_ALGORITHM_V1),
        Err(QecError::InvalidRandomTwoBlockSpec {
            option: "support_b_weight",
            ..
        })
    ));
    assert!(matches!(
        RandomTwoBlockSpec::new(s3_group(), 0, 2, 7, RANDOM_TWO_BLOCK_ALGORITHM_V1),
        Err(QecError::InvalidRandomTwoBlockSpec {
            option: "support_a_weight",
            ..
        })
    ));
    assert!(matches!(
        RandomTwoBlockSpec::new(s3_group(), 2, 0, 7, RANDOM_TWO_BLOCK_ALGORITHM_V1),
        Err(QecError::InvalidRandomTwoBlockSpec {
            option: "support_b_weight",
            ..
        })
    ));
    assert_eq!(
        RandomTwoBlockSpec::new(s3_group(), 2, 2, 7, 2),
        Err(QecError::UnsupportedRandomTwoBlockAlgorithm {
            algorithm_version: 2,
        })
    );
    assert_eq!(
        FiniteGroupSpec::new(MAX_FINITE_GROUP_ORDER + 1, 0, Vec::new()),
        Err(QecError::GroupOrderLimitExceeded {
            order: MAX_FINITE_GROUP_ORDER + 1,
            max_order: MAX_FINITE_GROUP_ORDER,
        })
    );
    assert!(matches!(
        FiniteGroupSpec::new(6, 0, vec![vec![0, 1, 2, 3, 4, 6]; 6]),
        Err(QecError::InvalidFiniteGroupTable { .. })
    ));
    assert!(matches!(
        parse_css_construction_json(&s3_request_json(false)),
        Err(QecError::InvalidRandomTwoBlockSpec { option: "seed", .. })
    ));
    assert!(matches!(
        parse_css_construction_json(
            r#"{"schema_version":1,"construction":"random_two_block","group":{"order":257,"identity":0,"multiplication_table":[]},"support_a_weight":1,"support_b_weight":1,"seed":7,"algorithm_version":1}"#
        ),
        Err(QecError::GroupOrderLimitExceeded { .. })
    ));
}