packages feed

HABQT-0.1.0.0: app/Main.hs

{-# LANGUAGE RecordWildCards #-}

module Main where

import Data.Semigroup ((<>))
import HABQTlib.Data
import HABQTlib.UnsafeAPI
import Options.Applicative
import Streaming (Of, Stream)
import qualified Streaming.Prelude as S
import qualified System.IO as IO

data CLIargs = CLIargs
  { cliQbNum :: QBitNum
  , cliRank :: Rank
  , cliMeasNum :: Int
  , cliExpNum :: Int
  , cliPtNum :: NumberOfParticles
  , cliVerb :: Int
  , cliFilePath :: IO.FilePath
  , cliMHMCiter :: MHMCiter
  , cliOptIter :: OptIter
  }

msgPositive :: String
msgPositive = "must be a positive integer"

readInt :: (Int -> Bool) -> String -> String -> Either String Int
readInt cond msg s =
  let pstr :: [(Int, String)]
      pstr = reads s
      go [(i, "")] =
        if cond i
          then Right i
          else Left msg
      go _ = Left msg
   in go pstr

readPostiveInt :: String -> Either String Int
readPostiveInt = readInt (> 0) msgPositive

positive :: ReadM Int
positive = eitherReader readPostiveInt

cliparse :: Parser CLIargs
cliparse =
  CLIargs <$>
  option
    positive
    (long "quantum-bit-number" <> short 'q' <>
     help "Number of quantum bits under tomography" <>
     showDefault <>
     value 1 <>
     metavar "QBNUM") <*>
  option
    positive
    (long "rank" <> short 'r' <> help "Rank of true states" <> showDefault <>
     value 1 <>
     metavar "RANK") <*>
  option
    positive
    (long "measurements-per-experiment" <> short 'm' <>
     help "Number of measurements to make per-experiment" <>
     showDefault <>
     value 100 <>
     metavar "MNUM") <*>
  option
    positive
    (long "experiment-number" <> short 'e' <>
     help "Number of tomography experiments to run" <>
     showDefault <>
     value 10 <>
     metavar "EXPNUM") <*>
  option
    positive
    (long "particle-number" <> short 'p' <>
     help
       "Number of particles to use for approximating the distribution over states (per rank)" <>
     showDefault <>
     value 1000 <>
     metavar "PNUM") <*>
  option
    auto
    (long "verbosity" <> short 'v' <>
     help
       "Verbosity level of output to stdout from 0 (no output) to 2 (full output)" <>
     showDefault <>
     value 2 <>
     metavar "VERB") <*>
  strOption
    (long "output-file-path" <> short 'o' <>
     help
       "Path to file which infidelities of tomographic estimates will be appended to" <>
     showDefault <>
     value "./output.txt" <>
     metavar "OUTPATH") <*>
  option
    positive
    (long "mhmc-iterations" <>
     help
       "Number of Metropolis–Hastings steps to perform when resampling (after adjusting for acceptance rate)" <>
     showDefault <>
     value 50 <>
     metavar "MHMCITER") <*>
  option
    positive
    (long "opt-iterations" <>
     help
       "Number of optimisation steps to perform when searching for optimal measurment" <>
     showDefault <>
     value 50 <>
     metavar "POVMITER")

writeCsInfid ::
     Int
  -> Int
  -> QBitNum
  -> Rank
  -> NumberOfParticles
  -> MHMCiter
  -> OptIter
  -> OutputVerb
  -> IO.FilePath
  -> IO ()
writeCsInfid numRuns numMeas qbn rank pn mi oi v fp = do
  let tomStr = streamTo numMeas (streamResults' qbn rank pn mi oi v)
      multStr = S.replicateM numRuns . tomStr
      dupIO fh s = do
        IO.hPutStr fh s
        IO.hFlush IO.stdout
  IO.withFile fp IO.AppendMode (S.effects . S.effects . multStr . dupIO)

streamTo ::
     Int
  -> Stream (Of Double) IO ()
  -> (String -> IO ())
  -> Stream (Of ()) IO ()
streamTo n is sf = do
  let rs = S.map show is
      fs = S.take 1 rs
      rest = S.drop 1 rs
  S.mapM sf . S.yield $ "\n"
  S.mapM sf fs
  S.mapM (\x -> sf (", " ++ x)) . S.take n $ rest
  S.mapM sf . S.yield $ "\n"

main :: IO ()
main = do
  CLIargs {..} <- execParser opts
  r' <-
    if cliRank > 2 ^ cliQbNum
      then do
        putStrLn "WARNING: RANK > 2 ^ QBNUM, truncating to 2 ^ QBNUM"
        return (2 ^ cliQbNum)
      else return cliRank
  v <-
    case cliVerb of
      0 -> return NoOutput
      1 -> return FidOutput
      2 -> return FullOutput
      _ -> do
        putStrLn
          "WARNING: VERB isn't equal to 0,1 or 2, defaulting to full output"
        return FullOutput
  writeCsInfid
    cliExpNum
    cliMeasNum
    cliQbNum
    r'
    cliPtNum
    cliMHMCiter
    cliOptIter
    v
    cliFilePath
  where
    opts =
      info
        (cliparse <**> helper)
        (fullDesc <>
         progDesc "Simulate HABQT and write infidelities of estimates to file" <>
         header "Hierarchical Adaptive Bayesian Quantum Tomography simulation")