pub fn canonical_assignment_kind(kind: &str) -> Result<&'static str, String> {
match kind {
"softmax" => Ok("softmax"),
"ordered_beta_bernoulli" => Ok("ordered_beta_bernoulli"),
"threshold_gate" => Ok("threshold_gate"),
"topk" => Ok("topk"),
_ => Err(format!(
"assignment={kind:?} is not a recognized assignment kind; expected one of \
['ordered_beta_bernoulli', 'softmax', 'threshold_gate', 'topk']"
)),
}
}
pub fn validate_fitted_basis_kind(name: &str) -> Result<(), String> {
match name {
"periodic" | "sphere" | "torus" | "projective_plane" | "klein_bottle" | "linear"
| "linear_block" | "euclidean" | "duchon" | "poincare" | "cylinder" | "mobius"
| "finite_set" | "spectral_graph" => Ok(()),
_ => Err(format!(
"basis kind {name:?} is not canonical; expected one of ['cylinder', 'duchon', \
'euclidean', 'finite_set', 'klein_bottle', 'linear', 'linear_block', 'mobius', \
'periodic', 'poincare', 'projective_plane', 'spectral_graph', 'sphere', 'torus']"
)),
}
}
pub fn validate_seed_basis_kind(name: &str) -> Result<(), String> {
match name {
"periodic" | "sphere" | "torus" | "projective_plane" | "klein_bottle" | "linear"
| "linear_block" | "euclidean" | "duchon" | "poincare" | "mobius" | "auto" => Ok(()),
"cylinder" | "finite_set" => Err(format!(
"basis kind {name:?} is discovery-only and cannot seed a fit"
)),
_ => Err(format!(
"basis kind {name:?} is not canonical; expected one of ['auto', 'duchon', \
'euclidean', 'klein_bottle', 'linear', 'linear_block', 'mobius', 'periodic', \
'poincare', 'projective_plane', 'sphere', 'torus']"
)),
}
}
pub fn basis_kind_for_topology(name: &str) -> Result<String, String> {
match name {
"circle" => Ok("periodic".to_string()),
"sphere" | "torus" | "projective_plane" | "klein_bottle" | "linear" | "linear_block"
| "euclidean" | "duchon" | "poincare" | "mobius" | "auto" => Ok(name.to_string()),
"cylinder" | "finite_set" => Err(format!(
"topology {name:?} is discovery-only and cannot seed a fit"
)),
_ => Err(format!(
"topology {name:?} is not canonical; expected one of ['auto', 'circle', 'duchon', \
'euclidean', 'klein_bottle', 'linear', 'linear_block', 'mobius', 'poincare', \
'projective_plane', 'sphere', 'torus']"
)),
}
}
pub fn basis_to_topology(basis: &str) -> Result<String, String> {
validate_fitted_basis_kind(basis)?;
Ok(match basis {
"periodic" => "circle",
"duchon" | "euclidean" => "euclidean",
"spectral_graph" => "graph",
other => other,
}
.to_string())
}
pub fn canonical_topology(name: &str) -> Result<String, String> {
basis_kind_for_topology(name)?;
Ok(name.to_string())
}
pub fn flat_block_assignment(gating: &str) -> Result<&'static str, String> {
match gating {
"norm_selection" => Ok("ordered_beta_bernoulli"),
"separate_gate" => Ok("threshold_gate"),
_ => Err(format!(
"flat_block gating={gating:?} is not recognized; expected one of \
['norm_selection', 'separate_gate']"
)),
}
}
pub fn topologies_for_bases(bases: &[String]) -> Result<Vec<String>, String> {
bases.iter().map(|b| basis_to_topology(b)).collect()
}
pub fn topology_for_bases(bases: &[String]) -> Result<Option<String>, String> {
let per_atom = topologies_for_bases(bases)?;
let Some(first) = per_atom.first() else {
return Ok(None);
};
if per_atom.iter().all(|t| t == first) {
Ok(Some(first.clone()))
} else {
Ok(Some("mixed".to_string()))
}
}