5353use pecos_rng:: { PecosRng , Rng } ;
5454use std:: collections:: { BTreeMap , BTreeSet } ;
5555
56+ use std:: fmt;
57+ use std:: str:: FromStr ;
58+
5659use super :: types:: combine_probabilities;
5760
5861// ============================================================================
@@ -160,10 +163,10 @@ impl EffectKey {
160163 observables,
161164 }
162165 }
166+ }
163167
164- /// Formats this key as a string (e.g., "D0 D1 L0").
165- #[ must_use]
166- pub fn to_string ( & self ) -> String {
168+ impl fmt:: Display for EffectKey {
169+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
167170 let mut parts: Vec < String > = Vec :: new ( ) ;
168171 for & d in & self . detectors {
169172 parts. push ( format ! ( "D{d}" ) ) ;
@@ -172,9 +175,9 @@ impl EffectKey {
172175 parts. push ( format ! ( "L{o}" ) ) ;
173176 }
174177 if parts. is_empty ( ) {
175- "(empty)" . to_string ( )
178+ write ! ( f , "(empty)" )
176179 } else {
177- parts. join ( " " )
180+ write ! ( f , "{}" , parts. join( " " ) )
178181 }
179182 }
180183}
@@ -208,54 +211,12 @@ impl ParsedDem {
208211 /// Parses a DEM from a string.
209212 ///
210213 /// Supports both Stim and PECOS DEM formats.
211- pub fn from_str ( dem_str : & str ) -> Result < Self , DemParseError > {
212- let mut mechanisms = Vec :: new ( ) ;
213- let mut max_det: i32 = -1 ;
214- let mut max_obs: i32 = -1 ;
215-
216- for line in dem_str. lines ( ) {
217- let line = line. trim ( ) ;
218-
219- // Skip empty lines and comments
220- if line. is_empty ( ) || line. starts_with ( '#' ) {
221- continue ;
222- }
223-
224- // Parse error lines
225- if line. starts_with ( "error(" ) {
226- let mech = Self :: parse_error_line ( line) ?;
227-
228- // Update max IDs
229- for comp in & mech. components {
230- for & d in & comp. detectors {
231- max_det = max_det. max ( d as i32 ) ;
232- }
233- for & o in & comp. observables {
234- max_obs = max_obs. max ( o as i32 ) ;
235- }
236- }
237-
238- mechanisms. push ( mech) ;
239- }
240- // Parse detector declarations
241- else if line. starts_with ( "detector" ) {
242- if let Some ( id) = Self :: extract_detector_id ( line) {
243- max_det = max_det. max ( id as i32 ) ;
244- }
245- }
246- // Parse observable declarations
247- else if line. starts_with ( "logical_observable" )
248- && let Some ( id) = Self :: extract_observable_id ( line)
249- {
250- max_obs = max_obs. max ( id as i32 ) ;
251- }
252- }
253-
254- Ok ( Self {
255- mechanisms,
256- num_detectors : if max_det >= 0 { max_det as u32 + 1 } else { 0 } ,
257- num_observables : if max_obs >= 0 { max_obs as u32 + 1 } else { 0 } ,
258- } )
214+ ///
215+ /// # Errors
216+ ///
217+ /// Returns `DemParseError` if the string cannot be parsed.
218+ pub fn parse ( dem_str : & str ) -> Result < Self , DemParseError > {
219+ dem_str. parse ( )
259220 }
260221
261222 /// Parses a single error line.
@@ -304,13 +265,13 @@ impl ParsedDem {
304265 let mut observables = Vec :: new ( ) ;
305266
306267 for token in s. split_whitespace ( ) {
307- if token. starts_with ( 'D' ) {
308- let id: u32 = token [ 1 .. ]
268+ if let Some ( id_str ) = token. strip_prefix ( 'D' ) {
269+ let id: u32 = id_str
309270 . parse ( )
310271 . map_err ( |_| DemParseError :: InvalidDetectorId ( token. to_string ( ) ) ) ?;
311272 detectors. push ( id) ;
312- } else if token. starts_with ( 'L' ) {
313- let id: u32 = token [ 1 .. ]
273+ } else if let Some ( id_str ) = token. strip_prefix ( 'L' ) {
274+ let id: u32 = id_str
314275 . parse ( )
315276 . map_err ( |_| DemParseError :: InvalidObservableId ( token. to_string ( ) ) ) ?;
316277 observables. push ( id) ;
@@ -473,6 +434,60 @@ impl Default for ParsedDem {
473434 }
474435}
475436
437+ impl FromStr for ParsedDem {
438+ type Err = DemParseError ;
439+
440+ fn from_str ( dem_str : & str ) -> Result < Self , Self :: Err > {
441+ let mut mechanisms = Vec :: new ( ) ;
442+ let mut max_det: i32 = -1 ;
443+ let mut max_obs: i32 = -1 ;
444+
445+ for line in dem_str. lines ( ) {
446+ let line = line. trim ( ) ;
447+
448+ // Skip empty lines and comments
449+ if line. is_empty ( ) || line. starts_with ( '#' ) {
450+ continue ;
451+ }
452+
453+ // Parse error lines
454+ if line. starts_with ( "error(" ) {
455+ let mech = Self :: parse_error_line ( line) ?;
456+
457+ // Update max IDs
458+ for comp in & mech. components {
459+ for & d in & comp. detectors {
460+ max_det = max_det. max ( d as i32 ) ;
461+ }
462+ for & o in & comp. observables {
463+ max_obs = max_obs. max ( o as i32 ) ;
464+ }
465+ }
466+
467+ mechanisms. push ( mech) ;
468+ }
469+ // Parse detector declarations
470+ else if line. starts_with ( "detector" ) {
471+ if let Some ( id) = Self :: extract_detector_id ( line) {
472+ max_det = max_det. max ( id as i32 ) ;
473+ }
474+ }
475+ // Parse observable declarations
476+ else if line. starts_with ( "logical_observable" )
477+ && let Some ( id) = Self :: extract_observable_id ( line)
478+ {
479+ max_obs = max_obs. max ( id as i32 ) ;
480+ }
481+ }
482+
483+ Ok ( Self {
484+ mechanisms,
485+ num_detectors : if max_det >= 0 { max_det as u32 + 1 } else { 0 } ,
486+ num_observables : if max_obs >= 0 { max_obs as u32 + 1 } else { 0 } ,
487+ } )
488+ }
489+ }
490+
476491// ============================================================================
477492// Parse Errors
478493// ============================================================================
@@ -928,7 +943,7 @@ mod tests {
928943
929944 assert_eq ! ( dem. mechanisms. len( ) , 1 ) ;
930945 assert ! ( !dem. mechanisms[ 0 ] . is_decomposed( ) ) ;
931- assert_eq ! ( dem. mechanisms[ 0 ] . probability, 0.01 ) ;
946+ assert ! ( ( dem. mechanisms[ 0 ] . probability - 0.01 ) . abs ( ) < f64 :: EPSILON ) ;
932947 assert_eq ! ( dem. mechanisms[ 0 ] . components[ 0 ] . detectors, vec![ 0 , 1 ] ) ;
933948 }
934949
0 commit comments