HABQT-0.1.0.0: src/HABQTlib/MeasurementProcessing.hs
{-|
Module : HABQTlib.MeasurementProcessing
Functions that deal with optimising and simulating measurements that take place
during tomography.
-}
module HABQTlib.MeasurementProcessing
( PurePOVM
, SingleQbParam
, measurementProbs
, svToAngles
, blochAnglesToSV
, mkAntipodalPOVM
, productPOVM
, simulateMeasuremet
, optimiseSingleQbPOVM
) where
import Control.Applicative (liftA2)
import Control.Newtype.Generics (unpack)
import Data.Complex (mkPolar, polar)
import Data.List (unfoldr)
import Data.Maybe (fromJust)
import qualified Data.Vector as V
import HABQTlib.Data
import HABQTlib.Data.Particle
import qualified Numeric.GSL as GSL
import Numeric.LinearAlgebra (Complex((:+)))
import qualified Numeric.LinearAlgebra as LA
import qualified System.Random.MWC as MWC
import System.Random.MWC.Distributions (categorical)
-- | A POVM consisting of projections onto pure states.
type PurePOVM = V.Vector WeighedPureStateVector
-- | Spherical coordinates of a pure single qubit state on Bloch sphere.
type SingleQbParam = (Double, Double)
-- | Probabilities to measure elements of POVM when performing the measurement
-- over a mixed state.
measurementProbs :: PurePOVM -> DensityMatrix -> V.Vector Double
measurementProbs povm dm = V.map (weighedProb dm) povm
weighedProb :: DensityMatrix -> WeighedPureStateVector -> Double
weighedProb dm (WeighedPureStateVector (w, sv)) = w * pureStateLikelihood sv dm
entropy :: V.Vector Double -> Double
entropy = negate . V.sum . V.map (\p -> p * log p)
povmPointEntropy :: PurePOVM -> DensityMatrix -> Double
povmPointEntropy povm dm = entropy (measurementProbs povm dm)
povmMeanEntropy :: PurePOVM -> ParticleHierarchy -> Double
povmMeanEntropy povm ph =
let meanEnt = foldOverPts (povmPointEntropy povm) (*) (+) 0
totalWeight = V.sum . V.map ptsWeight $ ph
rawEntropy = V.sum . V.map (liftA2 (*) ptsWeight meanEnt) $ ph
in rawEntropy / totalWeight
-- | Recover a state vector from spherical coordinates.
blochAnglesToSV :: SingleQbParam -> PureStateVector
blochAnglesToSV (th, phi) =
let z = LA.fromList [1, 0]
o = LA.fromList [0, 1]
in PureStateVector . LA.fromColumns . pure $
LA.scalar (cos (th / 2) :+ 0) * z +
LA.scalar (mkPolar (sin (th / 2)) phi) * o
-- | Return spherical coordinates of a single qubit pure state (on Bloch
-- sphere).
svToAngles :: PureStateVector -> Maybe SingleQbParam
svToAngles (PureStateVector sv) =
let s = LA.size sv
(m0, ph0) = polar $ LA.atIndex sv (0, 0)
(_, ph1) = polar $ LA.atIndex sv (1, 0)
ph = ph1 - ph0
th = 2 * acos m0
in if s == (2, 1)
then Just (th, ph)
else Nothing
-- | Given spherical coordinates, construct a POVM from the given vector and
-- one orthogonal to it.
mkAntipodalPOVM :: SingleQbParam -> PurePOVM
mkAntipodalPOVM c@(th, phi) =
let sv = blochAnglesToSV c
sv' = blochAnglesToSV (pi - th, phi + pi)
in V.fromList $ WeighedPureStateVector <$> [(1, sv), (1, sv')]
reshapeList :: Int -> [a] -> [[a]]
reshapeList n =
unfoldr
(\b ->
if length b < n
then Nothing
else Just (splitAt n b))
listToPairs :: [a] -> [(a, a)]
listToPairs = fmap (\(a:[b]) -> (a, b)) . reshapeList 2
pairToList :: (a, a) -> [a]
pairToList (a, b) = [a, b]
-- | Given a list of POVM measurements on sub-systems, construct a POVM over
-- the composite system that includes all of them.
productPOVM :: [PurePOVM] -> PurePOVM
productPOVM sqbPovms =
let pr ::
WeighedPureStateVector
-> WeighedPureStateVector
-> WeighedPureStateVector
pr wsv0 wsv1 =
WeighedPureStateVector (w0 * w1, PureStateVector $ LA.kronecker sv0 sv1)
where
up = fmap unpack . unpack
(w0, sv0) = up wsv0
(w1, sv1) = up wsv1
in V.foldl1' (liftA2 pr) $ V.fromList sqbPovms
-- | Approximate most informative separable POVM over a composite system of
-- quantum bits, given a list of single-qubit starting points and a particle
-- distribution.
optimiseSingleQbPOVM ::
OptIter -- ^ Number of optimisation steps to perform
-> [PureStateVector] -- ^ single qubit initial states
-> ParticleHierarchy
-> PurePOVM
optimiseSingleQbPOVM iter sv0s ph =
let nq = length sv0s
method = GSL.NMSimplex2
precision = 1e-6
iterations = iter
initialBox = replicate (2 * nq) (pi / 2)
estimateDM = getMixedEstimate ph
obj params =
negate $ povmPointEntropy povm estimateDM - povmMeanEntropy povm ph
where
povm = productPOVM . fmap mkAntipodalPOVM . listToPairs $ params
start = concatMap (pairToList . fromJust . svToAngles) sv0s
result = GSL.minimize method precision iterations initialBox obj start
in productPOVM . fmap mkAntipodalPOVM . listToPairs . fst $ result
-- | Simulate a POVM over a mixed state and return the state vector on which
-- the projection was obtained.
simulateMeasuremet ::
DensityMatrix -> PurePOVM -> MWC.GenIO -> IO PureStateVector
simulateMeasuremet dm povm gen = do
let probs = measurementProbs povm dm
svs = V.map (\(WeighedPureStateVector (_, sv)) -> sv) povm
idx <- categorical probs gen
return $ svs V.! idx