pub mod comm;
#[cfg(any(test, feature = "bench-internal"))]
pub mod loopback;
use std::sync::Arc;
pub use comm::{RankComm, SerialComm};
#[cfg(feature = "distributed-mpi")]
pub use comm::MpiComm;
use crate::env_knobs::parse_usize_knob;
pub const MIN_LOCAL_QUBITS_DEFAULT: usize = 10;
pub fn min_local_qubits() -> usize {
use std::sync::OnceLock;
static CACHED: OnceLock<usize> = OnceLock::new();
*CACHED.get_or_init(|| {
parse_usize_knob(
"PRISM_DIST_MIN_LOCAL_QUBITS",
std::env::var("PRISM_DIST_MIN_LOCAL_QUBITS").ok(),
MIN_LOCAL_QUBITS_DEFAULT,
1,
)
})
}
pub const EXCHANGE_CHUNK_DEFAULT: usize = usize::MAX;
pub fn exchange_chunk() -> usize {
use std::sync::OnceLock;
static CACHED: OnceLock<usize> = OnceLock::new();
*CACHED.get_or_init(|| {
parse_usize_knob(
"PRISM_DIST_EXCHANGE_CHUNK",
std::env::var("PRISM_DIST_EXCHANGE_CHUNK").ok(),
EXCHANGE_CHUNK_DEFAULT,
1,
)
})
}
pub fn relabel_enabled() -> bool {
use std::sync::OnceLock;
static CACHED: OnceLock<bool> = OnceLock::new();
*CACHED.get_or_init(|| {
parse_bool_knob(
"PRISM_DIST_RELABEL",
std::env::var("PRISM_DIST_RELABEL").ok(),
true,
)
})
}
fn parse_bool_knob(var: &str, raw: Option<String>, default: bool) -> bool {
let Some(raw) = raw else {
return default;
};
match raw.trim() {
"0" => false,
"1" => true,
other if other.eq_ignore_ascii_case("false") => false,
other if other.eq_ignore_ascii_case("true") => true,
other => {
eprintln!("warning: {var}={other:?} is not a flag; using {default}.");
default
}
}
}
pub struct DistributedContext {
comm: Arc<dyn RankComm>,
}
impl std::fmt::Debug for DistributedContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DistributedContext")
.field("rank", &self.rank())
.field("size", &self.size())
.finish()
}
}
impl DistributedContext {
pub fn from_comm(comm: Arc<dyn RankComm>) -> Arc<Self> {
Arc::new(Self { comm })
}
pub fn serial() -> Arc<Self> {
Self::from_comm(Arc::new(SerialComm))
}
#[cfg(feature = "distributed-mpi")]
pub fn world() -> crate::error::Result<Arc<Self>> {
MpiComm::world().map(|c| Self::from_comm(Arc::new(c)))
}
#[cfg(feature = "distributed-mpi")]
pub fn attached_world() -> crate::error::Result<Option<Arc<Self>>> {
Ok(MpiComm::attach_world()?.map(|c| Self::from_comm(Arc::new(c))))
}
pub fn rank(&self) -> usize {
self.comm.rank()
}
pub fn size(&self) -> usize {
self.comm.size()
}
pub(crate) fn comm(&self) -> &Arc<dyn RankComm> {
&self.comm
}
}
#[cfg(test)]
mod knob_tests {
use super::*;
#[test]
fn min_local_qubits_knob_rejects_invalid_values() {
let d = MIN_LOCAL_QUBITS_DEFAULT;
let parse =
|raw: &str| parse_usize_knob("PRISM_DIST_MIN_LOCAL_QUBITS", Some(raw.into()), d, 1);
assert_eq!(parse("4"), 4);
assert_eq!(parse(" 4 "), 4);
assert_eq!(parse("abc"), d, "unparseable falls back");
assert_eq!(parse("-1"), d, "negative falls back");
assert_eq!(parse("0"), d, "below the minimum falls back");
assert_eq!(
parse_usize_knob("PRISM_DIST_MIN_LOCAL_QUBITS", None, d, 1),
d
);
}
#[test]
fn exchange_chunk_knob_rejects_invalid_values() {
let d = EXCHANGE_CHUNK_DEFAULT;
let parse =
|raw: &str| parse_usize_knob("PRISM_DIST_EXCHANGE_CHUNK", Some(raw.into()), d, 1);
assert_eq!(parse("4096"), 4096);
assert_eq!(parse("4k"), d, "unparseable falls back");
assert_eq!(parse("0"), d, "below the minimum falls back");
assert_eq!(parse_usize_knob("PRISM_DIST_EXCHANGE_CHUNK", None, d, 1), d);
}
#[test]
fn relabel_knob_rejects_invalid_values() {
let parse = |raw: &str| parse_bool_knob("PRISM_DIST_RELABEL", Some(raw.into()), true);
assert!(!parse("0"));
assert!(!parse("false"));
assert!(!parse("FALSE"));
assert!(parse("1"));
assert!(parse("true"));
assert!(parse("yes"), "unrecognized falls back to the default");
assert!(parse(""), "empty falls back to the default");
assert!(parse_bool_knob("PRISM_DIST_RELABEL", None, true));
}
}