use std::collections::BTreeMap;
use std::sync::LazyLock;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use sha2::{Digest, Sha256};
use crate::binary::try_binary_rank;
use crate::codes::built_in_css::{
BuiltInCssCodeSpec, BuiltInCssFamily, BuiltInCssParams, built_in_css_checks,
parse_built_in_css_code_spec,
};
use crate::codes::color_666::{COLOR_666_CONSTRUCTION_ID, color_666_sparse_checks};
pub use crate::codes::color_666::{Color666FamilySpec, Color666Layout};
pub use crate::codes::coprime_bb::CoprimeBivariateBicycleSpec;
use crate::codes::coprime_bb::{
COPRIME_BB_CONSTRUCTION_ID, coprime_bb_known_distances, coprime_bb_sparse_checks,
};
use crate::codes::directional::{
DirectionalConnectivity, DirectionalCssSpec, build_directional_css_checks,
};
pub use crate::codes::generalized_bicycle::GeneralizedBicycleSpec;
use crate::codes::generalized_bicycle::{
GENERALIZED_BICYCLE_CONSTRUCTION_ID, generalized_bicycle_known_distances,
generalized_bicycle_sparse_checks,
};
use crate::codes::la_cross::{
LA_CROSS_CONSTRUCTION_ID, la_cross_classical_check, la_cross_known_distances,
};
pub use crate::codes::la_cross::{LaCrossBoundary, LaCrossSpec};
use crate::codes::quantum_tanner::{
QuantumTannerSpec, quantum_tanner_css_checks, quantum_tanner_spec_from_json_str,
};
use crate::codes::random_hgp::{
RandomHgpClassicalSample, RandomHgpSpec, random_hgp_spec_from_json_str,
sample_random_hgp_classical_matrices, sampled_random_hgp_to_hgp_spec,
};
use crate::codes::random_two_block::{
RandomTwoBlockSpec, random_two_block_css_checks, random_two_block_spec_from_json_str,
};
use crate::codes::toric_3d::{Toric3dSpec, toric_3d_css_checks};
use crate::css::SparseRowsMatrix;
use crate::error::{QecError, Result};
use crate::finite_group::{FiniteGroupSpec, GroupAlgebraElement};
use crate::lifted_product::lifted_product_binary_checks;
use crate::sparse_gf2::SparseGf2Matrix;
pub const CSS_CONSTRUCTION_SCHEMA_VERSION: u64 = 1;
pub const DOCUMENTED_NON_FAMILY_CONSTRUCTION_IDS: &[&str] = &[
"hypergraph_product",
"legacy_built_in",
"steane",
"bb72",
"apm_kasai",
"bb",
"repetition_x",
"repetition_z",
"surface_rotated",
"toric",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RequestedFamilyId {
Directional,
QuantumTanner,
GeneralizedBicycle,
LaCross,
RandomHgp,
LiftedProduct,
#[serde(rename = "hyperbolic_5_5")]
Hyperbolic55,
CoprimeBb,
#[serde(rename = "toric_3d")]
Toric3d,
#[serde(rename = "color_666")]
Color666,
Surface,
ShorLike,
RandomTwoBlock,
PerturbedHgp,
}
impl RequestedFamilyId {
pub const ALL: [Self; 14] = [
Self::Directional,
Self::QuantumTanner,
Self::GeneralizedBicycle,
Self::LaCross,
Self::RandomHgp,
Self::LiftedProduct,
Self::Hyperbolic55,
Self::CoprimeBb,
Self::Toric3d,
Self::Color666,
Self::Surface,
Self::ShorLike,
Self::RandomTwoBlock,
Self::PerturbedHgp,
];
pub const fn as_str(self) -> &'static str {
match self {
Self::Directional => "directional",
Self::QuantumTanner => "quantum_tanner",
Self::GeneralizedBicycle => "generalized_bicycle",
Self::LaCross => "la_cross",
Self::RandomHgp => "random_hgp",
Self::LiftedProduct => "lifted_product",
Self::Hyperbolic55 => "hyperbolic_5_5",
Self::CoprimeBb => "coprime_bb",
Self::Toric3d => "toric_3d",
Self::Color666 => "color_666",
Self::Surface => "surface",
Self::ShorLike => "shor_like",
Self::RandomTwoBlock => "random_two_block",
Self::PerturbedHgp => "perturbed_hgp",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SurfaceLayout {
Rotated,
Unrotated,
}
impl SurfaceLayout {
pub const fn as_str(self) -> &'static str {
match self {
Self::Rotated => "rotated",
Self::Unrotated => "unrotated",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SurfaceSpec {
pub layout: SurfaceLayout,
pub row_distance: usize,
pub column_distance: usize,
}
impl SurfaceSpec {
pub const fn rotated_square(distance: usize) -> Self {
Self {
layout: SurfaceLayout::Rotated,
row_distance: distance,
column_distance: distance,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SurfaceFamilySpec {
pub distance: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShorLikeSpec {
pub outer_blocks: usize,
pub inner_block: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CssFamilySpec {
Directional(DirectionalCssSpec),
QuantumTanner(QuantumTannerSpec),
GeneralizedBicycle(GeneralizedBicycleSpec),
LaCross(LaCrossSpec),
RandomHgp(RandomHgpSpec),
LiftedProduct(LiftedProductSpec),
CoprimeBb(CoprimeBivariateBicycleSpec),
Toric3d(Toric3dSpec),
Color666(Color666FamilySpec),
Surface(SurfaceFamilySpec),
ShorLike(ShorLikeSpec),
RandomTwoBlock(RandomTwoBlockSpec),
}
impl CssFamilySpec {
pub const fn callable_requested_family_ids() -> &'static [RequestedFamilyId] {
&[
RequestedFamilyId::Directional,
RequestedFamilyId::QuantumTanner,
RequestedFamilyId::GeneralizedBicycle,
RequestedFamilyId::LaCross,
RequestedFamilyId::RandomHgp,
RequestedFamilyId::LiftedProduct,
RequestedFamilyId::CoprimeBb,
RequestedFamilyId::Toric3d,
RequestedFamilyId::Color666,
RequestedFamilyId::Surface,
RequestedFamilyId::ShorLike,
RequestedFamilyId::RandomTwoBlock,
]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CssClassicalCheckSpec {
pub num_cols: usize,
pub rows: Vec<Vec<usize>>,
}
pub static CLASSICAL_IDENTITY_2: LazyLock<CssClassicalCheckSpec> =
LazyLock::new(|| CssClassicalCheckSpec {
num_cols: 2,
rows: vec![vec![0], vec![1]],
});
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HypergraphProductSpec {
pub left: CssClassicalCheckSpec,
pub right: CssClassicalCheckSpec,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FiniteGroupTableSpec {
pub order: usize,
pub identity: usize,
pub multiplication_table: Vec<Vec<usize>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GroupAlgebraElementSpec {
pub support: Vec<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GroupAlgebraProtographSpec {
pub rows: Vec<Vec<GroupAlgebraElementSpec>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LiftedProductSpec {
pub group: FiniteGroupTableSpec,
pub left: GroupAlgebraProtographSpec,
pub right: GroupAlgebraProtographSpec,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LegacyBuiltInCssSpec {
pub code_id: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CssConstructionSpec {
Family(CssFamilySpec),
Surface(SurfaceSpec),
HypergraphProduct(HypergraphProductSpec),
LiftedProduct(LiftedProductSpec),
LegacyBuiltIn(LegacyBuiltInCssSpec),
}
impl From<CssFamilySpec> for CssConstructionSpec {
fn from(value: CssFamilySpec) -> Self {
Self::Family(value)
}
}
impl From<SurfaceSpec> for CssConstructionSpec {
fn from(value: SurfaceSpec) -> Self {
Self::Surface(value)
}
}
impl CssConstructionSpec {
pub const fn documented_non_family_construction_ids() -> &'static [&'static str] {
DOCUMENTED_NON_FAMILY_CONSTRUCTION_IDS
}
pub fn from_inline(input: &str) -> Result<Self> {
let parsed = parse_built_in_css_code_spec(input)?;
if let BuiltInCssCodeSpec::Family {
family: BuiltInCssFamily::SurfaceRotated,
params: BuiltInCssParams::Distance { distance },
} = parsed
{
return Ok(CssFamilySpec::Surface(SurfaceFamilySpec { distance }).into());
}
if let BuiltInCssCodeSpec::Family {
family: BuiltInCssFamily::Color666,
params: BuiltInCssParams::Distance { distance },
} = parsed
{
return Ok(CssFamilySpec::Color666(Color666FamilySpec {
distance,
layout: Color666Layout::Triangular,
})
.into());
}
if let BuiltInCssCodeSpec::Family {
family: BuiltInCssFamily::Toric3d,
params: BuiltInCssParams::Toric3d(spec),
} = parsed
{
return Ok(CssFamilySpec::Toric3d(spec).into());
}
Ok(Self::LegacyBuiltIn(LegacyBuiltInCssSpec {
code_id: input.to_owned(),
}))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CssChecks {
pub h_x: Vec<Vec<usize>>,
pub h_z: Vec<Vec<usize>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CssCodeStats {
pub n: usize,
pub m_x: usize,
pub m_z: usize,
pub rank_x: usize,
pub rank_z: usize,
pub k: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub d_x: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub d_z: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CssConstructionProvenance {
pub adapter: String,
pub source: String,
pub normalized_input_digest: String,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct CssConstructionResult {
pub schema_version: u64,
pub construction_id: String,
pub requested_family_id: Option<RequestedFamilyId>,
pub normalized_parameters: BTreeMap<String, Value>,
pub checks: CssChecks,
pub stats: CssCodeStats,
pub provenance: CssConstructionProvenance,
}
pub fn construct_css(spec: CssConstructionSpec) -> Result<CssConstructionResult> {
match spec {
CssConstructionSpec::Family(CssFamilySpec::Surface(spec)) => construct_legacy_surface(spec),
CssConstructionSpec::Family(CssFamilySpec::QuantumTanner(spec)) => {
let checks = quantum_tanner_css_checks(&spec)?;
let parameters = quantum_tanner_normalized_parameters(&spec);
construction_result(
"quantum_tanner",
Some(RequestedFamilyId::QuantumTanner),
parameters,
checks.num_cols,
checks.hx,
checks.hz,
"quantum_tanner",
"CssFamilySpec::QuantumTanner",
None,
)
}
CssConstructionSpec::Family(CssFamilySpec::GeneralizedBicycle(spec)) => {
let checks = generalized_bicycle_sparse_checks(&spec)?;
let normalized = checks.normalized_spec;
let mut parameters = BTreeMap::new();
parameters.insert("order".to_owned(), Value::from(normalized.order));
parameters.insert(
"a_exponents".to_owned(),
serde_json::to_value(&normalized.a_exponents).expect("serializable exponents"),
);
parameters.insert(
"b_exponents".to_owned(),
serde_json::to_value(&normalized.b_exponents).expect("serializable exponents"),
);
let known_distances = generalized_bicycle_known_distances(&normalized);
construction_result(
GENERALIZED_BICYCLE_CONSTRUCTION_ID,
Some(RequestedFamilyId::GeneralizedBicycle),
parameters,
checks.num_cols,
checks.h_x,
checks.h_z,
GENERALIZED_BICYCLE_CONSTRUCTION_ID,
"CssFamilySpec::GeneralizedBicycle",
known_distances,
)
}
CssConstructionSpec::Family(CssFamilySpec::LaCross(spec)) => construct_la_cross(spec),
CssConstructionSpec::Family(CssFamilySpec::CoprimeBb(spec)) => {
let checks = coprime_bb_sparse_checks(&spec)?;
let normalized = checks.normalized_spec;
let mut parameters = BTreeMap::new();
parameters.insert("l".to_owned(), Value::from(normalized.l));
parameters.insert("m".to_owned(), Value::from(normalized.m));
parameters.insert(
"cyclic_order".to_owned(),
Value::from(normalized.l * normalized.m),
);
parameters.insert("pi".to_owned(), Value::from("xy"));
parameters.insert(
"a_exponents".to_owned(),
serde_json::to_value(&normalized.a_exponents).unwrap(),
);
parameters.insert(
"b_exponents".to_owned(),
serde_json::to_value(&normalized.b_exponents).unwrap(),
);
let known_distances = coprime_bb_known_distances(&normalized);
construction_result(
COPRIME_BB_CONSTRUCTION_ID,
Some(RequestedFamilyId::CoprimeBb),
parameters,
checks.num_cols,
checks.h_x,
checks.h_z,
"coprime_bb",
"CssFamilySpec::CoprimeBb",
known_distances,
)
}
CssConstructionSpec::Family(CssFamilySpec::Toric3d(spec)) => {
let checks = toric_3d_css_checks(spec)?;
let mut parameters = BTreeMap::new();
parameters.insert("lx".to_owned(), Value::from(spec.lx));
parameters.insert("ly".to_owned(), Value::from(spec.ly));
parameters.insert("lz".to_owned(), Value::from(spec.lz));
construction_result(
"toric_3d",
Some(RequestedFamilyId::Toric3d),
parameters,
checks.num_cols,
checks.hx,
checks.hz,
"toric_3d_chain_complex",
"CssFamilySpec::Toric3d",
Some((checks.distances.d_x, checks.distances.d_z)),
)
}
CssConstructionSpec::Family(CssFamilySpec::RandomTwoBlock(spec)) => {
let checks = random_two_block_css_checks(&spec)?;
let parameters = random_two_block_normalized_parameters(&spec, &checks);
construction_result(
"random_two_block",
Some(RequestedFamilyId::RandomTwoBlock),
parameters,
checks.num_cols,
checks.h_x,
checks.h_z,
"random_two_block",
"CssFamilySpec::RandomTwoBlock",
None,
)
}
CssConstructionSpec::Family(CssFamilySpec::RandomHgp(spec)) => {
let samples = sample_random_hgp_classical_matrices(&spec)?;
let hgp = sampled_random_hgp_to_hgp_spec(&samples);
construct_hypergraph_product_from_parts(
hgp,
"random_hgp",
Some(RequestedFamilyId::RandomHgp),
random_hgp_normalized_parameters(&samples),
"random_hgp",
"CssFamilySpec::RandomHgp",
)
}
CssConstructionSpec::Family(CssFamilySpec::LiftedProduct(spec)) => {
construct_lifted_product(spec, "CssFamilySpec::LiftedProduct")
}
CssConstructionSpec::Family(CssFamilySpec::Directional(spec)) => {
let checks = build_directional_css_checks(&spec)?;
let parameters = directional_normalized_parameters(&spec, &checks);
construction_result(
checks.code_id,
Some(RequestedFamilyId::Directional),
parameters,
checks.num_cols,
checks.hx,
checks.hz,
"directional",
"CssFamilySpec::Directional",
directional_known_distances(&spec, &checks.normalized_route),
)
}
CssConstructionSpec::Family(CssFamilySpec::Color666(spec)) => {
let checks = color_666_sparse_checks(&spec)?;
let mut parameters = BTreeMap::new();
parameters.insert("distance".to_owned(), Value::from(spec.distance));
parameters.insert("layout".to_owned(), Value::from(spec.layout.as_str()));
construction_result(
COLOR_666_CONSTRUCTION_ID,
Some(RequestedFamilyId::Color666),
parameters,
checks.num_cols,
checks.rows.clone(),
checks.rows,
COLOR_666_CONSTRUCTION_ID,
"CssFamilySpec::Color666",
Some((spec.distance, spec.distance)),
)
}
CssConstructionSpec::Family(CssFamilySpec::ShorLike(spec)) => construct_shor_like(spec),
CssConstructionSpec::Surface(spec) => construct_surface(spec),
CssConstructionSpec::HypergraphProduct(spec) => construct_hypergraph_product(spec),
CssConstructionSpec::LiftedProduct(spec) => {
construct_lifted_product(spec, "CssConstructionSpec::LiftedProduct")
}
CssConstructionSpec::LegacyBuiltIn(spec) => {
if let Some(distance) = legacy_surface_distance_from_code_id(&spec.code_id) {
preflight_legacy_surface_overflow(distance)?;
}
let checks = built_in_css_checks(&spec.code_id)?;
let mut parameters = BTreeMap::new();
parameters.insert("code_id".to_owned(), Value::from(spec.code_id));
construction_result(
checks.code_id,
None,
parameters,
checks.num_cols,
checks.hx,
checks.hz,
"built_in_css",
"CssConstructionSpec::LegacyBuiltIn",
None,
)
}
}
}
fn construct_la_cross(spec: LaCrossSpec) -> Result<CssConstructionResult> {
let generated = la_cross_classical_check(&spec)?;
let hgp = construct_hypergraph_product(HypergraphProductSpec {
left: generated.check.clone(),
right: generated.check.clone(),
})?;
let mut parameters = BTreeMap::new();
parameters.insert(
"seed_length".to_owned(),
Value::from(generated.spec.seed_length),
);
parameters.insert("reach".to_owned(), Value::from(generated.spec.reach));
parameters.insert(
"boundary".to_owned(),
Value::from(generated.spec.boundary.as_str()),
);
parameters.insert(
"classical_check".to_owned(),
serde_json::to_value(&generated.check).expect("serializable classical check"),
);
let known_distances = la_cross_known_distances(&generated.spec);
construction_result(
LA_CROSS_CONSTRUCTION_ID,
Some(RequestedFamilyId::LaCross),
parameters,
hgp.stats.n,
hgp.checks.h_x,
hgp.checks.h_z,
LA_CROSS_CONSTRUCTION_ID,
"CssFamilySpec::LaCross",
known_distances,
)
}
fn construct_shor_like(spec: ShorLikeSpec) -> Result<CssConstructionResult> {
validate_shor_like_spec(&spec)?;
let n = spec
.outer_blocks
.checked_mul(spec.inner_block)
.ok_or_else(|| QecError::InvalidCssConstruction {
construction: "shor_like".to_owned(),
reason: "shor_like dimension overflow during data qubit count".to_owned(),
})?;
let (h_x, h_z) = shor_like_supports(spec.outer_blocks, spec.inner_block);
let mut parameters = BTreeMap::new();
parameters.insert("inner_block".to_owned(), Value::from(spec.inner_block));
parameters.insert("outer_blocks".to_owned(), Value::from(spec.outer_blocks));
construction_result(
"shor_like",
Some(RequestedFamilyId::ShorLike),
parameters,
n,
h_x,
h_z,
"shor_like",
"CssFamilySpec::ShorLike",
Some((spec.inner_block, spec.outer_blocks)),
)
}
fn validate_shor_like_spec(spec: &ShorLikeSpec) -> Result<()> {
for (parameter, value) in [
("outer_blocks", spec.outer_blocks),
("inner_block", spec.inner_block),
] {
if value < 2 {
return Err(QecError::InvalidCssConstruction {
construction: "shor_like".to_owned(),
reason: format!("{parameter} must be at least 2, got {value}"),
});
}
}
Ok(())
}
fn shor_like_supports(
outer_blocks: usize,
inner_block: usize,
) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
let mut h_x = Vec::new();
for outer_block in 0..outer_blocks - 1 {
let first_base = outer_block * inner_block;
let second_base = (outer_block + 1) * inner_block;
let mut row = Vec::new();
for offset in 0..inner_block {
row.push(first_base + offset);
}
for offset in 0..inner_block {
row.push(second_base + offset);
}
h_x.push(row);
}
let mut h_z = Vec::new();
for outer_block in 0..outer_blocks {
let base = outer_block * inner_block;
for inner_index in 0..inner_block - 1 {
let first = base + inner_index;
h_z.push(vec![first, first + 1]);
}
}
(h_x, h_z)
}
fn directional_normalized_parameters(
spec: &DirectionalCssSpec,
checks: &crate::codes::directional::DirectionalCssChecks,
) -> BTreeMap<String, Value> {
let mut parameters = BTreeMap::new();
parameters.insert(
"torus".to_owned(),
serde_json::to_value(&spec.torus).expect("serializable directional torus"),
);
parameters.insert("route".to_owned(), Value::from(spec.route.clone()));
parameters.insert(
"normalized_route".to_owned(),
Value::from(checks.normalized_route.clone()),
);
parameters.insert(
"route_support".to_owned(),
serde_json::to_value(&checks.route_support)
.expect("serializable directional route support"),
);
parameters.insert(
"layout".to_owned(),
serde_json::to_value(&spec.layout).expect("serializable directional layout"),
);
parameters.insert(
"connectivity".to_owned(),
serde_json::to_value(spec.connectivity).expect("serializable directional connectivity"),
);
parameters
}
fn directional_known_distances(
spec: &DirectionalCssSpec,
normalized_route: &str,
) -> Option<(usize, usize)> {
match (
spec.torus.period_x,
spec.torus.period_y,
spec.torus.vertical_period_x_shift,
normalized_route,
spec.connectivity,
) {
(8, 6, 4, "NE2N", DirectionalConnectivity::Square) => Some((3, 3)),
(18, 4, 0, "NE3N", DirectionalConnectivity::Hex) => Some((4, 4)),
_ => None,
}
}
fn legacy_surface_distance_from_code_id(code_id: &str) -> Option<usize> {
match parse_built_in_css_code_spec(code_id).ok()? {
BuiltInCssCodeSpec::Family {
family: BuiltInCssFamily::SurfaceRotated,
params: BuiltInCssParams::Distance { distance },
} => Some(distance),
_ => None,
}
}
fn construct_legacy_surface(spec: SurfaceFamilySpec) -> Result<CssConstructionResult> {
preflight_legacy_surface_overflow(spec.distance)?;
let checks = built_in_css_checks(&format!("surface_rotated:d={}", spec.distance))?;
let mut parameters = BTreeMap::new();
parameters.insert("distance".to_owned(), Value::from(spec.distance));
construction_result(
checks.code_id,
Some(RequestedFamilyId::Surface),
parameters,
checks.num_cols,
checks.hx,
checks.hz,
"built_in_css",
"CssFamilySpec::Surface",
Some((spec.distance, spec.distance)),
)
}
fn construct_surface(spec: SurfaceSpec) -> Result<CssConstructionResult> {
validate_surface_spec(&spec)?;
let (n, h_x, h_z, construction_id) = match spec.layout {
SurfaceLayout::Rotated => {
let n = spec
.row_distance
.checked_mul(spec.column_distance)
.ok_or_else(|| surface_overflow("data qubit count"))?;
let (h_x, h_z) = rotated_surface_supports(spec.row_distance, spec.column_distance);
(n, h_x, h_z, "surface_rotated")
}
SurfaceLayout::Unrotated => {
let n = unrotated_surface_num_data_qubits(spec.row_distance, spec.column_distance)?;
let (h_x, h_z) = unrotated_surface_supports(spec.row_distance, spec.column_distance)?;
(n, h_x, h_z, "surface_unrotated")
}
};
let mut parameters = BTreeMap::new();
parameters.insert("layout".to_owned(), Value::from(spec.layout.as_str()));
parameters.insert("row_distance".to_owned(), Value::from(spec.row_distance));
parameters.insert(
"column_distance".to_owned(),
Value::from(spec.column_distance),
);
construction_result(
construction_id,
Some(RequestedFamilyId::Surface),
parameters,
n,
h_x,
h_z,
"surface",
"CssConstructionSpec::Surface",
Some((spec.column_distance, spec.row_distance)),
)
}
fn validate_surface_spec(spec: &SurfaceSpec) -> Result<()> {
validate_surface_distance("row_distance", spec.row_distance)?;
validate_surface_distance("column_distance", spec.column_distance)?;
if matches!(spec.layout, SurfaceLayout::Rotated)
&& (spec.row_distance > isize::MAX as usize / 2
|| spec.column_distance > isize::MAX as usize / 2)
{
return Err(surface_overflow("rotated coordinate arithmetic"));
}
Ok(())
}
fn validate_surface_distance(parameter: &'static str, value: usize) -> Result<()> {
if value < 2 {
return Err(QecError::InvalidCssConstruction {
construction: "surface".to_owned(),
reason: format!("{parameter} must be at least 2, got {value}"),
});
}
Ok(())
}
fn preflight_legacy_surface_overflow(distance: usize) -> Result<()> {
distance
.checked_mul(distance)
.ok_or_else(|| surface_overflow("data qubit count"))?;
if distance > isize::MAX as usize / 2 {
return Err(surface_overflow("rotated coordinate arithmetic"));
}
Ok(())
}
fn surface_overflow(operation: &'static str) -> QecError {
QecError::InvalidCssConstruction {
construction: "surface".to_owned(),
reason: format!("surface dimension overflow during {operation}"),
}
}
fn rotated_surface_supports(
row_distance: usize,
column_distance: usize,
) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
let mut h_x = Vec::new();
let mut h_z = Vec::new();
for ax in 0..=row_distance {
for ay in 0..=column_distance {
let on_row_boundary = ax == 0 || ax == row_distance;
let on_column_boundary = ay == 0 || ay == column_distance;
let parity = (ax % 2) != (ay % 2);
if on_row_boundary && parity {
continue;
}
if on_column_boundary && !parity {
continue;
}
let support = rotated_surface_measure_support(row_distance, column_distance, ax, ay);
if support.is_empty() {
continue;
}
if parity {
h_x.push(support);
} else {
h_z.push(support);
}
}
}
(h_x, h_z)
}
fn rotated_surface_measure_support(
row_distance: usize,
column_distance: usize,
ax: usize,
ay: usize,
) -> Vec<usize> {
let mut support = Vec::new();
let mx = (2 * ax) as isize;
let my = (2 * ay) as isize;
for (dx, dy) in [(1isize, 1isize), (1, -1), (-1, 1), (-1, -1)] {
let x = mx + dx;
let y = my + dy;
if x >= 1
&& x <= (2 * row_distance - 1) as isize
&& y >= 1
&& y <= (2 * column_distance - 1) as isize
&& x % 2 == 1
&& y % 2 == 1
{
let qx = ((x - 1) / 2) as usize;
let qy = ((y - 1) / 2) as usize;
if qx < row_distance && qy < column_distance {
support.push(qx * column_distance + qy);
}
}
}
support.sort_unstable();
support.dedup();
support
}
fn unrotated_surface_num_data_qubits(row_distance: usize, column_distance: usize) -> Result<usize> {
let grid_rows = checked_surface_grid_extent(row_distance, "row grid extent")?;
let grid_columns = checked_surface_grid_extent(column_distance, "column grid extent")?;
grid_rows
.checked_mul(grid_columns)
.and_then(|count| count.checked_add(1))
.map(|count| count / 2)
.ok_or_else(|| surface_overflow("data qubit count"))
}
fn checked_surface_grid_extent(distance: usize, operation: &'static str) -> Result<usize> {
distance
.checked_mul(2)
.and_then(|extent| extent.checked_sub(1))
.ok_or_else(|| surface_overflow(operation))
}
fn unrotated_surface_data_indices(
grid_rows: usize,
grid_columns: usize,
) -> Result<Vec<Vec<Option<usize>>>> {
let mut data_indices = Vec::with_capacity(grid_rows);
let mut next_index = 0usize;
for row in 0..grid_rows {
let mut indices = Vec::with_capacity(grid_columns);
for column in 0..grid_columns {
if (row % 2) == (column % 2) {
indices.push(Some(next_index));
next_index = next_index
.checked_add(1)
.ok_or_else(|| surface_overflow("data qubit count"))?;
} else {
indices.push(None);
}
}
data_indices.push(indices);
}
Ok(data_indices)
}
fn unrotated_surface_supports(
row_distance: usize,
column_distance: usize,
) -> Result<(Vec<Vec<usize>>, Vec<Vec<usize>>)> {
let grid_rows = checked_surface_grid_extent(row_distance, "row grid extent")?;
let grid_columns = checked_surface_grid_extent(column_distance, "column grid extent")?;
let data_indices = unrotated_surface_data_indices(grid_rows, grid_columns)?;
let mut h_x = Vec::with_capacity(
row_distance
.checked_sub(1)
.and_then(|rows| rows.checked_mul(column_distance))
.ok_or_else(|| surface_overflow("X-check count"))?,
);
let mut h_z = Vec::with_capacity(
column_distance
.checked_sub(1)
.and_then(|columns| row_distance.checked_mul(columns))
.ok_or_else(|| surface_overflow("Z-check count"))?,
);
for row in (1..grid_rows).step_by(2) {
for column in (0..grid_columns).step_by(2) {
h_x.push(unrotated_surface_check_support(
grid_rows,
grid_columns,
&data_indices,
row,
column,
));
}
}
for row in (0..grid_rows).step_by(2) {
for column in (1..grid_columns).step_by(2) {
h_z.push(unrotated_surface_check_support(
grid_rows,
grid_columns,
&data_indices,
row,
column,
));
}
}
Ok((h_x, h_z))
}
fn unrotated_surface_check_support(
grid_rows: usize,
grid_columns: usize,
data_indices: &[Vec<Option<usize>>],
row: usize,
column: usize,
) -> Vec<usize> {
let mut support = Vec::with_capacity(4);
for (neighbor_row, neighbor_column) in [
(row.checked_sub(1), Some(column)),
(Some(row), column.checked_sub(1)),
(Some(row), column.checked_add(1)),
(row.checked_add(1), Some(column)),
] {
if let (Some(neighbor_row), Some(neighbor_column)) = (neighbor_row, neighbor_column)
&& neighbor_row < grid_rows
&& neighbor_column < grid_columns
&& let Some(index) = data_indices[neighbor_row][neighbor_column]
{
support.push(index);
}
}
support
}
fn quantum_tanner_normalized_parameters(spec: &QuantumTannerSpec) -> BTreeMap<String, Value> {
let mut base_group = BTreeMap::new();
base_group.insert(
"name".to_owned(),
spec.base_group
.name
.as_ref()
.map_or(Value::Null, |value| Value::from(value.clone())),
);
base_group.insert(
"element_order".to_owned(),
spec.base_group
.element_order
.as_ref()
.map_or(Value::Null, |value| Value::from(value.clone())),
);
base_group.insert("order".to_owned(), Value::from(spec.base_group.order));
base_group.insert("identity".to_owned(), Value::from(spec.base_group.identity));
base_group.insert(
"multiplication_table".to_owned(),
serde_json::to_value(&spec.base_group.multiplication_table).expect("serializable table"),
);
let mut local_codes = BTreeMap::new();
local_codes.insert(
"matrix_role".to_owned(),
Value::from(spec.local_codes.matrix_role.clone()),
);
local_codes.insert(
"field".to_owned(),
Value::from(spec.local_codes.field.clone()),
);
local_codes.insert(
"h_a".to_owned(),
serde_json::to_value(&spec.local_codes.h_a).expect("serializable h_a"),
);
local_codes.insert(
"h_b".to_owned(),
serde_json::to_value(&spec.local_codes.h_b).expect("serializable h_b"),
);
local_codes.insert(
"g_a".to_owned(),
serde_json::to_value(&spec.local_codes.g_a).expect("serializable g_a"),
);
local_codes.insert(
"g_b".to_owned(),
serde_json::to_value(&spec.local_codes.g_b).expect("serializable g_b"),
);
let mut parameters = BTreeMap::new();
parameters.insert(
"construction_mode".to_owned(),
Value::from(spec.construction_mode.as_str()),
);
parameters.insert(
"base_group".to_owned(),
serde_json::to_value(base_group).expect("serializable base_group"),
);
parameters.insert(
"a_generator_indices".to_owned(),
serde_json::to_value(&spec.a_generator_indices).expect("serializable a generators"),
);
parameters.insert(
"b_generator_indices".to_owned(),
serde_json::to_value(&spec.b_generator_indices).expect("serializable b generators"),
);
parameters.insert(
"local_codes".to_owned(),
serde_json::to_value(local_codes).expect("serializable local_codes"),
);
parameters
}
fn random_two_block_normalized_parameters(
spec: &RandomTwoBlockSpec,
checks: &crate::codes::random_two_block::RandomTwoBlockCssChecks,
) -> BTreeMap<String, Value> {
let mut group = BTreeMap::new();
group.insert("order".to_owned(), Value::from(spec.group.order()));
group.insert("identity".to_owned(), Value::from(spec.group.identity()));
group.insert(
"multiplication_table".to_owned(),
serde_json::to_value(spec.group.multiplication_table())
.expect("serializable random two-block group table"),
);
let mut parameters = BTreeMap::new();
parameters.insert(
"group".to_owned(),
serde_json::to_value(group).expect("serializable random two-block group"),
);
parameters.insert(
"group_digest".to_owned(),
Value::from(checks.metadata.group_digest.clone()),
);
parameters.insert("seed".to_owned(), Value::from(checks.metadata.seed));
parameters.insert(
"support_a_weight".to_owned(),
Value::from(checks.metadata.support_a_weight),
);
parameters.insert(
"support_b_weight".to_owned(),
Value::from(checks.metadata.support_b_weight),
);
parameters.insert(
"algorithm_version".to_owned(),
Value::from(checks.metadata.algorithm_version),
);
parameters.insert(
"support_a".to_owned(),
serde_json::to_value(&checks.support_a).expect("serializable support A"),
);
parameters.insert(
"support_b".to_owned(),
serde_json::to_value(&checks.support_b).expect("serializable support B"),
);
parameters
}
fn random_hgp_normalized_parameters(
samples: &crate::codes::random_hgp::RandomHgpClassicalSamples,
) -> BTreeMap<String, Value> {
let mut parameters = BTreeMap::new();
parameters.insert(
"left".to_owned(),
random_hgp_classical_parameters(&samples.left),
);
parameters.insert(
"right".to_owned(),
random_hgp_classical_parameters(&samples.right),
);
parameters
}
fn random_hgp_classical_parameters(sample: &RandomHgpClassicalSample) -> Value {
serde_json::json!({
"classical_spec": sample.spec,
"rows": sample.rows,
"sampler_version": sample.spec.algorithm_version,
})
}
pub fn parse_css_construction_json(input: &str) -> Result<CssConstructionSpec> {
let value: Value = serde_json::from_str(input)
.map_err(|error| QecError::InvalidCssConstructionJson(error.to_string()))?;
let object = value.as_object().ok_or_else(|| {
QecError::InvalidCssConstructionJson(
"construction request must be a JSON object".to_owned(),
)
})?;
let version = required_u64(object, "schema_version")?;
if version != CSS_CONSTRUCTION_SCHEMA_VERSION {
return Err(QecError::UnsupportedCssConstructionSchemaVersion { version });
}
let construction = required_string(object, "construction")?;
match construction {
"surface" => surface_construction_from_json(object, construction),
"generalized_bicycle" => Ok(CssFamilySpec::GeneralizedBicycle(GeneralizedBicycleSpec {
order: required_usize(object, "order", construction)?,
a_exponents: required_usize_array(object, "a_exponents", construction)?,
b_exponents: required_usize_array(object, "b_exponents", construction)?,
})
.into()),
"coprime_bb" => Ok(CssFamilySpec::CoprimeBb(CoprimeBivariateBicycleSpec {
l: required_usize(object, "l", construction)?,
m: required_usize(object, "m", construction)?,
a_exponents: required_usize_array(object, "a_exponents", construction)?,
b_exponents: required_usize_array(object, "b_exponents", construction)?,
})
.into()),
"color_666" => {
let layout = optional_string(object, "layout")?
.map(Color666Layout::parse)
.transpose()?
.unwrap_or(Color666Layout::Triangular);
Ok(CssFamilySpec::Color666(Color666FamilySpec {
distance: required_usize(object, "distance", construction)?,
layout,
})
.into())
}
"shor_like" => {
let spec = ShorLikeSpec {
outer_blocks: required_usize(object, "outer_blocks", construction)?,
inner_block: required_usize(object, "inner_block", construction)?,
};
validate_shor_like_spec(&spec)?;
Ok(CssFamilySpec::ShorLike(spec).into())
}
"quantum_tanner" => {
let spec_value = object.get("spec").unwrap_or(&value);
let mut spec_object = spec_value.as_object().cloned().ok_or_else(|| {
QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: "spec must be a JSON object".to_owned(),
}
})?;
spec_object.remove("schema_version");
spec_object.remove("construction");
let spec_json = serde_json::to_string(&spec_object)
.expect("JSON object serialization should not fail");
Ok(CssFamilySpec::QuantumTanner(quantum_tanner_spec_from_json_str(&spec_json)?).into())
}
"toric_3d" => {
let spec = Toric3dSpec {
lx: required_usize(object, "lx", construction)?,
ly: required_usize(object, "ly", construction)?,
lz: required_usize(object, "lz", construction)?,
};
toric_3d_css_checks(spec)?;
Ok(CssFamilySpec::Toric3d(spec).into())
}
"la_cross" => {
let spec = LaCrossSpec {
seed_length: required_usize(object, "seed_length", construction)?,
reach: required_usize(object, "reach", construction)?,
boundary: LaCrossBoundary::parse(required_string(object, "boundary")?)?,
};
la_cross_classical_check(&spec)?;
Ok(CssFamilySpec::LaCross(spec).into())
}
"random_two_block" => {
Ok(CssFamilySpec::RandomTwoBlock(random_two_block_spec_from_json_str(input)?).into())
}
"random_hgp" => Ok(CssFamilySpec::RandomHgp(random_hgp_spec_from_json_str(input)?).into()),
"directional" => directional_construction_from_json(object, construction),
"hypergraph_product" => Ok(CssConstructionSpec::HypergraphProduct(
serde_json::from_value(value.clone()).map_err(|error| {
QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: error.to_string(),
}
})?,
)),
"lifted_product" => Ok(CssFamilySpec::LiftedProduct(
serde_json::from_value(value.clone()).map_err(|error| {
QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: error.to_string(),
}
})?,
)
.into()),
"legacy_built_in" => Ok(CssConstructionSpec::LegacyBuiltIn(LegacyBuiltInCssSpec {
code_id: required_string(object, "code_id")?.to_owned(),
})),
unknown => Err(QecError::UnknownCssConstruction {
construction: unknown.to_owned(),
}),
}
}
fn directional_construction_from_json(
object: &Map<String, Value>,
construction: &str,
) -> Result<CssConstructionSpec> {
let spec_value = if let Some(spec_value) = object.get("spec") {
for key in object.keys() {
if !matches!(key.as_str(), "schema_version" | "construction" | "spec") {
return Err(QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("unknown directional construction field {key:?}"),
});
}
}
spec_value.clone()
} else {
let mut spec_object = object.clone();
spec_object.remove("schema_version");
spec_object.remove("construction");
Value::Object(spec_object)
};
let spec =
serde_json::from_value(spec_value).map_err(|error| QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: error.to_string(),
})?;
Ok(CssFamilySpec::Directional(spec).into())
}
pub fn verify_css_orthogonality(n: usize, h_x: &[Vec<usize>], h_z: &[Vec<usize>]) -> Result<()> {
let h_x = canonical_sparse_rows(n, h_x.to_vec())?;
let h_z = canonical_sparse_rows(n, h_z.to_vec())?;
if h_x.iter().all(|x_row| {
h_z.iter().all(|z_row| {
let mut x_index = 0;
let mut z_index = 0;
let mut parity = false;
while x_index < x_row.len() && z_index < z_row.len() {
match x_row[x_index].cmp(&z_row[z_index]) {
std::cmp::Ordering::Less => x_index += 1,
std::cmp::Ordering::Greater => z_index += 1,
std::cmp::Ordering::Equal => {
parity = !parity;
x_index += 1;
z_index += 1;
}
}
}
!parity
})
}) {
Ok(())
} else {
Err(QecError::InvalidCssOrthogonality)
}
}
fn construct_lifted_product(
spec: LiftedProductSpec,
provenance_source: &str,
) -> Result<CssConstructionResult> {
let group = FiniteGroupSpec::new(
spec.group.order,
spec.group.identity,
spec.group.multiplication_table,
)?;
let left = group_algebra_protograph_matrix(&group, spec.left)?;
let right = group_algebra_protograph_matrix(&group, spec.right)?;
let checks = lifted_product_binary_checks(&group, &left, &right)?;
let mut parameters = BTreeMap::new();
parameters.insert(
"group".to_owned(),
serde_json::json!({
"order": group.order(),
"identity": group.identity(),
"multiplication_table": group.multiplication_table(),
}),
);
parameters.insert(
"left".to_owned(),
serde_json::to_value(group_algebra_protograph_spec(&left))
.expect("serializable lifted-product protograph"),
);
parameters.insert(
"right".to_owned(),
serde_json::to_value(group_algebra_protograph_spec(&right))
.expect("serializable lifted-product protograph"),
);
let known_distances = is_canonical_c3_fixture(&group, &left)
.then_some((3, 3))
.filter(|_| is_canonical_c3_fixture(&group, &right));
construction_result(
"lifted_product",
Some(RequestedFamilyId::LiftedProduct),
parameters,
checks.num_cols,
checks.h_x,
checks.h_z,
"lifted_product",
provenance_source,
known_distances,
)
}
fn group_algebra_protograph_matrix(
group: &FiniteGroupSpec,
spec: GroupAlgebraProtographSpec,
) -> Result<Vec<Vec<GroupAlgebraElement>>> {
spec.rows
.into_iter()
.map(|row| {
row.into_iter()
.map(|entry| GroupAlgebraElement::new(group, entry.support))
.collect()
})
.collect()
}
fn group_algebra_protograph_spec(
matrix: &[Vec<GroupAlgebraElement>],
) -> GroupAlgebraProtographSpec {
GroupAlgebraProtographSpec {
rows: matrix
.iter()
.map(|row| {
row.iter()
.map(|entry| GroupAlgebraElementSpec {
support: entry.support().to_vec(),
})
.collect()
})
.collect(),
}
}
fn is_canonical_c3_fixture(group: &FiniteGroupSpec, matrix: &[Vec<GroupAlgebraElement>]) -> bool {
group.order() == 3
&& group.identity() == 0
&& group.multiplication_table() == [vec![0, 1, 2], vec![1, 2, 0], vec![2, 0, 1]]
&& matrix_supports(matrix)
== vec![
vec![vec![1, 2], vec![0], vec![]],
vec![vec![], vec![0, 1], vec![1]],
]
}
fn matrix_supports(matrix: &[Vec<GroupAlgebraElement>]) -> Vec<Vec<Vec<usize>>> {
matrix
.iter()
.map(|row| {
row.iter()
.map(|element| element.support().to_vec())
.collect()
})
.collect()
}
fn construct_hypergraph_product(spec: HypergraphProductSpec) -> Result<CssConstructionResult> {
let parameters = normalized_hypergraph_product_parameters(&spec)?;
construct_hypergraph_product_from_parts(
spec,
"hypergraph_product",
None,
parameters,
"hypergraph_product",
"CssConstructionSpec::HypergraphProduct",
)
}
fn construct_hypergraph_product_from_parts(
spec: HypergraphProductSpec,
construction_id: &'static str,
requested_family_id: Option<RequestedFamilyId>,
normalized_parameters: BTreeMap<String, Value>,
adapter: &'static str,
source: &'static str,
) -> Result<CssConstructionResult> {
let HypergraphProductSpec {
left: left_spec,
right: right_spec,
} = spec;
let left = classical_check_matrix(left_spec)?;
let right = classical_check_matrix(right_spec)?;
let left_identity_rows = SparseGf2Matrix::identity(left.num_rows())?;
let left_identity_cols = SparseGf2Matrix::identity(left.num_cols())?;
let right_identity_rows = SparseGf2Matrix::identity(right.num_rows())?;
let right_identity_cols = SparseGf2Matrix::identity(right.num_cols())?;
let left_transpose = left.transpose()?;
let right_transpose = right.transpose()?;
let h_x = left
.kron(&right_identity_cols)?
.hconcat(&left_identity_rows.kron(&right_transpose)?)?;
let h_z = left_identity_cols
.kron(&right)?
.hconcat(&left_transpose.kron(&right_identity_rows)?)?;
debug_assert_eq!(h_x.num_cols(), h_z.num_cols());
construction_result(
construction_id,
requested_family_id,
normalized_parameters,
h_x.num_cols(),
h_x.rows().to_vec(),
h_z.rows().to_vec(),
adapter,
source,
None,
)
}
fn normalized_hypergraph_product_parameters(
spec: &HypergraphProductSpec,
) -> Result<BTreeMap<String, Value>> {
let left = classical_check_matrix(spec.left.clone())?;
let right = classical_check_matrix(spec.right.clone())?;
let mut parameters = BTreeMap::new();
parameters.insert(
"left".to_owned(),
serde_json::to_value(CssClassicalCheckSpec {
num_cols: left.num_cols(),
rows: left.rows().to_vec(),
})
.expect("serializable spec"),
);
parameters.insert(
"right".to_owned(),
serde_json::to_value(CssClassicalCheckSpec {
num_cols: right.num_cols(),
rows: right.rows().to_vec(),
})
.expect("serializable spec"),
);
Ok(parameters)
}
fn classical_check_matrix(spec: CssClassicalCheckSpec) -> Result<SparseGf2Matrix> {
for (row_index, row) in spec.rows.iter().enumerate() {
let mut supports = std::collections::BTreeSet::new();
for &support in row {
if !supports.insert(support) {
return Err(QecError::DuplicateSparseRowSupport {
row: row_index,
support,
});
}
}
}
SparseGf2Matrix::new(spec.rows.len(), spec.num_cols, spec.rows)
}
fn construction_result(
construction_id: impl Into<String>,
requested_family_id: Option<RequestedFamilyId>,
normalized_parameters: BTreeMap<String, Value>,
n: usize,
h_x: Vec<Vec<usize>>,
h_z: Vec<Vec<usize>>,
adapter: impl Into<String>,
source: impl Into<String>,
known_distances: Option<(usize, usize)>,
) -> Result<CssConstructionResult> {
let construction_id = construction_id.into();
let adapter = adapter.into();
let source = source.into();
let normalized_input_digest = normalized_input_digest(
&construction_id,
requested_family_id,
&normalized_parameters,
);
let h_x = canonical_sparse_rows(n, h_x)?;
let h_z = canonical_sparse_rows(n, h_z)?;
verify_css_orthogonality(n, &h_x, &h_z)?;
let rank_x = try_binary_rank(&dense_rows(n, &h_x))?;
let rank_z = try_binary_rank(&dense_rows(n, &h_z))?;
let (d_x, d_z) = known_distances
.map(|(d_x, d_z)| (Some(d_x), Some(d_z)))
.unwrap_or((None, None));
let stats = CssCodeStats {
n,
m_x: h_x.len(),
m_z: h_z.len(),
rank_x,
rank_z,
k: n.saturating_sub(rank_x + rank_z),
d_x,
d_z,
};
Ok(CssConstructionResult {
schema_version: CSS_CONSTRUCTION_SCHEMA_VERSION,
construction_id,
requested_family_id,
normalized_parameters,
checks: CssChecks { h_x, h_z },
stats,
provenance: CssConstructionProvenance {
adapter,
source,
normalized_input_digest,
},
})
}
fn normalized_input_digest(
construction_id: &str,
requested_family_id: Option<RequestedFamilyId>,
normalized_parameters: &BTreeMap<String, Value>,
) -> String {
let payload = serde_json::json!({
"schema_version": CSS_CONSTRUCTION_SCHEMA_VERSION,
"construction_id": construction_id,
"requested_family_id": requested_family_id,
"normalized_parameters": normalized_parameters,
});
let json = serde_json::to_vec(&payload).expect("normalized construction input is serializable");
format!("sha256:{}", lower_hex(&Sha256::digest(json)))
}
fn lower_hex(bytes: impl AsRef<[u8]>) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let bytes = bytes.as_ref();
let mut output = String::with_capacity(bytes.len() * 2);
for &byte in bytes {
output.push(HEX[(byte >> 4) as usize] as char);
output.push(HEX[(byte & 0x0f) as usize] as char);
}
output
}
fn canonical_sparse_rows(n: usize, mut rows: Vec<Vec<usize>>) -> Result<Vec<Vec<usize>>> {
SparseRowsMatrix::new(n, rows.clone())?;
for row in &mut rows {
row.sort_unstable();
}
Ok(rows)
}
fn dense_rows(n: usize, rows: &[Vec<usize>]) -> Vec<Vec<u8>> {
rows.iter()
.map(|row| {
let mut dense = vec![0; n];
for &column in row {
dense[column] = 1;
}
dense
})
.collect()
}
fn surface_construction_from_json(
object: &Map<String, Value>,
construction: &str,
) -> Result<CssConstructionSpec> {
let has_legacy_distance = object.contains_key("distance");
let has_layout_aware_fields = object.contains_key("layout")
|| object.contains_key("row_distance")
|| object.contains_key("column_distance");
if has_legacy_distance && has_layout_aware_fields {
return Err(QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: "conflicting legacy distance and layout-aware surface parameters".to_owned(),
});
}
if has_legacy_distance {
return Ok(CssFamilySpec::Surface(SurfaceFamilySpec {
distance: required_usize(object, "distance", construction)?,
})
.into());
}
if !has_layout_aware_fields {
return Err(QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: "missing or invalid distance".to_owned(),
});
}
let layout = match required_string(object, "layout")? {
"rotated" => SurfaceLayout::Rotated,
"unrotated" => SurfaceLayout::Unrotated,
value => {
return Err(QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("unknown surface layout {value}"),
});
}
};
Ok(SurfaceSpec {
layout,
row_distance: required_usize(object, "row_distance", construction)?,
column_distance: required_usize(object, "column_distance", construction)?,
}
.into())
}
fn required_string<'a>(object: &'a Map<String, Value>, field: &str) -> Result<&'a str> {
object
.get(field)
.and_then(Value::as_str)
.ok_or_else(|| QecError::InvalidCssConstructionJson(format!("missing or invalid {field}")))
}
fn optional_string<'a>(object: &'a Map<String, Value>, field: &str) -> Result<Option<&'a str>> {
match object.get(field) {
None => Ok(None),
Some(Value::String(value)) => Ok(Some(value)),
Some(_) => Err(QecError::InvalidCssConstructionJson(format!(
"missing or invalid {field}"
))),
}
}
fn required_u64(object: &Map<String, Value>, field: &str) -> Result<u64> {
object
.get(field)
.and_then(Value::as_u64)
.ok_or_else(|| QecError::InvalidCssConstructionJson(format!("missing or invalid {field}")))
}
fn required_usize(object: &Map<String, Value>, field: &str, construction: &str) -> Result<usize> {
let value = object.get(field).and_then(Value::as_u64).ok_or_else(|| {
QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("missing or invalid {field}"),
}
})?;
usize::try_from(value).map_err(|_| QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("{field} is outside usize range"),
})
}
fn required_usize_array(
object: &Map<String, Value>,
field: &str,
construction: &str,
) -> Result<Vec<usize>> {
let values = object.get(field).and_then(Value::as_array).ok_or_else(|| {
QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("missing or invalid {field}"),
}
})?;
values
.iter()
.enumerate()
.map(|(index, value)| {
let value = value
.as_u64()
.ok_or_else(|| QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("{field}[{index}] must be a nonnegative integer"),
})?;
json_u64_to_usize_array_entry(value, field, index, construction)
})
.collect()
}
#[cfg(target_pointer_width = "64")]
fn json_u64_to_usize_array_entry(
value: u64,
_field: &str,
_index: usize,
_construction: &str,
) -> Result<usize> {
Ok(value as usize)
}
#[cfg(not(target_pointer_width = "64"))]
fn json_u64_to_usize_array_entry(
value: u64,
field: &str,
index: usize,
construction: &str,
) -> Result<usize> {
usize::try_from(value).map_err(|_| QecError::InvalidCssConstruction {
construction: construction.to_owned(),
reason: format!("{field}[{index}] is outside usize range"),
})
}