prism_q/distributed/
mod.rs1pub mod comm;
6#[cfg(any(test, feature = "bench-internal"))]
7pub mod loopback;
8
9use std::sync::Arc;
10
11pub use comm::{RankComm, SerialComm};
12
13#[cfg(feature = "distributed-mpi")]
14pub use comm::MpiComm;
15
16use crate::env_knobs::parse_usize_knob;
17
18pub const MIN_LOCAL_QUBITS_DEFAULT: usize = 10;
21
22pub fn min_local_qubits() -> usize {
27 use std::sync::OnceLock;
28 static CACHED: OnceLock<usize> = OnceLock::new();
29 *CACHED.get_or_init(|| {
30 parse_usize_knob(
31 "PRISM_DIST_MIN_LOCAL_QUBITS",
32 std::env::var("PRISM_DIST_MIN_LOCAL_QUBITS").ok(),
33 MIN_LOCAL_QUBITS_DEFAULT,
34 1,
35 )
36 })
37}
38
39pub const EXCHANGE_CHUNK_DEFAULT: usize = usize::MAX;
41
42pub fn exchange_chunk() -> usize {
47 use std::sync::OnceLock;
48 static CACHED: OnceLock<usize> = OnceLock::new();
49 *CACHED.get_or_init(|| {
50 parse_usize_knob(
51 "PRISM_DIST_EXCHANGE_CHUNK",
52 std::env::var("PRISM_DIST_EXCHANGE_CHUNK").ok(),
53 EXCHANGE_CHUNK_DEFAULT,
54 1,
55 )
56 })
57}
58
59pub fn relabel_enabled() -> bool {
69 use std::sync::OnceLock;
70 static CACHED: OnceLock<bool> = OnceLock::new();
71 *CACHED.get_or_init(|| {
72 parse_bool_knob(
73 "PRISM_DIST_RELABEL",
74 std::env::var("PRISM_DIST_RELABEL").ok(),
75 true,
76 )
77 })
78}
79
80fn parse_bool_knob(var: &str, raw: Option<String>, default: bool) -> bool {
83 let Some(raw) = raw else {
84 return default;
85 };
86 match raw.trim() {
87 "0" => false,
88 "1" => true,
89 other if other.eq_ignore_ascii_case("false") => false,
90 other if other.eq_ignore_ascii_case("true") => true,
91 other => {
92 eprintln!("warning: {var}={other:?} is not a flag; using {default}.");
93 default
94 }
95 }
96}
97
98pub struct DistributedContext {
100 comm: Arc<dyn RankComm>,
101}
102
103impl std::fmt::Debug for DistributedContext {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.debug_struct("DistributedContext")
106 .field("rank", &self.rank())
107 .field("size", &self.size())
108 .finish()
109 }
110}
111
112impl DistributedContext {
113 pub fn from_comm(comm: Arc<dyn RankComm>) -> Arc<Self> {
114 Arc::new(Self { comm })
115 }
116
117 pub fn serial() -> Arc<Self> {
119 Self::from_comm(Arc::new(SerialComm))
120 }
121
122 #[cfg(feature = "distributed-mpi")]
125 pub fn world() -> crate::error::Result<Arc<Self>> {
126 MpiComm::world().map(|c| Self::from_comm(Arc::new(c)))
127 }
128
129 #[cfg(feature = "distributed-mpi")]
136 pub fn attached_world() -> crate::error::Result<Option<Arc<Self>>> {
137 Ok(MpiComm::attach_world()?.map(|c| Self::from_comm(Arc::new(c))))
138 }
139
140 pub fn rank(&self) -> usize {
141 self.comm.rank()
142 }
143
144 pub fn size(&self) -> usize {
145 self.comm.size()
146 }
147
148 pub(crate) fn comm(&self) -> &Arc<dyn RankComm> {
149 &self.comm
150 }
151}
152
153#[cfg(test)]
154mod knob_tests {
155 use super::*;
156
157 #[test]
160 fn min_local_qubits_knob_rejects_invalid_values() {
161 let d = MIN_LOCAL_QUBITS_DEFAULT;
162 let parse =
163 |raw: &str| parse_usize_knob("PRISM_DIST_MIN_LOCAL_QUBITS", Some(raw.into()), d, 1);
164 assert_eq!(parse("4"), 4);
165 assert_eq!(parse(" 4 "), 4);
166 assert_eq!(parse("abc"), d, "unparseable falls back");
167 assert_eq!(parse("-1"), d, "negative falls back");
168 assert_eq!(parse("0"), d, "below the minimum falls back");
169 assert_eq!(
170 parse_usize_knob("PRISM_DIST_MIN_LOCAL_QUBITS", None, d, 1),
171 d
172 );
173 }
174
175 #[test]
176 fn exchange_chunk_knob_rejects_invalid_values() {
177 let d = EXCHANGE_CHUNK_DEFAULT;
178 let parse =
179 |raw: &str| parse_usize_knob("PRISM_DIST_EXCHANGE_CHUNK", Some(raw.into()), d, 1);
180 assert_eq!(parse("4096"), 4096);
181 assert_eq!(parse("4k"), d, "unparseable falls back");
182 assert_eq!(parse("0"), d, "below the minimum falls back");
183 assert_eq!(parse_usize_knob("PRISM_DIST_EXCHANGE_CHUNK", None, d, 1), d);
184 }
185
186 #[test]
187 fn relabel_knob_rejects_invalid_values() {
188 let parse = |raw: &str| parse_bool_knob("PRISM_DIST_RELABEL", Some(raw.into()), true);
189 assert!(!parse("0"));
190 assert!(!parse("false"));
191 assert!(!parse("FALSE"));
192 assert!(parse("1"));
193 assert!(parse("true"));
194 assert!(parse("yes"), "unrecognized falls back to the default");
195 assert!(parse(""), "empty falls back to the default");
196 assert!(parse_bool_knob("PRISM_DIST_RELABEL", None, true));
197 }
198}