packages feed

stim-parser-0.1.0.0: test/Test/Parse.hs

module Test.Parse where

import Test.HUnit
import StimParser.Parse
import StimParser.ParseUtils (run)
import StimParser.Expr

tests :: Test
tests = TestList
  [ testParsePauli
  , testParsePauliInd
  , testParsePauliChain
  , testParseQ
  , testParseGateTy
  , testParseGate
  , testParseMeasureTy
  , testParsePh
  , testParseMeasure
  , testParseGppTy
  , testParseGpp
  , testParseNoiseTy
  , testParseErrorTag
  , testParseNoise
  , testParseAnnTy
  , testParseFInd
  , testParseAnn
  ]

-- Helper to compare using show (since types don't derive Eq)
assertShowEqual :: (Show a) => String -> a -> a -> Test
assertShowEqual name expected actual = 
  TestCase $ assertEqual name (show expected) (show actual)

-- Test Pauli parsing
testParsePauli :: Test
testParsePauli = TestList
  [ assertShowEqual "parsePauli X" PX (run parsePauli "X")
  , assertShowEqual "parsePauli Y" PY (run parsePauli "Y")
  , assertShowEqual "parsePauli Z" PZ (run parsePauli "Z")
  ]

-- Test PauliInd parsing
testParsePauliInd :: Test
testParsePauliInd = TestList
  [ assertShowEqual "parsePauliInd X0" (PauliInd PX 0) (run parsePauliInd "X0")
  , assertShowEqual "parsePauliInd Y5" (PauliInd PY 5) (run parsePauliInd "Y5")
  , assertShowEqual "parsePauliInd Z10" (PauliInd PZ 10) (run parsePauliInd "Z10")
  ]

-- Test PauliChain parsing
testParsePauliChain :: Test
testParsePauliChain = TestList
  [ assertShowEqual "parsePauliChain simple" 
      (P [PauliInd PX 1]) (run parsePauliChain "X1")
  , assertShowEqual "parsePauliChain with *" 
      (P [PauliInd PX 1, PauliInd PX 2]) (run parsePauliChain "X1*X2")
  , assertShowEqual "parsePauliChain multiple" 
      (P [PauliInd PX 1, PauliInd PY 2, PauliInd PZ 3]) (run parsePauliChain "X1*Y2*Z3")
  , assertShowEqual "parsePauliChain negated" 
      (N [PauliInd PZ 5]) (run parsePauliChain "!Z5")
  , assertShowEqual "parsePauliChain negated multiple" 
      (N [PauliInd PX 1, PauliInd PZ 2]) (run parsePauliChain "!X1*Z2")
  ]

-- Test Q parsing
testParseQ :: Test
testParseQ = TestList
  [ assertShowEqual "parseQ simple" (Q 5) (run parseQ "5")
  , assertShowEqual "parseQ rec" (QRec (Rec (-1))) (run parseQ "rec[-1]")
  , assertShowEqual "parseQ rec negative" (QRec (Rec (-5))) (run parseQ "rec[-5]")
  , assertShowEqual "parseQ sweep" (QSweep (Sweep 5)) (run parseQ "sweep[5]")
  , assertShowEqual "parseQ not" (Not 3) (run parseQ "!3")
  -- Note: rec[5] and rec[0] should fail - they are tested via parse failure tests
  ]

-- Test GateTy parsing
testParseGateTy :: Test
testParseGateTy = TestList
  [ assertShowEqual "parseGateTy I" I (run parseGateTy "I")
  , assertShowEqual "parseGateTy X" X (run parseGateTy "X")
  , assertShowEqual "parseGateTy H" H (run parseGateTy "H")
  , assertShowEqual "parseGateTy CNOT" CNOT (run parseGateTy "CNOT")
  , assertShowEqual "parseGateTy SQRT_X" SQRT_X (run parseGateTy "SQRT_X")
  , assertShowEqual "parseGateTy ISWAP_DAG" ISWAP_DAG (run parseGateTy "ISWAP_DAG")
  -- Case-insensitive tests
  , assertShowEqual "parseGateTy lowercase cnot" CNOT (run parseGateTy "cnot")
  , assertShowEqual "parseGateTy mixed case Cnot" CNOT (run parseGateTy "Cnot")
  , assertShowEqual "parseGateTy lowercase h" H (run parseGateTy "h")
  , assertShowEqual "parseGateTy lowercase sqrt_x" SQRT_X (run parseGateTy "sqrt_x")
  , assertShowEqual "parseGateTy mixed case Sqrt_X_Dag" SQRT_X_DAG (run parseGateTy "Sqrt_X_Dag")
  ]

-- Test Gate parsing
testParseGate :: Test
testParseGate = TestList
  [ assertShowEqual "parseGate single qubit" 
      (Gate X Nothing [Q 0]) (run parseGate "X 0")
  , assertShowEqual "parseGate multiple qubits" 
      (Gate H Nothing [Q 0, Q 1, Q 2]) (run parseGate "H 0 1 2")
  , assertShowEqual "parseGate with rec" 
      (Gate CY Nothing [QRec (Rec (-1)), Q 6]) (run parseGate "CY rec[-1] 6")
  , assertShowEqual "parseGate with sweep" 
      (Gate CY Nothing [QSweep (Sweep 5), Q 7, QSweep (Sweep 5), Q 8]) (run parseGate "CY sweep[5] 7 sweep[5] 8")
  , assertShowEqual "parseGate with not" 
      (Gate X Nothing [Not 1, Q 2]) (run parseGate "X !1 2")
  ]

-- Test MeasureTy parsing
testParseMeasureTy :: Test
testParseMeasureTy = TestList
  [ assertShowEqual "parseMeasureTy M" M (run parseMeasureTy "M")
  , assertShowEqual "parseMeasureTy MX" MX (run parseMeasureTy "MX")
  , assertShowEqual "parseMeasureTy MXX" MXX (run parseMeasureTy "MXX")
  , assertShowEqual "parseMeasureTy MR" MR (run parseMeasureTy "MR")
  -- Case-insensitive tests
  , assertShowEqual "parseMeasureTy lowercase m" M (run parseMeasureTy "m")
  , assertShowEqual "parseMeasureTy lowercase mx" MX (run parseMeasureTy "mx")
  , assertShowEqual "parseMeasureTy lowercase mxx" MXX (run parseMeasureTy "mxx")
  , assertShowEqual "parseMeasureTy mixed case Mr" MR (run parseMeasureTy "Mr")
  ]

-- Test Ph (phase) parsing
testParsePh :: Test
testParsePh = TestList
  [ "parsePh float" ~: 0.02 ~=? run parsePh "(0.02)"
  , "parsePh negative" ~: (-0.5) ~=? run parsePh "(-0.5)"
  ]

-- Test Measure parsing
testParseMeasure :: Test
testParseMeasure = TestList
  [ assertShowEqual "parseMeasure simple" 
      (Measure M Nothing Nothing [Q 5]) (run parseMeasure "M 5")
  , assertShowEqual "parseMeasure with phase" 
      (Measure MZ Nothing (Just 0.02) [Q 2, Q 3, Q 5]) (run parseMeasure "MZ(0.02) 2 3 5")
  , assertShowEqual "parseMeasure pair" 
      (Measure MXX Nothing Nothing [Q 1, Q 2]) (run parseMeasure "MXX 1 2")
  -- Case-insensitive tests
  , assertShowEqual "parseMeasure lowercase m" 
      (Measure M Nothing Nothing [Q 5]) (run parseMeasure "m 5")
  , assertShowEqual "parseMeasure lowercase mz" 
      (Measure MZ Nothing (Just 0.02) [Q 2]) (run parseMeasure "mz(0.02) 2")
  , assertShowEqual "parseMeasure pair with phase" 
      (Measure MXX Nothing (Just 0.01) [Q 2, Q 3]) (run parseMeasure "MXX(0.01) 2 3")
  , assertShowEqual "parseMeasure with not" 
      (Measure MZ Nothing Nothing [Not 5]) (run parseMeasure "MZ !5")
  ]

-- Test GppTy parsing
testParseGppTy :: Test
testParseGppTy = TestList
  [ assertShowEqual "parseGppTy MPP" MPP (run parseGppTy "MPP")
  , assertShowEqual "parseGppTy SPP" SPP (run parseGppTy "SPP")
  , assertShowEqual "parseGppTy SPP_DAG" SPP_DAG (run parseGppTy "SPP_DAG")
  ]

-- Test Gpp parsing
testParseGpp :: Test
testParseGpp = TestList
  [ assertShowEqual "parseGpp simple" 
      (Gpp MPP Nothing Nothing [P [PauliInd PX 2, PauliInd PX 3, PauliInd PX 5, PauliInd PX 7]]) 
      (run parseGpp "MPP X2*X3*X5*X7")
  , assertShowEqual "parseGpp with negation" 
      (Gpp MPP Nothing Nothing [N [PauliInd PZ 5]]) 
      (run parseGpp "MPP !Z5")
  , assertShowEqual "parseGpp with phase" 
      (Gpp MPP Nothing (Just 0.001) [P [PauliInd PZ 1, PauliInd PZ 2], P [PauliInd PX 1, PauliInd PX 2]]) 
      (run parseGpp "MPP(0.001) Z1*Z2 X1*X2")
  , assertShowEqual "parseGpp mixed" 
      (Gpp SPP Nothing Nothing [N [PauliInd PX 1, PauliInd PY 2, PauliInd PZ 3]]) 
      (run parseGpp "SPP !X1*Y2*Z3")
  ]

-- Test NoiseTy parsing
testParseNoiseTy :: Test
testParseNoiseTy = TestList
  [ assertShowEqual "parseNoiseTy E" E (run parseNoiseTy "E")
  , assertShowEqual "parseNoiseTy X_ERROR" X_ERROR (run parseNoiseTy "X_ERROR")
  , assertShowEqual "parseNoiseTy DEPOLARIZE1" DEPOLARIZE1 (run parseNoiseTy "DEPOLARIZE1")
  , assertShowEqual "parseNoiseTy CORRELATED_ERROR" CORRELATED_ERROR (run parseNoiseTy "CORRELATED_ERROR")
  ]

-- Test ErrorTag parsing
testParseErrorTag :: Test
testParseErrorTag = TestList
  [ assertShowEqual "parseErrorTag simple" 
      (ErrorTag MULTIPLE_NOISE_MECHANISMS) (run parseErrorTag "MULTIPLE_NOISE_MECHANISMS")
  , assertShowEqual "parseErrorTag with coef" 
      (ErrorTagCoef LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR 0.1) 
      (run parseErrorTag "LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR:0.1")
  ]

-- Test Noise parsing with all tag/arg combinations
testParseNoise :: Test
testParseNoise = TestList
  -- Basic noise without tag
  [ assertShowEqual "parseNoise X_ERROR with args" 
      (NoiseNormal X_ERROR Nothing Nothing [0.01] [Q 5]) 
      (run parseNoise "X_ERROR(0.01) 5")
  , assertShowEqual "parseNoise DEPOLARIZE1" 
      (NoiseNormal DEPOLARIZE1 Nothing Nothing [0.01] [Q 2,Q 3]) 
      (run parseNoise "DEPOLARIZE1(0.01) 2 3")
  -- II_ERROR variants
  , assertShowEqual "parseNoise II_ERROR no args no tag" 
      (NoiseNormal II_ERROR Nothing Nothing [] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR 0 1")
  , assertShowEqual "parseNoise II_ERROR args no tag" 
      (NoiseNormal II_ERROR Nothing Nothing [0.1] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR(0.1) 0 1")
  , assertShowEqual "parseNoise II_ERROR ErrorTag no args" 
      (NoiseNormal II_ERROR Nothing (Just (ErrorTag TWO_QUBIT_LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR)) [] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR[TWO_QUBIT_LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR] 0 1")
  , assertShowEqual "parseNoise II_ERROR ErrorTag coef no args" 
      (NoiseNormal II_ERROR Nothing (Just (ErrorTagCoef TWO_QUBIT_LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR 0.1)) [] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR[TWO_QUBIT_LEAKAGE_NOISE_FOR_AN_ADVANCED_SIMULATOR:0.1] 0 1")
  , assertShowEqual "parseNoise II_ERROR ErrorTag with args" 
      (NoiseNormal II_ERROR Nothing (Just (ErrorTag MULTIPLE_TWO_QUBIT_NOISE_MECHANISMS)) [0.1,0.2] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR[MULTIPLE_TWO_QUBIT_NOISE_MECHANISMS](0.1, 0.2) 0 1")
  , assertShowEqual "parseNoise II_ERROR ErrorTag coef with args" 
      (NoiseNormal II_ERROR Nothing (Just (ErrorTagCoef MULTIPLE_TWO_QUBIT_NOISE_MECHANISMS 0.5)) [0.1,0.2] [Q 0,Q 1]) 
      (run parseNoise "II_ERROR[MULTIPLE_TWO_QUBIT_NOISE_MECHANISMS:0.5](0.1, 0.2) 0 1")
  -- I_ERROR variants
  , assertShowEqual "parseNoise I_ERROR no args no tag" 
      (NoiseNormal I_ERROR Nothing Nothing [] [Q 0]) 
      (run parseNoise "I_ERROR 0")
  , assertShowEqual "parseNoise I_ERROR args no tag" 
      (NoiseNormal I_ERROR Nothing Nothing [0.1] [Q 0]) 
      (run parseNoise "I_ERROR(0.1) 0")
  -- CORRELATED_ERROR (NoiseE type)
  , assertShowEqual "parseNoise CORRELATED_ERROR" 
      (NoiseE CORRELATED_ERROR Nothing 0.2 [PauliInd PX 1,PauliInd PY 2]) 
      (run parseNoise "CORRELATED_ERROR(0.2) X1 Y2")
  , assertShowEqual "parseNoise E (alias)" 
      (NoiseE E Nothing 0.15 [PauliInd PZ 3]) 
      (run parseNoise "E(0.15) Z3")
  ]

-- Test AnnTy parsing
testParseAnnTy :: Test
testParseAnnTy = TestList
  [ assertShowEqual "parseAnnTy DETECTOR" DETECTOR (run parseAnnTy "DETECTOR")
  , assertShowEqual "parseAnnTy TICK" TICK (run parseAnnTy "TICK")
  , assertShowEqual "parseAnnTy SHIFT_COORDS" SHIFT_COORDS (run parseAnnTy "SHIFT_COORDS")
  , assertShowEqual "parseAnnTy QUBIT_COORDS" QUBIT_COORDS (run parseAnnTy "QUBIT_COORDS")
  ]

-- Test FInd parsing
testParseFInd :: Test
testParseFInd = TestList
  [ assertShowEqual "parseFInd Int" (In 5) (run parseFInd "5")
  , assertShowEqual "parseFInd Float" (Fl 3.14) (run parseFInd "3.14")
  , assertShowEqual "parseFInd negative Int" (In (-3)) (run parseFInd "-3")
  , assertShowEqual "parseFInd negative Float" (Fl (-2.5)) (run parseFInd "-2.5")
  ]

-- Test Ann parsing
testParseAnn :: Test
testParseAnn = TestList
  [ 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))]) 
      (run parseAnn "DETECTOR(1, 0) rec[-3] rec[-6]")
  , assertShowEqual "parseAnn DETECTOR without coords" 
      (Ann DETECTOR Nothing [] [QRec (Rec (-3)), QRec (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))]) 
      (run parseAnn "OBSERVABLE_INCLUDE(0) rec[-1]")
  ]