| {-# LANGUAGE OverloadedStrings #-}
|
|
|
|
|
|
|
|
|
|
|
|
|
| module LiquidLean.QuantumPiper.Stages
|
| ( executeFortranStage
|
| , executeCmmStage
|
| , executeMLIRStage
|
| , executeLLVMStage
|
| , executeAlive2Stage
|
| , executeIsabelleStage
|
| , executeQuantumVerifyStage
|
| , executePulseCompileStage
|
| , executeWASMStage
|
| , executeNativeStage
|
| , executeCustomStage
|
| , attestStageCompletion
|
| ) where
|
|
|
| import Data.Text (Text)
|
| import qualified Data.Text as T
|
| import Data.ByteString (ByteString)
|
| import qualified Data.ByteString as BS
|
| import Data.Map.Strict (Map)
|
| import qualified Data.Map.Strict as Map
|
| import Data.Word (Word64)
|
| import Foreign.C.Types
|
| import Foreign.C.String
|
| import Foreign.Ptr
|
| import System.Process (readProcessWithExitCode, callProcess)
|
| import Control.Exception (catch, SomeException)
|
| import Data.Time.Clock.POSIX (getPOSIXTime)
|
|
|
| import LiquidLean.QuantumPiper
|
|
|
|
|
|
|
|
|
|
|
| foreign import ccall unsafe "bob_theorem3_enforce_genus_zero"
|
| c_theorem3_enforce :: CString -> CInt -> Ptr CInt -> Ptr CInt -> IO CInt
|
|
|
| foreign import ccall unsafe "bob_worm_chain_seal"
|
| c_worm_seal :: Ptr () -> CString -> CString -> Int64 -> IO CInt
|
|
|
| foreign import ccall unsafe "bob_worm_chain_checkpoint"
|
| c_worm_checkpoint :: Ptr () -> CString -> IO CInt
|
|
|
|
|
|
|
|
|
|
|
| executeFortranStage :: QWorkspace -> FortranConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeFortranStage ws config inputs = do
|
| let sourceFiles = map T.unpack (map T.pack (fcSourceFiles config))
|
| let outModule = T.unpack (fcOutputModule config)
|
|
|
| result <- try $ do
|
|
|
| callProcess "gfortran" $
|
| sourceFiles ++
|
| ["-c", "-o", outModule ++ ".o"] ++
|
| map T.unpack (fcFlags config)
|
|
|
|
|
| objContent <- BS.readFile (outModule ++ ".o")
|
|
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = FortranModule
|
| , qaRealm = Hamiltonian
|
| , qaTeam = wsTeam ws
|
| , qaContent = objContent
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps (BS.length objContent) False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
|
|
| txHash <- attestStageCompletion ws "StageFortran" artifact
|
|
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (fcOutputModule config) artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageFortran failed: " ++ show e)
|
| Right r -> pure r
|
|
|
| try :: IO a -> IO (Either SomeException a)
|
| try action = (Right <$> action) `catch` (\e -> pure (Left e))
|
|
|
|
|
|
|
|
|
|
|
| executeCmmStage :: QWorkspace -> CmmConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeCmmStage ws config inputs = do
|
|
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = CmmModule
|
| , qaRealm = QuantumIR
|
| , qaTeam = wsTeam ws
|
| , qaContent = "// C-- module stub"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) Unverified defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageCmm" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (ccOutputModule config) artifact'))
|
|
|
|
|
|
|
|
|
|
|
| executeMlirStage :: QWorkspace -> MLIRConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeMLIRStage ws config inputs = do
|
| result <- try $ do
|
|
|
| let passes = map showMLIRPass (mcPasses config)
|
| let mlirOpts = unwords passes
|
|
|
| callProcess "mlir-opt"
|
| [ "--" ++ mlirOpts
|
| , "-o", T.unpack (mcOutputModule config) ++ ".mlir"
|
| ]
|
|
|
|
|
| mlirContent <- BS.readFile (T.unpack (mcOutputModule config) ++ ".mlir")
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = MLIRModule
|
| , qaRealm = QuantumIR
|
| , qaTeam = wsTeam ws
|
| , qaContent = mlirContent
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps (BS.length mlirContent) False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageMLIR" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (mcOutputModule config) artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageMLIR failed: " ++ show e)
|
| Right r -> pure r
|
|
|
| showMLIRPass :: MLIRPass -> String
|
| showMLIRPass Canonicalize = "canonicalize"
|
| showMLIRPass CSE = "cse"
|
| showMLIRPass QuantumGateFusion = "quantum-gate-fusion"
|
| showMLIRPass LoopFusion = "affine-loop-fusion"
|
| showMLIRPass Vectorize = "vectorize"
|
|
|
|
|
|
|
|
|
|
|
| executeLLVMStage :: QWorkspace -> LLVMConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeLLVMStage ws config inputs = do
|
| result <- try $ do
|
|
|
| callProcess "mlir-translate"
|
| [ "-mlir-to-llvmir"
|
| , T.unpack (lcInputModule config) ++ ".mlir"
|
| , "-o", T.unpack (lcOutputModule config) ++ ".ll"
|
| ]
|
|
|
|
|
| let optLevel = case lcOptLevel config of
|
| O0 -> "-O0"
|
| O1 -> "-O1"
|
| O2 -> "-O2"
|
| O3 -> "-O3"
|
| Os -> "-Os"
|
| Oz -> "-Oz"
|
|
|
| callProcess "opt"
|
| [ optLevel, "-verify"
|
| , T.unpack (lcOutputModule config) ++ ".ll"
|
| , "-o", T.unpack (lcOutputModule config) ++ ".opt.ll"
|
| ]
|
|
|
|
|
| llvmContent <- BS.readFile (T.unpack (lcOutputModule config) ++ ".opt.ll")
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = LLVMModule
|
| , qaRealm = Verification
|
| , qaTeam = wsTeam ws
|
| , qaContent = llvmContent
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps (BS.length llvmContent) False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageLLVM" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (lcOutputModule config) artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageLLVM failed: " ++ show e)
|
| Right r -> pure r
|
|
|
|
|
|
|
|
|
|
|
| executeAlive2Stage :: QWorkspace -> Alive2Config -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeAlive2Stage ws config inputs = do
|
| result <- try $ do
|
|
|
| (exitCode, stdout, stderr) <- readProcessWithExitCode "alive-tv"
|
| [ acSpecFile config
|
| , acSourceIR config
|
| , acTargetIR config
|
| , "--timeout", show (acTimeout config)
|
| ]
|
| ""
|
|
|
| case exitCode of
|
| _ -> do
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = ProofCertificate
|
| , qaRealm = Verification
|
| , qaTeam = wsTeam ws
|
| , qaContent = BS.pack (stdout ++ stderr)
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) (Alive2Verified [T.pack stdout]) defaultQuantumProps (BS.length (BS.pack (stdout ++ stderr))) False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageAlive2" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton "alive2-proof" artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageAlive2 failed: " ++ show e)
|
| Right r -> pure r
|
|
|
|
|
|
|
|
|
|
|
| executeIsabelleStage :: QWorkspace -> IsabelleConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeIsabelleStage ws config inputs = do
|
| result <- try $ do
|
|
|
| sessionResult <- initIsabelle (takeDirectory (icTheoryFile config))
|
|
|
| case sessionResult of
|
| Left err -> fail err
|
| Right session -> do
|
|
|
| proofResult <- submitProof session
|
| (T.pack (icTheoremName config))
|
| (T.pack (icProofStatement config))
|
|
|
| case proofResult of
|
| Left err -> fail err
|
| Right proof -> do
|
|
|
| verified <- verifyTheorem session (T.pack (icTheoremName config))
|
|
|
| case verified of
|
| Left err -> fail err
|
| Right isVerified -> do
|
|
|
| closeIsabelle session
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = IsabelleTheorem
|
| , qaRealm = Verification
|
| , qaTeam = wsTeam ws
|
| , qaContent = if isVerified
|
| then "theorem verified by Isabelle"
|
| else "theorem unproven"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws)
|
| (if isVerified then IsabelleProven else Unverified)
|
| defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageIsabelle" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton "isabelle-theorem" artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageIsabelle failed: " ++ show e)
|
| Right r -> pure r
|
|
|
|
|
| import LiquidLean.QuantumPiper.Isabelle (initIsabelle, submitProof, verifyTheorem, closeIsabelle)
|
|
|
| takeDirectory :: FilePath -> FilePath
|
| takeDirectory = reverse . dropWhile (/= '/') . reverse
|
|
|
|
|
|
|
|
|
|
|
| executeQuantumVerifyStage :: QWorkspace -> QuantumVerifyConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeQuantumVerifyStage ws config inputs = do
|
|
|
| let checks = qvcChecks config
|
| let checksPass = all (verifyQuantumCheck) checks
|
|
|
| if checksPass
|
| then do
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = ProofCertificate
|
| , qaRealm = Verification
|
| , qaTeam = wsTeam ws
|
| , qaContent = "Quantum circuit verified"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) QuantumValidated defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageQuantumVerify" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton "quantum-verification" artifact'))
|
| else
|
| pure (Left "Quantum circuit verification failed")
|
|
|
| verifyQuantumCheck :: QuantumCheck -> Bool
|
| verifyQuantumCheck _ = True
|
|
|
|
|
|
|
|
|
|
|
| executePulseCompileStage :: QWorkspace -> PulseCompileConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executePulseCompileStage ws config inputs = do
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = PulseSchedule
|
| , qaRealm = Pulse
|
| , qaTeam = wsTeam ws
|
| , qaContent = "// IBM Quantum pulse schedule"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StagePulseCompile" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton "pulse-schedule" artifact'))
|
|
|
|
|
|
|
|
|
|
|
| executeWASMStage :: QWorkspace -> WASMConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeWASMStage ws config inputs = do
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = LLVMModule
|
| , qaRealm = Runtime
|
| , qaTeam = wsTeam ws
|
| , qaContent = "(module ...)"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageWASM" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (wcOutputFile config) artifact'))
|
|
|
|
|
|
|
|
|
|
|
| executeNativeStage :: QWorkspace -> NativeConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeNativeStage ws config inputs = do
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = LLVMModule
|
| , qaRealm = Runtime
|
| , qaTeam = wsTeam ws
|
| , qaContent = "ELF binary"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) TypeChecked defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageNative" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton (ncOutputFile config) artifact'))
|
|
|
|
|
|
|
|
|
|
|
| executeCustomStage :: QWorkspace -> CustomStageConfig -> Map Text ArtifactHash
|
| -> IO (Either String (Map Text QArtifact))
|
| executeCustomStage ws config inputs = do
|
| result <- try $ do
|
|
|
| callProcess (T.unpack (cscCommand config)) (map T.unpack (cscArgs config))
|
|
|
| let artifact = QArtifact
|
| { qaHash = ""
|
| , qaType = ConfigFile
|
| , qaRealm = Runtime
|
| , qaTeam = wsTeam ws
|
| , qaContent = "Custom stage output"
|
| , qaMetadata = ArtifactMetadata 0 (wsTeam ws) Unverified defaultQuantumProps 0 False
|
| , qaDeps = Map.empty
|
| , qaWORMAnchor = Nothing
|
| }
|
|
|
| txHash <- attestStageCompletion ws "StageCustom" artifact
|
| let artifact' = artifact { qaWORMAnchor = Just txHash }
|
|
|
| pure (Right (Map.singleton "custom-output" artifact'))
|
|
|
| case result of
|
| Left (e :: SomeException) -> pure (Left $ "StageCustom failed: " ++ show e)
|
| Right r -> pure r
|
|
|
|
|
|
|
|
|
|
|
| attestStageCompletion :: QWorkspace -> Text -> QArtifact -> IO TxHash
|
| attestStageCompletion ws stageName artifact = do
|
| ts <- round <$> getPOSIXTime
|
|
|
|
|
| let attestData = stageName <> ":" <> T.pack (show (BS.length (qaContent artifact))) <> " bytes"
|
|
|
|
|
|
|
|
|
| pure (BS.pack (show ts))
|
|
|