qec-code 0.3.0

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

use std::ffi::OsString;
use std::path::Path;

use clap::Parser;
use qec_code::QecError;
use qec_code::cli::{Cli, run};
use qec_code::css::{CssCode, SparseRowsMatrix};
use qec_code::distance::compute_distance;
use qec_code::family_contract::{
    CssClassicalCheckSpec, CssConstructionSpec, HypergraphProductSpec, construct_css,
    parse_css_construction_json, verify_css_orthogonality,
};
use tempfile::tempdir;

fn classical_2x3() -> CssClassicalCheckSpec {
    CssClassicalCheckSpec {
        num_cols: 3,
        rows: vec![vec![0, 1], vec![1, 2]],
    }
}

fn classical_1x2() -> CssClassicalCheckSpec {
    CssClassicalCheckSpec {
        num_cols: 2,
        rows: vec![vec![0, 1]],
    }
}

fn fixture_spec() -> CssConstructionSpec {
    CssConstructionSpec::HypergraphProduct(HypergraphProductSpec {
        left: classical_2x3(),
        right: classical_2x3(),
    })
}

fn fixture_json() -> &'static str {
    r#"{"schema_version":1,"construction":"hypergraph_product","left":{"num_cols":3,"rows":[[0,1],[1,2]]},"right":{"num_cols":3,"rows":[[0,1],[1,2]]}}"#
}

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

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

fn css_code(num_cols: usize, h_x: &[Vec<usize>], h_z: &[Vec<usize>]) -> CssCode {
    CssCode::from_hx_hz(
        SparseRowsMatrix::new(num_cols, h_x.to_vec())
            .unwrap()
            .to_dense_rows(),
        SparseRowsMatrix::new(num_cols, h_z.to_vec())
            .unwrap()
            .to_dense_rows(),
    )
    .unwrap()
}

fn construct_cli_output(spec_path: &Path, output: &str) -> String {
    run(Cli::parse_from([
        OsString::from("qec-code"),
        OsString::from("code"),
        OsString::from("css"),
        OsString::from("construct"),
        OsString::from("--spec"),
        spec_path.as_os_str().to_owned(),
        OsString::from(output),
    ]))
    .unwrap()
}

#[test]
fn hypergraph_product_matches_2x3_fixture() {
    let result = construct_css(fixture_spec()).unwrap();

    assert_eq!(result.construction_id, "hypergraph_product");
    assert_eq!(result.requested_family_id, None);
    assert_eq!(
        result.normalized_parameters["left"]["num_cols"],
        serde_json::json!(3)
    );
    assert_eq!(
        result.normalized_parameters["left"]["rows"],
        serde_json::json!([[0, 1], [1, 2]])
    );
    assert_eq!(
        result.normalized_parameters["right"]["num_cols"],
        serde_json::json!(3)
    );
    assert_eq!(
        result.normalized_parameters["right"]["rows"],
        serde_json::json!([[0, 1], [1, 2]])
    );
    assert_eq!(result.stats.n, 13);
    assert_eq!(result.stats.m_x, 6);
    assert_eq!(result.stats.m_z, 6);
    assert_eq!(result.stats.rank_x, 6);
    assert_eq!(result.stats.rank_z, 6);
    assert_eq!(result.stats.k, 1);
    assert_eq!(result.stats.d_x, None);
    assert_eq!(result.stats.d_z, None);
    assert_eq!(result.checks.h_x, expected_hx());
    assert_eq!(result.checks.h_z, expected_hz());
    verify_css_orthogonality(result.stats.n, &result.checks.h_x, &result.checks.h_z).unwrap();

    let distance =
        compute_distance(css_code(result.stats.n, &result.checks.h_x, &result.checks.h_z).code())
            .unwrap();
    assert_eq!(distance.distance, 3);
    assert_eq!(distance.witness.weight(), 3);

    let parsed = parse_css_construction_json(fixture_json()).unwrap();
    let parsed_result = construct_css(parsed).unwrap();
    assert_eq!(parsed_result.checks, result.checks);
    assert_eq!(
        serde_json::to_string(&parsed_result.normalized_parameters).unwrap(),
        serde_json::to_string(&result.normalized_parameters).unwrap()
    );

    let dir = tempdir().unwrap();
    let spec_path = dir.path().join("hgp.json");
    std::fs::write(&spec_path, fixture_json()).unwrap();

    let hx_json: serde_json::Value =
        serde_json::from_str(&construct_cli_output(&spec_path, "hx")).unwrap();
    let hz_json: serde_json::Value =
        serde_json::from_str(&construct_cli_output(&spec_path, "hz")).unwrap();
    let metadata: serde_json::Value =
        serde_json::from_str(&construct_cli_output(&spec_path, "metadata")).unwrap();

    assert_eq!(hx_json["format"], "sparse_rows");
    assert_eq!(hx_json["num_cols"], 13);
    assert_eq!(hx_json["rows"], serde_json::json!(expected_hx()));
    assert_eq!(hz_json["format"], "sparse_rows");
    assert_eq!(hz_json["num_cols"], 13);
    assert_eq!(hz_json["rows"], serde_json::json!(expected_hz()));
    assert_eq!(metadata["schema_version"], 1);
    assert_eq!(metadata["construction_id"], "hypergraph_product");
    assert_eq!(metadata["requested_family_id"], serde_json::Value::Null);
    assert_eq!(
        metadata["normalized_parameters"]["left"],
        serde_json::json!({"num_cols": 3, "rows": [[0, 1], [1, 2]]})
    );
    assert_eq!(
        metadata["normalized_parameters"]["right"],
        serde_json::json!({"num_cols": 3, "rows": [[0, 1], [1, 2]]})
    );
    assert_eq!(metadata["stats"]["n"], 13);
    assert_eq!(metadata["stats"]["m_x"], 6);
    assert_eq!(metadata["stats"]["m_z"], 6);
    assert_eq!(metadata["stats"]["rank_x"], 6);
    assert_eq!(metadata["stats"]["rank_z"], 6);
    assert_eq!(metadata["stats"]["k"], 1);
    assert_eq!(metadata["checks"]["h_x"], serde_json::json!(expected_hx()));
    assert_eq!(metadata["checks"]["h_z"], serde_json::json!(expected_hz()));
    assert_eq!(metadata["provenance"]["adapter"], "hypergraph_product");
    assert_eq!(
        metadata["provenance"]["source"],
        "CssConstructionSpec::HypergraphProduct"
    );
    assert!(
        !metadata["provenance"]["normalized_input_digest"]
            .as_str()
            .unwrap()
            .is_empty()
    );
}

#[test]
fn hypergraph_product_accepts_independent_rectangular_inputs() {
    let left_small = construct_css(CssConstructionSpec::HypergraphProduct(
        HypergraphProductSpec {
            left: classical_1x2(),
            right: classical_2x3(),
        },
    ))
    .unwrap();

    assert_eq!(left_small.stats.n, 8);
    assert_eq!(left_small.stats.m_x, 3);
    assert_eq!(left_small.stats.m_z, 4);
    assert_eq!(
        left_small.checks.h_x,
        vec![vec![0, 3, 6], vec![1, 4, 6, 7], vec![2, 5, 7]]
    );
    assert_eq!(
        left_small.checks.h_z,
        vec![vec![0, 1, 6], vec![1, 2, 7], vec![3, 4, 6], vec![4, 5, 7]]
    );
    verify_css_orthogonality(
        left_small.stats.n,
        &left_small.checks.h_x,
        &left_small.checks.h_z,
    )
    .unwrap();

    let right_small = construct_css(CssConstructionSpec::HypergraphProduct(
        HypergraphProductSpec {
            left: classical_2x3(),
            right: classical_1x2(),
        },
    ))
    .unwrap();

    assert_eq!(right_small.stats.n, 8);
    assert_eq!(right_small.stats.m_x, 4);
    assert_eq!(right_small.stats.m_z, 3);
    assert_eq!(
        right_small.checks.h_x,
        vec![vec![0, 2, 6], vec![1, 3, 6], vec![2, 4, 7], vec![3, 5, 7]]
    );
    assert_eq!(
        right_small.checks.h_z,
        vec![vec![0, 1, 6], vec![2, 3, 6, 7], vec![4, 5, 7]]
    );
    verify_css_orthogonality(
        right_small.stats.n,
        &right_small.checks.h_x,
        &right_small.checks.h_z,
    )
    .unwrap();
}

#[test]
fn hypergraph_product_rejects_out_of_range_input() {
    let err = construct_css(CssConstructionSpec::HypergraphProduct(
        HypergraphProductSpec {
            left: CssClassicalCheckSpec {
                num_cols: 3,
                rows: vec![vec![0, 3]],
            },
            right: classical_2x3(),
        },
    ))
    .unwrap_err();

    assert_eq!(
        err,
        QecError::SparseGf2SupportOutOfRange {
            row: 0,
            support: 3,
            num_cols: 3,
        }
    );
}

#[test]
fn hypergraph_product_rejects_duplicate_input_support() {
    let err = construct_css(CssConstructionSpec::HypergraphProduct(
        HypergraphProductSpec {
            left: CssClassicalCheckSpec {
                num_cols: 3,
                rows: vec![vec![1, 1]],
            },
            right: classical_2x3(),
        },
    ))
    .unwrap_err();

    assert_eq!(
        err,
        QecError::DuplicateSparseRowSupport { row: 0, support: 1 }
    );
}