stim-parser 0.3.0.0 → 0.4.0.0
raw patch · 7 files changed
+121/−41 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ StimParser.Expr: AnnPauli :: Pauli -> Ind -> AnnTarget
+ StimParser.Expr: AnnQ :: Ind -> AnnTarget
+ StimParser.Expr: AnnRec :: Rec -> AnnTarget
+ StimParser.Expr: data AnnTarget
+ StimParser.Expr: instance GHC.Classes.Eq StimParser.Expr.AnnTarget
+ StimParser.Expr: instance GHC.Classes.Eq StimParser.Expr.Pauli
+ StimParser.Expr: instance GHC.Classes.Eq StimParser.Expr.Rec
+ StimParser.Expr: instance GHC.Classes.Ord StimParser.Expr.Pauli
+ StimParser.Expr: instance GHC.Classes.Ord StimParser.Expr.Rec
+ StimParser.Expr: instance GHC.Show.Show StimParser.Expr.AnnTarget
+ StimParser.Parse: parseAnnPauliTarget :: Parser AnnTarget
+ StimParser.Parse: parseAnnRecTarget :: Parser AnnTarget
+ StimParser.Parse: parseAnnTargetMixed :: Parser AnnTarget
+ StimParser.Parse: parseAnnTargetQ :: Parser AnnTarget
+ StimParser.Trans: instance StimParser.Trans.FlattenQ StimParser.Expr.AnnTarget
- StimParser.Expr: Ann :: AnnTy -> Maybe Tag -> [FInd] -> [Q] -> Ann
+ StimParser.Expr: Ann :: AnnTy -> Maybe Tag -> [FInd] -> [AnnTarget] -> Ann
Files
- CHANGELOG.md +13/−0
- src/StimParser/Expr.hs +21/−3
- src/StimParser/Parse.hs +52/−26
- src/StimParser/Trans.hs +17/−6
- stim-parser.cabal +1/−1
- test/Test/Parse.hs +12/−3
- test/Test/Trans.hs +5/−2
CHANGELOG.md view
@@ -1,5 +1,18 @@ # Revision history for stim-parser +## 0.4.0.0 -- 2026-07-20++* Add Pauli-target support for `OBSERVABLE_INCLUDE` and `DETECTOR`+ annotations.+ - New `AnnTarget` sum type: `AnnQ Ind | AnnRec Rec | AnnPauli Pauli Ind`.+ - `Ann` now stores `[AnnTarget]` instead of `[Q]`.+ - Single-qubit Pauli targets such as `X0`, `Y1`, `Z2` are accepted and+ preserved, matching Python Stim behavior.+ - Mixed `rec[-k]` and Pauli targets are supported.+ - The `*` combiner remains unsupported inside annotations.+* This is a **breaking API change**. Downstream code pattern-matching on+ `Ann` or constructing annotation values needs to use `AnnTarget`.+ ## 0.3.0.0 -- 2026-07-20 * Add optional tag support for DEM detector and observable declarations.
src/StimParser/Expr.hs view
@@ -4,7 +4,7 @@ type Ind = Int type Ph = Double newtype Rec = Rec Int- deriving (Show)+ deriving (Show, Eq, Ord) newtype Sweep = Sweep Int deriving (Show) @@ -17,7 +17,7 @@ deriving (Show) data Pauli = PX | PY | PZ - deriving (Show)+ deriving (Show, Eq, Ord) data PauliInd = PauliInd Pauli Ind deriving (Show) @@ -161,11 +161,29 @@ annTyList :: [AnnTy] annTyList = [DETECTOR ..] +-- Targets that can appear inside annotations.+--+-- * 'AnnQ' is a plain qubit index (used by QUBIT_COORDS and MPAD).+-- * 'AnnRec' is a measurement-record reference (used by DETECTOR and+-- OBSERVABLE_INCLUDE).+-- * 'AnnPauli' is a single-qubit Pauli operator (used by DETECTOR and+-- OBSERVABLE_INCLUDE to declare Pauli-product detectors/observables).+--+-- Space-separated Pauli targets are implicitly multiplied, matching Python+-- Stim behavior. The '*' combiner is not supported inside annotations.+data AnnTarget+ = AnnQ Ind+ | AnnRec Rec+ | AnnPauli Pauli Ind+ deriving (Show, Eq)+ -- Annotations examples: -- DETECTOR(1, 0) rec[-3] rec[-6] -- DETECTOR(2, 0, 0) rec[-8] -- DETECTOR rec[-3] rec[-4] rec[-7]+-- DETECTOR X0 Z1 -- OBSERVABLE_INCLUDE(0) rec[-1] rec[-2]+-- OBSERVABLE_INCLUDE(0) X0 Z1 -- QUBIT_COORDS(0, 0) 0 -- MPAD 0 0 1 0 1 -- SHIFT_COORDS(500.5)@@ -193,7 +211,7 @@ newtype Coords = Coords [FInd] -data Ann = Ann AnnTy (Maybe Tag) [FInd] [Q]+data Ann = Ann AnnTy (Maybe Tag) [FInd] [AnnTarget] deriving (Show) -- repeat example:
src/StimParser/Parse.hs view
@@ -6,6 +6,7 @@ import StimParser.ParseUtils import Control.Monad import Data.List+import Data.Maybe import Text.Megaparsec hiding (State) import Text.Megaparsec.Char import qualified Text.Megaparsec.Char.Lexer as L@@ -241,35 +242,60 @@ -- the order is tricky try pm2 <|> pm1 +-- | Parse a measurement-record target inside an annotation.+-- Requires a negative index, matching the semantics of rec[] in gates.+parseAnnRecTarget :: Parser AnnTarget+parseAnnRecTarget = do+ lstring "rec"+ lstring "["+ i <- parseInt+ lstring "]"+ if i < 0+ then return $ AnnRec (Rec i)+ else fail "rec[] index in annotations must be negative (e.g., rec[-1])"++-- | Parse a single-qubit Pauli target inside an annotation.+-- The '*' combiner is intentionally not supported here.+parseAnnPauliTarget :: Parser AnnTarget+parseAnnPauliTarget = lexeme $ do+ PauliInd p i <- parsePauliInd+ return $ AnnPauli p i++-- | Parse a target that may appear inside DETECTOR or OBSERVABLE_INCLUDE:+-- either a measurement-record reference or a single-qubit Pauli target.+parseAnnTargetMixed :: Parser AnnTarget+parseAnnTargetMixed =+ try parseAnnRecTarget <|> parseAnnPauliTarget++-- | Parse a plain qubit-index target for annotations such as QUBIT_COORDS.+parseAnnTargetQ :: Parser AnnTarget+parseAnnTargetQ = AnnQ <$> parseInt+ parseAnn :: Parser Ann -parseAnn = do - let - -- parser for case: DETECTOR[tag](1, 0) rec[-3] rec[-6]- pm1 = do - ty <- parseAnnTy - tag <- optional parseTag+parseAnn = do+ ty <- parseAnnTy+ tag <- optional parseTag+ case ty of+ TICK ->+ return $ Ann TICK tag [] []+ SHIFT_COORDS -> do fds <- parseTuple parseFInd- qs <- parseExhaust parseQ- return $ Ann ty tag fds qs- -- parser for case: DETECTOR[tag] rec[-3] rec[-4] rec[-7]- pm2 = do - ty <- parseAnnTy- tag <- optional parseTag- qs <- parseExhaust parseQ- return $ Ann ty tag [] qs- -- parser for case: SHIFT_COORDS[tag](500.5)- pm3 = do - ty <- parseAnnTy- tag <- optional parseTag+ return $ Ann SHIFT_COORDS tag fds []+ QUBIT_COORDS -> do fds <- parseTuple parseFInd- return $ Ann ty tag fds []- -- parser for case: TICK[tag]- pm4 = do- ty <- parseShowCI TICK- tag <- optional parseTag- return $ Ann ty tag [] []- -- the order is tricky- try pm1 <|> try pm2 <|> try pm3 <|> pm4 + qs <- parseExhaust parseAnnTargetQ+ return $ Ann QUBIT_COORDS tag fds qs+ MPAD -> do+ qs <- parseExhaust parseAnnTargetQ+ return $ Ann MPAD tag [] qs+ DETECTOR -> do+ mfds <- optional (parseTuple parseFInd)+ ts <- parseExhaust parseAnnTargetMixed+ return $ Ann DETECTOR tag (fromMaybe [] mfds) ts+ OBSERVABLE_INCLUDE -> do+ fds <- parseTuple parseFInd+ ts <- parseExhaust parseAnnTargetMixed+ return $ Ann OBSERVABLE_INCLUDE tag fds ts parseStart :: Parser () parseStart = void (lstring "!!!Start")
src/StimParser/Trans.hs view
@@ -37,6 +37,17 @@ nqs <- mapM flattenQ qs return $ Gate gt tag nqs +-- Annotation targets behave similarly to 'Q': plain qubits and Pauli targets+-- are preserved, while measurement-record references are resolved to absolute+-- qubit indices using the current measurement count.+instance FlattenQ AnnTarget where+ type Out AnnTarget = AnnTarget+ flattenQ (AnnQ i) = return (AnnQ i)+ flattenQ (AnnRec (Rec r)) = do+ (count, _) <- get+ return $ AnnQ (count + r + 1)+ flattenQ (AnnPauli p i) = return (AnnPauli p i)+ -- for each measure, we increase the count by 1 -- currently, we assume measure does not contains (QRec Rec) instance FlattenQ Measure where@@ -74,15 +85,15 @@ -- TODO: type-level constraint that fs and cs should be of the same length -- TODO: type-level constraint that count should not be used in this case- flattenQ (Ann DETECTOR tag fs qs) = do+ flattenQ (Ann DETECTOR tag fs ts) = do (count, Coords cs) <- get let nfs = zipWith (+) fs cs- nqs <- mapM flattenQ qs- return $ Just $ Ann DETECTOR tag nfs nqs+ nts <- mapM flattenQ ts+ return $ Just $ Ann DETECTOR tag nfs nts - flattenQ (Ann aty tag fs qs) = do - nqs <- mapM flattenQ qs - return $ Just $ Ann aty tag fs nqs+ flattenQ (Ann aty tag fs ts) = do + nts <- mapM flattenQ ts + return $ Just $ Ann aty tag fs nts instance FlattenQ Stim where type Out Stim = Maybe Stim
stim-parser.cabal view
@@ -20,7 +20,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.3.0.0+version: 0.4.0.0 -- A short (one-line) description of the package. synopsis: A parser combinator library for STIM quantum circuit files
test/Test/Parse.hs view
@@ -260,17 +260,26 @@ [ assertShowEqual "parseAnn TICK" (Ann TICK Nothing [] []) (run parseAnn "TICK") , assertShowEqual "parseAnn DETECTOR with coords and rec" - (Ann DETECTOR Nothing [In 1, In 0] [QRec (Rec (-3)), QRec (Rec (-6))]) + (Ann DETECTOR Nothing [In 1, In 0] [AnnRec (Rec (-3)), AnnRec (Rec (-6))]) (run parseAnn "DETECTOR(1, 0) rec[-3] rec[-6]") , assertShowEqual "parseAnn DETECTOR without coords" - (Ann DETECTOR Nothing [] [QRec (Rec (-3)), QRec (Rec (-4))]) + (Ann DETECTOR Nothing [] [AnnRec (Rec (-3)), AnnRec (Rec (-4))]) (run parseAnn "DETECTOR rec[-3] rec[-4]") , assertShowEqual "parseAnn SHIFT_COORDS" (Ann SHIFT_COORDS Nothing [Fl 500.5] []) (run parseAnn "SHIFT_COORDS(500.5)") , assertShowEqual "parseAnn OBSERVABLE_INCLUDE" - (Ann OBSERVABLE_INCLUDE Nothing [In 0] [QRec (Rec (-1))]) + (Ann OBSERVABLE_INCLUDE Nothing [In 0] [AnnRec (Rec (-1))]) (run parseAnn "OBSERVABLE_INCLUDE(0) rec[-1]")+ , assertShowEqual "parseAnn DETECTOR with Pauli targets" + (Ann DETECTOR Nothing [] [AnnPauli PX 0, AnnPauli PZ 1]) + (run parseAnn "DETECTOR X0 Z1")+ , assertShowEqual "parseAnn OBSERVABLE_INCLUDE with Pauli targets" + (Ann OBSERVABLE_INCLUDE Nothing [In 0] [AnnPauli PX 0, AnnPauli PZ 1]) + (run parseAnn "OBSERVABLE_INCLUDE(0) X0 Z1")+ , assertShowEqual "parseAnn OBSERVABLE_INCLUDE mixed targets" + (Ann OBSERVABLE_INCLUDE Nothing [In 0] [AnnRec (Rec (-1)), AnnPauli PZ 0]) + (run parseAnn "OBSERVABLE_INCLUDE(0) rec[-1] Z0") ] -- | Regression test for the noise-before-gate ordering bug.
test/Test/Trans.hs view
@@ -83,8 +83,11 @@ [ assertShowEqual "flattenQ Ann TICK" (Just (Ann TICK Nothing [] [])) (evalState (flattenQ (Ann TICK Nothing [] [])) (0, Coords [In 0])) , assertShowEqual "flattenQ Ann DETECTOR" - (Just (Ann DETECTOR Nothing [In 1, In 0] [Q 10])) - (evalState (flattenQ (Ann DETECTOR Nothing [In 1, In 0] [Q 10])) (10, Coords [In 0, In 0]))+ (Just (Ann DETECTOR Nothing [In 1, In 0] [AnnQ 10])) + (evalState (flattenQ (Ann DETECTOR Nothing [In 1, In 0] [AnnQ 10])) (10, Coords [In 0, In 0]))+ , assertShowEqual "flattenQ Ann OBSERVABLE_INCLUDE resolves rec" + (Just (Ann OBSERVABLE_INCLUDE Nothing [In 0] [AnnQ 10, AnnPauli PZ 0])) + (evalState (flattenQ (Ann OBSERVABLE_INCLUDE Nothing [In 0] [AnnRec (Rec (-1)), AnnPauli PZ 0])) (10, Coords [In 0])) ] -- Note: SHIFT_COORDS test moved to separate test case