From dbfbad386b421f721d971fbc9fbcabc6b632a099 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Mon, 25 Sep 2023 15:49:00 +0900 Subject: [PATCH 01/15] Add Aer simulator target --- CMakeLists.txt | 12 + targets/simulators/aer/AerSimulator.cpp | 425 +++++++++++++++ targets/simulators/aer/AerSimulator.h | 105 ++++ targets/simulators/aer/CMakeLists.txt | 59 +++ .../simulators/aer/Conversion/QUIRToAer.cpp | 500 ++++++++++++++++++ targets/simulators/aer/Conversion/QUIRToAer.h | 46 ++ .../aer/Conversion/TypeConversion.cpp | 110 ++++ .../aer/Conversion/TypeConversion.h | 57 ++ targets/simulators/aer/Target.inc | 33 ++ .../Transforms/OutputClassicalRegisters.cpp | 150 ++++++ .../aer/Transforms/OutputClassicalRegisters.h | 52 ++ targets/simulators/aer/test/CMakeLists.txt | 17 + .../aer/test/Conversion/bell-quir-to-aer.mlir | 93 ++++ .../simulators/aer/test/Conversion/bell.qasm | 45 ++ .../aer/test/Conversion/output-cregs.mlir | 85 +++ .../test/Conversion/teleport-quir-to-aer.mlir | 121 +++++ .../aer/test/Conversion/teleport.qasm | 62 +++ targets/simulators/aer/test/lit.local.cfg.py | 17 + .../aer/test/python_lib/conftest.py | 106 ++++ .../simulators/aer/test/python_lib/pytest.ini | 14 + .../aer/test/python_lib/test_compile.py | 264 +++++++++ targets/simulators/aer/test/test.cfg | 5 + 22 files changed, 2378 insertions(+) create mode 100644 targets/simulators/aer/AerSimulator.cpp create mode 100644 targets/simulators/aer/AerSimulator.h create mode 100644 targets/simulators/aer/CMakeLists.txt create mode 100644 targets/simulators/aer/Conversion/QUIRToAer.cpp create mode 100644 targets/simulators/aer/Conversion/QUIRToAer.h create mode 100644 targets/simulators/aer/Conversion/TypeConversion.cpp create mode 100644 targets/simulators/aer/Conversion/TypeConversion.h create mode 100644 targets/simulators/aer/Target.inc create mode 100644 targets/simulators/aer/Transforms/OutputClassicalRegisters.cpp create mode 100644 targets/simulators/aer/Transforms/OutputClassicalRegisters.h create mode 100644 targets/simulators/aer/test/CMakeLists.txt create mode 100644 targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir create mode 100644 targets/simulators/aer/test/Conversion/bell.qasm create mode 100644 targets/simulators/aer/test/Conversion/output-cregs.mlir create mode 100644 targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir create mode 100644 targets/simulators/aer/test/Conversion/teleport.qasm create mode 100644 targets/simulators/aer/test/lit.local.cfg.py create mode 100644 targets/simulators/aer/test/python_lib/conftest.py create mode 100644 targets/simulators/aer/test/python_lib/pytest.ini create mode 100644 targets/simulators/aer/test/python_lib/test_compile.py create mode 100644 targets/simulators/aer/test/test.cfg diff --git a/CMakeLists.txt b/CMakeLists.txt index a411ca668..c7fea8e2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,7 @@ set(QSSC_RESOURCES_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/resourc set(QSSC_PLUGIN_EXT ${CMAKE_SHARED_LIBRARY_SUFFIX}) option(QSSC_WITH_MOCK_TARGET "Available targets include the built-in mock target" ON) +option(QSSC_WITH_AER_SIMULATOR_TARGET "Available targets include the built-in aer-simulator target" ON) set(QSSC_TARGET_DIRS "" CACHE STRING @@ -154,6 +155,17 @@ if(QSSC_WITH_MOCK_TARGET) ) endif() +if(QSSC_WITH_AER_SIMULATOR_TARGET) + set(QSSC_TARGET_DIRS + ${QSSC_TARGET_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/targets/simulators/aer + ) + set(QSSC_TARGET_TEST_DIRS + ${QSSC_TARGET_TEST_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/targets/simulators/aer/test + ) +endif() + set(QSSC_PAYLOAD_PATHS "" CACHE STRING diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp new file mode 100644 index 000000000..6866c9f61 --- /dev/null +++ b/targets/simulators/aer/AerSimulator.cpp @@ -0,0 +1,425 @@ +//===- AerSimulator.cpp -----------------------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// + +#include "AerSimulator.h" + +#include + +#include "Conversion/QUIRToAer.h" +#include "Conversion/QUIRToLLVM/QUIRToLLVM.h" +#include "Transforms/OutputClassicalRegisters.h" + +#include "Dialect/QUIR/Transforms/Passes.h" +#include "HAL/TargetSystemRegistry.h" +#include "Payload/Payload.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Target/TargetMachine.h" + +#include "mlir/Conversion/Passes.h" +#include "mlir/Dialect/LLVMIR/Transforms/Passes.h" +#include "mlir/ExecutionEngine/OptUtils.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" +#include "mlir/Target/LLVMIR/Export.h" + +#include +#include + +using namespace mlir; +using namespace mlir::quir; + +using namespace qssc::hal; +using namespace qssc::targets::simulators::aer; + +using json = nlohmann::json; + +// The space below at the front of the string causes this category to be printed +// first +static llvm::cl::OptionCategory simulatorCat( + " QSS Compiler Options for the Simulator target", + "Options that control Simulator-specific behavior of the Simulator QSS " + "Compiler target"); + +namespace qssc::targets::simulators::aer { + +int init() { + bool registered = + registry::TargetSystemRegistry::registerPlugin( + "aer-simulator", + "Quantum simulator using qiskit Aer for quantum programs in " + "OpenQASM3/QUIR", + [](llvm::Optional configurationPath) + -> llvm::Expected> { + if (!configurationPath) + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "Configuration file must be specified.\n"); + + auto config = + std::make_unique(*configurationPath); + return std::make_unique(std::move(config)); + }); + return registered ? 0 : -1; +} + +const char *toStringInAer(SimulationMethod method) { + switch (method) { + case SimulationMethod::STATEVECTOR: + return "statevector"; + case SimulationMethod::DENSITY_MATRIX: + return "density_matrix"; + case SimulationMethod::MPS: + return "matrix_product_state"; + case SimulationMethod::STABILIZER: + return "stabilizer"; + case SimulationMethod::EXTENDED_STABILIZER: + return "extended_stabilizer"; + case SimulationMethod::UNITARY: + return "unitary"; + case SimulationMethod::SUPEROP: + return "superop"; + } + + assert(false && "Invalid simulation method"); + return ""; +} + +const char *toStringInAer(SimulationDevice device) { + switch (device) { + case SimulationDevice::CPU: + return "CPU"; + case SimulationDevice::GPU: + return "GPU"; + case SimulationDevice::THRUST_CPU: + return "ThrustCPU"; + } + + assert(false && "Invalid simulation device"); + return ""; +} + +const char *toStringInAer(SimulationPrecision precision) { + switch (precision) { + case SimulationPrecision::DOUBLE: + return "double"; + } + + assert(false && "Invalid simulation precision"); + return ""; +} + +} // namespace qssc::targets::simulators::aer + +const std::vector AerSimulator::childNames = {}; + +AerSimulatorConfig::AerSimulatorConfig(llvm::StringRef configurationPath) + : SystemConfiguration(), method(SimulationMethod::STATEVECTOR), + device(SimulationDevice::CPU), precision(SimulationPrecision::DOUBLE) { + std::ifstream cfgFile(configurationPath.data()); + if (!cfgFile) { + llvm::errs() << "Failed to open the configuration file: "; + llvm::errs() << configurationPath; + return; + } + + json cfg; + cfgFile >> cfg; + if (cfg.contains("method")) { + const auto cfgMethod = cfg["method"]; + if (cfgMethod == "statevector") + method = SimulationMethod::STATEVECTOR; + else if (cfgMethod == "density_matrix") + method = SimulationMethod::DENSITY_MATRIX; + else if (cfgMethod == "MPS") + method = SimulationMethod::MPS; + else if (cfgMethod == "stabilizer") + method = SimulationMethod::STABILIZER; + else if (cfgMethod == "extended_stabilizer") + method = SimulationMethod::EXTENDED_STABILIZER; + else if (cfgMethod == "unitary") + method = SimulationMethod::UNITARY; + else if (cfgMethod == "superop") + method = SimulationMethod::SUPEROP; + else { + llvm::errs() << "Unsupported Aer simulation method: " << cfgMethod.dump(); + llvm::errs() << ". Use default value.\n"; + } + } + if (cfg.contains("device")) { + const auto cfgDevice = cfg["device"]; + if (cfgDevice == "cpu" || cfgDevice == "CPU") + device = SimulationDevice::CPU; + else if (cfgDevice == "gpu" || cfgDevice == "GPU") + device = SimulationDevice::GPU; + else if (cfgDevice == "thrust_cpu") + device = SimulationDevice::THRUST_CPU; + else { + llvm::errs() << "Unsupported Aer simulation device: " << cfgDevice.dump(); + llvm::errs() << ". Use default value.\n"; + } + } + if (cfg.contains("precision")) { + const auto cfgPrecision = cfg["precision"]; + if (cfgPrecision == "double") + precision = SimulationPrecision::DOUBLE; + else { + llvm::errs() << "Unsupported Aer simulation precision: " + << cfgPrecision.dump(); + llvm::errs() << ". Use default value.\n"; + } + } +} // SimulatorConfig + +AerSimulator::AerSimulator(std::unique_ptr config) + : TargetSystem("AerSimulator", nullptr), + simulatorConfig(std::move(config)) {} // AerSimulator + +llvm::Error AerSimulator::registerTargetPasses() { + mlir::PassRegistration(); + mlir::PassRegistration(); + + return llvm::Error::success(); +} // AerSimulator::registerTargetPasses + +namespace { +void simulatorPipelineBuilder(mlir::OpPassManager &pm) { + pm.addPass(mlir::createCanonicalizerPass()); + pm.addPass(std::make_unique()); + pm.addPass(std::make_unique()); + pm.addPass(std::make_unique(false)); + pm.addPass(std::make_unique()); +} // simulatorPipelineBuilder +} // anonymous namespace + +llvm::Error AerSimulator::registerTargetPipelines() { + mlir::PassPipelineRegistration<> pipeline( + "simulator-conversion", "Run Simulator-specific conversions", + simulatorPipelineBuilder); + + return llvm::Error::success(); +} // AerSimulator::registerTargetPipelines + +llvm::Error AerSimulator::addPayloadPasses(mlir::PassManager &pm) { + if (payloadPassesFound(pm)) { + // command line specified payload conversion, + // let the user handle exactly what to add + return llvm::Error::success(); + } + + simulatorPipelineBuilder(pm); + + return llvm::Error::success(); +} // AerSimulator::addPayloadPasses + +auto AerSimulator::payloadPassesFound(mlir::PassManager &pm) -> bool { + for (auto &pass : pm.getPasses()) + if (pass.getName() == + "qssc::targets::simulator::aer::conversion::QUIRToAerPass") + return true; + return false; +} // AerSimulator::payloadPassesFound + +llvm::Error AerSimulator::addToPayload(mlir::ModuleOp &moduleOp, + qssc::payload::Payload &payload) { + buildLLVMPayload(moduleOp, payload); + + // TODO: if buildLLVMPayload failed? + return llvm::Error::success(); +} // AerSimulator::addToPayload + +void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, + payload::Payload &payload) { + + auto *context = moduleOp.getContext(); + assert(context); + + // Register LLVM dialect and all infrastructure required for translation to + // LLVM IR + mlir::registerLLVMDialectTranslation(*context); + + mlir::PassManager pm(context); + // Apply any generic pass manager command line options and run the pipeline. + mlir::applyPassManagerCLOptions(pm); + mlir::applyDefaultTimingPassManagerCLOptions(pm); + + // `OutputCRegsPass` must be applied before `VariableEliminationPass`. + // It inserts classical `oq3` instructions for printing the values + // of classical registers. These instructions will be converted into + // standard ops by `VariableEliminationPass`. + pm.addPass(std::make_unique()); + pm.addPass(std::make_unique(false)); + pm.addPass(std::make_unique()); + pm.addPass(mlir::createCanonicalizerPass()); + pm.addPass(mlir::createLowerToLLVMPass()); + pm.addPass(mlir::LLVM::createLegalizeForExportPass()); + if (failed(pm.run(moduleOp))) { + llvm::errs() << "Problems converting `Simulator` module to AER!\n"; + return; + } + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmParser(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeAllTargetMCs(); + + // Setup the machine properties for the target architecture. + // TODO: In future, it would be better to make this configurable + std::string targetTriple = llvm::sys::getDefaultTargetTriple(); + std::string errorMessage; + const auto *target = + llvm::TargetRegistry::lookupTarget(targetTriple, errorMessage); + if (!target) { + llvm::errs() << "Unable to find target: " << errorMessage << "\n"; + return; + } + + std::string cpu("generic"); + llvm::SubtargetFeatures features; + std::unique_ptr machine(target->createTargetMachine( + targetTriple, cpu, features.getString(), {}, {})); + auto dataLayout = machine->createDataLayout(); + + if (auto err = quir::translateModuleToLLVMDialect(moduleOp, dataLayout)) { + llvm::errs() << err; + return; + } + + // Build LLVM payload + llvm::LLVMContext llvmContext; + std::unique_ptr llvmModule = + mlir::translateModuleToLLVMIR(moduleOp, llvmContext); + if (!llvmModule) { + llvm::errs() << "Error converting LLVM module to LLVM IR!\n"; + llvm::errs() << moduleOp << "\n"; + return; + } + + llvmModule->setDataLayout(dataLayout); + llvmModule->setTargetTriple(targetTriple); + + // Optionally run an optimization pipeline over the llvm module. + auto optPipeline = mlir::makeOptimizingTransformer(0, 0, nullptr); + if (auto err = optPipeline(llvmModule.get())) { + llvm::errs() << "Failed to optimize LLVM IR " << err << "\n"; + return; + } + + llvm::SmallString<128> objPath; + int objFd; + if (auto err = llvm::sys::fs::createTemporaryFile("simulatorModule", "obj", + objFd, objPath)) { + llvm::errs() + << "Failed to create temporary object file for simulator module.\n"; + return; + } + auto obj = std::make_unique(objPath, objFd); + llvm::legacy::PassManager pass; + if (machine->addPassesToEmitFile(pass, obj->os(), nullptr, + llvm::CodeGenFileType::CGFT_ObjectFile)) { + llvm::errs() << "Cannot emit object files with TargetMachine.\n"; + return; + } + pass.run(*llvmModule); + obj->os().flush(); + + // Link the generated obj with a dynamic library of qiskit Aer + char *LD = getenv("LD_PATH"); + char *AERLIB = getenv("LIBAER_PATH"); + + llvm::SmallString<128> outputPath; + if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "out", + outputPath)) + return; + + llvm::SmallVector lld_argv{"ld", objPath, AERLIB, "-o", + outputPath}; + + llvm::SmallString<128> stdErrPath; + if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "err", + stdErrPath)) + return; + + llvm::Optional redirects[] = { + {""}, {""}, llvm::StringRef(stdErrPath)}; + + if (auto err = callTool(LD, lld_argv, redirects, true)) { + auto bufOrError = llvm::MemoryBuffer::getFile(stdErrPath); + if (!bufOrError) { + llvm::errs() << "call linker error: " << bufOrError.getError().message() + << ", ret=" << err; + } else { + llvm::errs() << "call linker error: ret=" << err; + } + return; + } + + std::ifstream output(outputPath.c_str(), std::ios_base::binary); + if (!output) { + llvm::errs() << "Failed to open generated simulator object file " + << outputPath; + return; + } + + std::string outputContents{std::istreambuf_iterator(output), + std::istreambuf_iterator()}; + + payload.getFile("simulator.bin")->assign(std::move(outputContents)); + +} // AerSimulator::buildLLVMPayload + +llvm::Error AerSimulator::callTool( + llvm::StringRef program, llvm::ArrayRef args, + llvm::ArrayRef> redirects, bool dumpArgs) { + + if (dumpArgs) { + llvm::errs() << "Calling " << program << " with args"; + for (auto const ¶m : args) + llvm::errs() << " " << param; + llvm::errs() << "\n"; + } + + std::string executeError; + int ret = + llvm::sys::ExecuteAndWait(program, args, /* environment */ llvm::None, + redirects, 0, 0, &executeError); + + if (ret < 0 || executeError.size() > 0) + return llvm::createStringError(llvm::inconvertibleErrorCode(), + llvm::Twine("Failed to execute ") + program + + " " + executeError); + + if (ret == 0) + return llvm::Error::success(); + else + return llvm::createStringError( + std::error_code{ret, std::generic_category()}, + "%*s failed with return code %d", program.size(), program.data(), ret); +} // callTool diff --git a/targets/simulators/aer/AerSimulator.h b/targets/simulators/aer/AerSimulator.h new file mode 100644 index 000000000..758f4804c --- /dev/null +++ b/targets/simulators/aer/AerSimulator.h @@ -0,0 +1,105 @@ +//===- AerSimulator.h - Simulator target info -------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file declares the classes for the a Simulator target interface +// +//===----------------------------------------------------------------------===// +#ifndef HAL_TARGETS_AER_SIMULATOR_H +#define HAL_TARGETS_AER_SIMULATOR_H + +#include "HAL/SystemConfiguration.h" +#include "HAL/TargetSystem.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include +#include + +namespace qssc::targets::simulators::aer { + +// Register the Aer simulator target. +int init(); + +// "method" : +enum class SimulationMethod { + STATEVECTOR, // "statevector" + DENSITY_MATRIX, // "density_matrix" + MPS, // "MPS" + STABILIZER, // "stabilizer" + EXTENDED_STABILIZER, // "extended_stabilizer" + UNITARY, // "unitary" + SUPEROP, // "superop" +}; + +// "device" : +enum class SimulationDevice { + CPU, // "cpu" + GPU, // "gpu" + THRUST_CPU, // "thrust_cpu" +}; + +// "precision" : +enum class SimulationPrecision { + DOUBLE, // "double" +}; + +const char *toStringInAer(SimulationMethod method); +const char *toStringInAer(SimulationDevice device); +const char *toStringInAer(SimulationPrecision precision); + +class AerSimulatorConfig : public qssc::hal::SystemConfiguration { +public: + explicit AerSimulatorConfig(llvm::StringRef configurationPath); + + SimulationMethod getMethod() const { return method; } + SimulationDevice getDevice() const { return device; } + SimulationPrecision getPrecision() const { return precision; } + +private: + SimulationMethod method; + SimulationDevice device; + SimulationPrecision precision; +}; // class AerSimulatorConfig + +class AerSimulator : public qssc::hal::TargetSystem { +public: + static constexpr auto name = "aer-simulator"; + static const std::vector childNames; + explicit AerSimulator(std::unique_ptr config); + static llvm::Error registerTargetPasses(); + static llvm::Error registerTargetPipelines(); + llvm::Error addPayloadPasses(mlir::PassManager &pm) override; + auto payloadPassesFound(mlir::PassManager &pm) -> bool; + llvm::Error addToPayload(mlir::ModuleOp &moduleOp, + payload::Payload &payload) override; + auto getConfig() -> AerSimulatorConfig & { return *simulatorConfig; } + + static llvm::Error + callTool(llvm::StringRef program, llvm::ArrayRef args, + llvm::ArrayRef> redirects, + bool dumpArgs); + +private: + std::unique_ptr simulatorConfig; + +private: + void buildLLVMPayload(mlir::ModuleOp &moduleOp, payload::Payload &payload); +}; // class AerSimulatorSystem + +} // namespace qssc::targets::simulators::aer + +#endif // HAL_TARGETS_AER_SIMULATOR_H diff --git a/targets/simulators/aer/CMakeLists.txt b/targets/simulators/aer/CMakeLists.txt new file mode 100644 index 000000000..f5dda0e1f --- /dev/null +++ b/targets/simulators/aer/CMakeLists.txt @@ -0,0 +1,59 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +get_property(qssc_api_libs GLOBAL PROPERTY QSSC_API_LIBS) + +set(llvm_code_gen_libraries + LLVMPowerPCCodeGen + LLVMPowerPCInfo + LLVMPowerPCAsmParser + LLVMPowerPCDesc + LLVMX86CodeGen + LLVMX86Info + LLVMX86AsmParser + LLVMX86Desc + LLVMX86TargetMCA +) + +# Add AArch64 libraries if present in LLVM build. +if ("LLVMAArch64CodeGen" IN_LIST LLVM_AVAILABLE_LIBS) + list(APPEND llvm_code_gen_libraries + LLVMAArch64CodeGen + LLVMAArch64Info + LLVMAArch64AsmParser + LLVMAArch64Desc + ) + message(STATUS "Adding AArch64 libraries to llvm_code_gen_libraries") +endif () + +qssc_add_plugin(QSSCTargetAerSimulator QSSC_TARGET_PLUGIN +Conversion/QUIRToAer.cpp +Conversion/TypeConversion.cpp +Transforms/OutputClassicalRegisters.cpp +AerSimulator.cpp + +ADDITIONAL_HEADER_DIRS +${CMAKE_CURRENT_SOURCE_DIR} +${QSSC_INCLUDE_DIR}/HAL + +LINK_LIBS +${qssc_api_libs} +QSSCHAL +MLIRExecutionEngine +MLIROptLib +MLIRLLVMIR +MLIRLLVMToLLVMIRTranslation +MLIRStandardOpsTransforms +${llvm_code_gen_libraries} +PLUGIN_REGISTRATION_HEADERS +${CMAKE_CURRENT_SOURCE_DIR}/Target.inc +) diff --git a/targets/simulators/aer/Conversion/QUIRToAer.cpp b/targets/simulators/aer/Conversion/QUIRToAer.cpp new file mode 100644 index 000000000..3220f2a08 --- /dev/null +++ b/targets/simulators/aer/Conversion/QUIRToAer.cpp @@ -0,0 +1,500 @@ +//===- QUIRToAER.cpp - Convert QUIR to AER --------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file implements passes for converting QUIR to AER +// +//===----------------------------------------------------------------------===// +#include "Conversion/QUIRToAer.h" +#include "Conversion/OQ3ToStandard/OQ3ToStandard.h" +#include "Conversion/QUIRToStandard/VariablesToGlobalMemRefConversion.h" +#include "Conversion/TypeConversion.h" + +#include "Dialect/OQ3/IR/OQ3Ops.h" +#include "Dialect/Pulse/IR/PulseDialect.h" +#include "Dialect/QCS/IR/QCSOps.h" +#include "Dialect/QUIR/IR/QUIRDialect.h" +#include "Dialect/QUIR/IR/QUIROps.h" +#include "Dialect/QUIR/Utils/Utils.h" + +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Dialect/SCF/SCF.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/Dialect/StandardOps/Transforms/FuncConversions.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" + +using namespace mlir; + +#include + +namespace qssc::targets::simulators::aer::conversion { + +namespace { + +// The wrapper helps a converter access the global state by generating +// `AddressOp` and `LoadOp` automatically. +class AerStateWrapper { +public: + AerStateWrapper() = default; + AerStateWrapper(LLVM::GlobalOp mem) : mem(mem) {} + + Value access(OpBuilder &builder) const { + auto addr = getAddr(builder); + return builder.create(builder.getUnknownLoc(), addr, + /*alignment=*/8); + } + + Value getAddr(OpBuilder &builder) const { + return builder.create(builder.getUnknownLoc(), mem); + } + + LLVM::GlobalOp raw() const { return mem; } + +private: + LLVM::GlobalOp mem; +}; + +AerStateWrapper aerState; +std::map aerFuncTable; + +// Declare Aer runtime API functions globally. +// The definitions of those functions are given externally by a linker. +void declareAerFunctions(ModuleOp moduleOp) { + using LLVM::LLVMFunctionType; + + aerFuncTable.clear(); + + OpBuilder builder(moduleOp); + + auto registerFunc = [&](const char *name, LLVMFunctionType ty) { + const auto loc = builder.getUnknownLoc(); + const auto f = builder.create(loc, name, ty); + aerFuncTable.insert({name, f}); + }; + + auto context = moduleOp->getContext(); + builder.setInsertionPointToStart(moduleOp.getBody()); + // common types + const auto voidType = LLVM::LLVMVoidType::get(context); + const auto i8Type = builder.getI8Type(); + const auto i64Type = builder.getI64Type(); + const auto f64Type = builder.getF64Type(); + const auto aerStateType = LLVM::LLVMPointerType::get(i8Type); + const auto strType = LLVM::LLVMPointerType::get(i8Type); + // @aer_state(...) -> i8* + const auto aerStateFunType = LLVMFunctionType::get(aerStateType, {}, true); + registerFunc("aer_state", aerStateFunType); + // @aer_state_configure(i8* noundef, i8* noundef, i8* noundef) -> void + const auto aerStateConfigureType = + LLVMFunctionType::get(voidType, {strType, strType, strType}); + registerFunc("aer_state_configure", aerStateConfigureType); + // @aer_allocate_qubits(i8* noundef, i64 noundef) -> i64 + const auto aerAllocQubitsType = + LLVMFunctionType::get(i64Type, {aerStateType, i64Type}); + registerFunc("aer_allocate_qubits", aerAllocQubitsType); + // @aer_state_initialize(i8*) -> i8* + const auto aerStateInitType = + LLVMFunctionType::get(aerStateType, {aerStateType}); + registerFunc("aer_state_initialize", aerStateInitType); + // @aer_apply_u3(i8* noundef, i64 noundef, i64 noundef, i64 noundef) -> void + const auto aerApplyU3Type = LLVMFunctionType::get( + voidType, {aerStateType, i64Type, f64Type, f64Type, f64Type}); + registerFunc("aer_apply_u3", aerApplyU3Type); + // @aer_apply_cx(i8* noundef, i64 noundef, i64 noundef) -> void + const auto aerApplyCXType = + LLVMFunctionType::get(voidType, {aerStateType, i64Type, i64Type}); + registerFunc("aer_apply_cx", aerApplyCXType); + // @aer_apply_measure(i8* noundef, i64* noundef, i64 noundef) -> i64 + const auto aerMeasType = LLVMFunctionType::get( + i64Type, {aerStateType, LLVM::LLVMPointerType::get(i64Type), i64Type}); + registerFunc("aer_apply_measure", aerMeasType); + // @aer_state_finalize(i8* noundef) -> void + const auto aerStateFinalizeType = + LLVMFunctionType::get(voidType, aerStateType); + registerFunc("aer_state_finalize", aerStateFinalizeType); +} + +// Create an Aer state globally and then wrap the state value. +void createAerState(MLIRContext *ctx, ModuleOp moduleOp) { + const auto i8Type = IntegerType::get(ctx, 8); + + OpBuilder builder(moduleOp); + builder.setInsertionPointToStart(moduleOp.getBody()); + auto aerStateTy = LLVM::LLVMPointerType::get(i8Type); + auto globalState = builder.create( + moduleOp->getLoc(), aerStateTy, /*isConstant=*/false, LLVM::Linkage::Weak, + "aer_state_handler", Attribute{}, + /*alignment=*/8); + aerState = AerStateWrapper(globalState); + + auto mainFunc = mlir::quir::getMainFunction(moduleOp); + auto mainBody = &mainFunc->getRegion(0).getBlocks().front(); + builder.setInsertionPointToStart(mainBody); + auto addr = aerState.getAddr(builder); + auto call = + builder + .create(builder.getUnknownLoc(), + aerFuncTable.at("aer_state"), ValueRange{}) + .getResult(0); + builder.create(builder.getUnknownLoc(), call, addr); +} + +// Aer C API requires an array of measured qubits. This provides a common +// array for the measurements that can avoid a stack allocation for each +// function call of the Aer measurement. +// Note that the size of this array must be large enough to perform all +// the measurements appeared in a given program. +LLVM::AllocaOp arrayForMeas; + +void insertAerStateInitialize(ModuleOp moduleOp) { + OpBuilder builder(moduleOp); + + // Insert Aer runtime initialization after qubit declarations. + // Assume that the following conditions hold: + // 1. Each qubit declaration has a unique id (e.g., {id = 0 : i32}). + // 2. The last qubit declaration has the biggest id. + std::optional lastQubitDeclOp; + moduleOp.walk([&](quir::DeclareQubitOp declOp) { + if (!lastQubitDeclOp || + lastQubitDeclOp->id().getValue() < declOp.id().getValue()) + lastQubitDeclOp = declOp; + }); + assert(lastQubitDeclOp && "At least one qubit must be declared."); + builder.setInsertionPointAfter(*lastQubitDeclOp); + auto state = aerState.access(builder); + builder.create(lastQubitDeclOp->getLoc(), + aerFuncTable.at("aer_state_initialize"), state); +} + +// Allocate an array for measurements globally. +void prepareArrayForMeas(ModuleOp moduleOp) { + OpBuilder builder(moduleOp); + + const auto i64Type = builder.getI64Type(); + auto mainFunc = mlir::quir::getMainFunction(moduleOp); + builder.setInsertionPointToStart(&mainFunc->getRegion(0).getBlocks().front()); + const int arraySize = 1; // TODO: Support multi-body measurement in future + auto arrSizeOp = builder.create( + builder.getUnknownLoc(), i64Type, + builder.getIntegerAttr(i64Type, arraySize)); + arrayForMeas = builder.create( + builder.getUnknownLoc(), LLVM::LLVMPointerType::get(i64Type), arrSizeOp, + /*alignment=*/8); +} + +} // namespace + +// Assume qcs.init is called before all quir.declare_qubit operations +struct QCSInitConversionPat : public OpConversionPattern { + explicit QCSInitConversionPat(MLIRContext *ctx, TypeConverter &typeConverter, + AerSimulatorConfig const &config) + : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1), + config(config) {} + + LogicalResult + matchAndRewrite(qcs::SystemInitOp initOp, qcs::SystemInitOp::Adaptor adapter, + ConversionPatternRewriter &rewriter) const override { + + // global string values for aer configuration + std::map globals; + const auto method = config.getMethod(); + const auto device = config.getDevice(); + const auto precision = config.getPrecision(); + const auto methodStr = toStringInAer(method); + const auto deviceStr = toStringInAer(device); + const auto precisionStr = toStringInAer(precision); + const auto config_strs = {"method", methodStr, "device", + deviceStr, "precision", precisionStr}; + for (auto config_str : config_strs) { + const auto var_name = std::string("aer_conf_") + config_str; + const auto with_null = config_str + std::string("\0", 1); + globals[config_str] = + LLVM::createGlobalString(initOp->getLoc(), rewriter, var_name, + with_null, LLVM::Linkage::Private); + } + // configure + // aer_state_configure(state, "method", ) + // aer_state_configure(state, "device", ) + // aer_state_configure(state, "precision", ) + auto state = aerState.access(rewriter); + rewriter.create( + initOp->getLoc(), aerFuncTable.at("aer_state_configure"), + ValueRange{state, globals["method"], globals[methodStr]}); + rewriter.create( + initOp->getLoc(), aerFuncTable.at("aer_state_configure"), + ValueRange{state, globals["device"], globals[deviceStr]}); + rewriter.create( + initOp->getLoc(), aerFuncTable.at("aer_state_configure"), + ValueRange{state, globals["precision"], globals[precisionStr]}); + rewriter.eraseOp(initOp); + return success(); + } + +private: + const AerSimulatorConfig config; +}; + +// Currently the simulator target does not support shot iterations. +struct RemoveQCSShotInitConversionPat + : public OpConversionPattern { + explicit RemoveQCSShotInitConversionPat(MLIRContext *ctx, + TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1) {} + + LogicalResult + matchAndRewrite(qcs::ShotInitOp initOp, qcs::ShotInitOp::Adaptor adapter, + ConversionPatternRewriter &rewriter) const override { + rewriter.eraseOp(initOp); + return success(); + } +}; + +struct FinalizeConversionPat + : public OpConversionPattern { + explicit FinalizeConversionPat(MLIRContext *ctx, TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1) {} + + LogicalResult + matchAndRewrite(qcs::SystemFinalizeOp finOp, + qcs::SystemFinalizeOp::Adaptor adapter, + ConversionPatternRewriter &rewriter) const override { + PatternRewriter::InsertionGuard insertGuard(rewriter); + rewriter.setInsertionPointAfter(finOp); + rewriter.create(rewriter.getUnknownLoc(), + aerFuncTable.at("aer_state_finalize"), + aerState.access(rewriter)); + rewriter.eraseOp(finOp); + return success(); + } +}; + +struct DeclareQubitConversionPat + : public OpConversionPattern { + using Adaptor = quir::DeclareQubitOp::Adaptor; + + explicit DeclareQubitConversionPat(MLIRContext *ctx, + TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + + LogicalResult + matchAndRewrite(quir::DeclareQubitOp op, Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + const int width = op.getType().dyn_cast().getWidth(); + assert(width == 1 && "Multi qubit declaration has not supported yet."); + + const auto sizeAttr = rewriter.getIntegerAttr(rewriter.getI64Type(), width); + auto constOp = rewriter.create( + op->getLoc(), rewriter.getI64Type(), sizeAttr); + auto state = aerState.access(rewriter); + auto alloc = rewriter.create( + op->getLoc(), aerFuncTable.at("aer_allocate_qubits"), + ValueRange{state, constOp}); + rewriter.replaceOp(op, alloc.getResults()); + return success(); + } +}; + +struct BuiltinUopConversionPat : public OpConversionPattern { + explicit BuiltinUopConversionPat(MLIRContext *ctx, + TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + + LogicalResult + matchAndRewrite(quir::Builtin_UOp op, quir::Builtin_UOp::Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto state = aerState.access(rewriter); + std::vector args = {state}; + args.insert(args.end(), adaptor.getOperands().begin(), + adaptor.getOperands().end()); + rewriter.create(op.getLoc(), aerFuncTable.at("aer_apply_u3"), + args); + rewriter.eraseOp(op); + return success(); + } +}; + +struct BuiltinCXConversionPat : public OpConversionPattern { + explicit BuiltinCXConversionPat(MLIRContext *ctx, + TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + + LogicalResult + matchAndRewrite(quir::BuiltinCXOp op, quir::BuiltinCXOp::Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + auto state = aerState.access(rewriter); + std::vector args = {state}; + args.insert(args.end(), adaptor.getOperands().begin(), + adaptor.getOperands().end()); + rewriter.create(op->getLoc(), aerFuncTable.at("aer_apply_cx"), + args); + rewriter.eraseOp(op); + return success(); + } +}; + +struct MeasureOpConversionPat : public OpConversionPattern { + explicit MeasureOpConversionPat(MLIRContext *ctx, + TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + + LogicalResult + matchAndRewrite(quir::MeasureOp op, quir::MeasureOp::Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + assert(op->getNumOperands() == 1 && + "Multi-body measurement have not been supported yet."); + const auto i64Type = rewriter.getI64Type(); + const unsigned arrSize = 1; // TODO + const IntegerAttr arraySizeAttr = rewriter.getIntegerAttr(i64Type, arrSize); + const auto qubit = *adaptor.getOperands().begin(); + auto arrSizeOp = rewriter.create(op->getLoc(), i64Type, + arraySizeAttr); + rewriter.create(op->getLoc(), qubit, arrayForMeas); + auto state = aerState.access(rewriter); + auto meas = rewriter.create( + op->getLoc(), aerFuncTable.at("aer_apply_measure"), + ValueRange{state, arrayForMeas.getResult(), arrSizeOp}); + auto casted = rewriter.create( + op->getLoc(), meas.getResult(0), rewriter.getI1Type()); + rewriter.replaceOp(op, casted.getResult()); + + return success(); + } +}; + +struct ConstConversionPat : public OpConversionPattern { + explicit ConstConversionPat(MLIRContext *ctx, TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, 1) {} + + LogicalResult + matchAndRewrite(quir::ConstantOp op, quir::ConstantOp::Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + if (auto angleAttr = op.value().dyn_cast()) { + rewriter.setInsertionPointAfter(op); + const auto angle = angleAttr.getValue().convertToDouble(); + const auto fType = rewriter.getF64Type(); + FloatAttr fAttr = rewriter.getFloatAttr(fType, angle); + auto constOp = + rewriter.create(op->getLoc(), fType, fAttr); + rewriter.replaceOp(op, {constOp}); + } else if (op.value().isa()) { + rewriter.eraseOp(op); + } + return success(); + } +}; + +template +struct RemoveConversionPat : public OpConversionPattern { + using Adaptor = typename Op::Adaptor; + + explicit RemoveConversionPat(MLIRContext *ctx, TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, 1) {} + + LogicalResult + matchAndRewrite(Op op, Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + rewriter.eraseOp(op); + return success(); + } +}; + +// TDOO: Supporting custom gates is future work. +struct FunctionConversionPat : public OpConversionPattern { + explicit FunctionConversionPat(MLIRContext *ctx, TypeConverter &typeConverter) + : OpConversionPattern(typeConverter, ctx, 1) {} + + LogicalResult + matchAndRewrite(mlir::FuncOp funcOp, mlir::FuncOp::Adaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + // Assume: funcOp != mainFunc + if (funcOp.getName() == "main") + return success(); + + rewriter.eraseOp(funcOp); + return success(); + } +}; + +void conversion::QUIRToAERPass::getDependentDialects( + DialectRegistry ®istry) const { + registry.insert(); +} + +void QUIRToAERPass::runOnOperation(AerSimulator &system) { + ModuleOp moduleOp = getOperation(); + const auto simulatorConfig = system.getConfig(); + + // First remove all arguments from synchronization ops + moduleOp->walk([](qcs::SynchronizeOp synchOp) { + synchOp.qubitsMutable().assign(ValueRange({})); + }); + + AerTypeConverter typeConverter; + auto *context = &getContext(); + ConversionTarget target(*context); + + target.addLegalDialect(); + target + .addIllegalDialect(); + target.addDynamicallyLegalOp( + [&](FuncOp op) { return typeConverter.isSignatureLegal(op.getType()); }); + + RewritePatternSet patterns(context); + populateFunctionOpInterfaceTypeConversionPattern(patterns, + typeConverter); + populateCallOpTypeConversionPattern(patterns, typeConverter); + oq3::populateOQ3ToStandardConversionPatterns(typeConverter, patterns); + patterns + .add, // TODO: Support noise models + RemoveConversionPat, // TODO: Support noise models + RemoveConversionPat, // TODO: Support custom gates + DeclareQubitConversionPat, ConstConversionPat, FinalizeConversionPat, + BuiltinUopConversionPat, BuiltinCXConversionPat, + MeasureOpConversionPat, FunctionConversionPat>(context, + typeConverter); + patterns.add(context, typeConverter, simulatorConfig); + + // Aer initialization + declareAerFunctions(moduleOp); + createAerState(context, moduleOp); + insertAerStateInitialize(moduleOp); + prepareArrayForMeas(moduleOp); + + // With the target and rewrite patterns defined, we can now attempt the + // conversion. The conversion will signal failure if any of our `illegal` + // operations were not converted successfully. + if (failed(applyPartialConversion(moduleOp, target, std::move(patterns)))) + llvm::outs() << "Failed applyPartialConversion\n"; +} // QUIRToStdPass::runOnOperation() + +llvm::StringRef QUIRToAERPass::getArgument() const { + return "simulator-quir-to-aer"; +} + +llvm::StringRef QUIRToAERPass::getDescription() const { + return "Convert QUIR ops to aer"; +} + +} // namespace qssc::targets::simulators::aer::conversion diff --git a/targets/simulators/aer/Conversion/QUIRToAer.h b/targets/simulators/aer/Conversion/QUIRToAer.h new file mode 100644 index 000000000..d67328004 --- /dev/null +++ b/targets/simulators/aer/Conversion/QUIRToAer.h @@ -0,0 +1,46 @@ +//===- QUIRToAer.h - Convert QUIR to Aer Dialect ----------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file declares the pass for converting QUIR to Aer +// +//===----------------------------------------------------------------------===// + +#ifndef SIMULATOR_AER_CONVERSION_QUIR_TO_AER_H +#define SIMULATOR_AER_CONVERSION_QUIR_TO_AER_H + +#include "AerSimulator.h" + +#include "HAL/TargetOperationPass.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Pass/Pass.h" + +namespace qssc::targets::simulators::aer::conversion { +struct QUIRToAERPass + : public mlir::PassWrapper< + QUIRToAERPass, + hal::TargetOperationPass> { + void runOnOperation(AerSimulator &system) override; + void getDependentDialects(mlir::DialectRegistry ®istry) const override; + + QUIRToAERPass() : PassWrapper() {} + + llvm::StringRef getArgument() const override; + llvm::StringRef getDescription() const override; +}; +} // namespace qssc::targets::simulators::aer::conversion + +#endif // SIMULATOR_AER_CONVERSION_QUIR_TO_AER_H diff --git a/targets/simulators/aer/Conversion/TypeConversion.cpp b/targets/simulators/aer/Conversion/TypeConversion.cpp new file mode 100644 index 000000000..8eb5d9f53 --- /dev/null +++ b/targets/simulators/aer/Conversion/TypeConversion.cpp @@ -0,0 +1,110 @@ +//===- TypeConversion.cpp - Convert QUIR types to Std -----------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +/// +/// This file implements common utilities for converting QUIR to std +/// +//===----------------------------------------------------------------------===// + +#include "Conversion/TypeConversion.h" +#include "Dialect/OQ3/IR/OQ3Ops.h" +#include "Dialect/QUIR/IR/QUIROps.h" + +namespace qssc::targets::simulators::aer { + +using namespace mlir; + +namespace { +Optional convertCBitType(quir::CBitType t) { + + if (t.getWidth() <= 64) + return IntegerType::get(t.getContext(), t.getWidth()); + + return llvm::None; +} + +Optional legalizeIndexType(mlir::IndexType t) { return t; } +} // anonymous namespace + +AerTypeConverter::AerTypeConverter() { + addConversion(convertQubitType); + addConversion(convertAngleType); + addConversion(convertDurationType); + addSourceMaterialization(qubitSourceMaterialization); + addSourceMaterialization(angleSourceMaterialization); + addSourceMaterialization(durationSourceMaterialization); + + addConversion(convertCBitType); + addConversion(legalizeIndexType); +} + +Optional AerTypeConverter::convertQubitType(Type t) { + if (auto qubitTy = t.dyn_cast()) + return IntegerType::get(t.getContext(), 64); + return llvm::None; +} + +Optional AerTypeConverter::convertAngleType(Type t) { + auto *context = t.getContext(); + if (auto angleType = t.dyn_cast()) { + auto width = angleType.getWidth(); + + if (!width.hasValue()) { + llvm::errs() << "Cannot lower an angle with no width!\n"; + return {}; + } + return Float64Type::get(context); + } + if (auto intType = t.dyn_cast()) { + // MUST return the converted type as itself to mark legal + // for function types in func defs and calls + return intType; + } + return llvm::None; +} + +Optional AerTypeConverter::convertDurationType(Type t) { + if (auto durTy = t.dyn_cast()) + return IntegerType::get(t.getContext(), 64); + return llvm::None; +} + +Optional +AerTypeConverter::qubitSourceMaterialization(OpBuilder &builder, + quir::QubitType qType, + ValueRange values, Location loc) { + for (Value val : values) + return val; + return llvm::None; +} + +Optional AerTypeConverter::angleSourceMaterialization( + OpBuilder &builder, quir::AngleType aType, ValueRange valRange, + Location loc) { + for (Value val : valRange) { + auto castOp = builder.create(loc, aType, val); + return castOp.out(); + } // for val : valRange + return llvm::None; +} // angleSourceMaterialization + +Optional AerTypeConverter::durationSourceMaterialization( + OpBuilder &builder, quir::DurationType dType, ValueRange valRange, + Location loc) { + for (Value val : valRange) + return val; + return llvm::None; +} +} // namespace qssc::targets::simulators::aer diff --git a/targets/simulators/aer/Conversion/TypeConversion.h b/targets/simulators/aer/Conversion/TypeConversion.h new file mode 100644 index 000000000..a4ad479a6 --- /dev/null +++ b/targets/simulators/aer/Conversion/TypeConversion.h @@ -0,0 +1,57 @@ +//===- TypeConversion.h - Convert QUIR types to Std -------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file declares common utilities for converting QUIR to std +// +//===----------------------------------------------------------------------===// + +#ifndef AERTOSTD_TYPECONVERSION__H +#define AERTOSTD_TYPECONVERSION__H + +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/Transforms/DialectConversion.h" + +#include "Dialect/QUIR/IR/QUIRTypes.h" + +namespace qssc::targets::simulators::aer { + +struct AerTypeConverter : public mlir::TypeConverter { + using mlir::TypeConverter::TypeConverter; + + AerTypeConverter(); + + static mlir::Optional convertQubitType(mlir::Type t); + static mlir::Optional convertAngleType(mlir::Type t); + static mlir::Optional convertDurationType(mlir::Type t); + + static mlir::Optional + qubitSourceMaterialization(mlir::OpBuilder &builder, + mlir::quir::QubitType qType, + mlir::ValueRange valRange, mlir::Location loc); + static mlir::Optional + angleSourceMaterialization(mlir::OpBuilder &builder, + mlir::quir::AngleType aType, + mlir::ValueRange valRange, mlir::Location loc); + static mlir::Optional + durationSourceMaterialization(mlir::OpBuilder &builder, + mlir::quir::DurationType dType, + mlir::ValueRange valRange, mlir::Location loc); + +}; // struct AerTypeConverter + +} // namespace qssc::targets::simulators::aer + +#endif // AERTOSTD_TYPECONVERSION__H diff --git a/targets/simulators/aer/Target.inc b/targets/simulators/aer/Target.inc new file mode 100644 index 000000000..cfd8fe30c --- /dev/null +++ b/targets/simulators/aer/Target.inc @@ -0,0 +1,33 @@ +//===- Target.inc - Simulator target registration ---------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file defines static objects that register system targets +// with the QSS compiler core. +// +//===----------------------------------------------------------------------===// + +#ifndef HAL_TARGETS_AER_SIMULATOR_TARGET_H +#define HAL_TARGETS_AER_SIMULATOR_TARGET_H + +#include "AerSimulator.h" + +namespace qssc::targets::simulators::aer { + +[[maybe_unused]] int registrar = init(); + +} // namespace qssc::targets::simulators::aer + +#endif // HAL_TARGETS_AER_SIMULATOR_TARGET_H diff --git a/targets/simulators/aer/Transforms/OutputClassicalRegisters.cpp b/targets/simulators/aer/Transforms/OutputClassicalRegisters.cpp new file mode 100644 index 000000000..8a232ad16 --- /dev/null +++ b/targets/simulators/aer/Transforms/OutputClassicalRegisters.cpp @@ -0,0 +1,150 @@ +//===- OutputClassicalRegisters.cpp ----------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file implements passes to output classical register values +// +//===----------------------------------------------------------------------===// +#include "Transforms/OutputClassicalRegisters.h" + +#include "Dialect/OQ3/IR/OQ3Ops.h" +#include "Dialect/QCS/IR/QCSOps.h" + +#include "mlir/Dialect/Affine/IR/AffineOps.h" +#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "mlir/Dialect/MemRef/IR/MemRef.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" + +using namespace mlir; + +namespace qssc::targets::simulators::aer::transforms { + +class OutputCRegsPassImpl { +public: + OutputCRegsPassImpl() = default; + + void runOnOperation(MLIRContext *context, ModuleOp moduleOp) { + mlir::TypeConverter typeConverter; + ConversionTarget target(*context); + + prepareConversion(moduleOp); + insertOutputCRegs(moduleOp); + } + +private: + void prepareConversion(ModuleOp moduleOp) { + // Create definition table that maps classical register names -> + // defining operations. + cbitDecls.clear(); + moduleOp->walk([&](oq3::DeclareVariableOp op) { + if (auto ty = op.type().dyn_cast()) + cbitDecls.emplace_back(op.getName().str(), op); + }); + + // Declare the printf function. + // TODO: In future, the result values should be printed with another way. + OpBuilder builder(moduleOp); + builder.setInsertionPointToStart(moduleOp.getBody()); + const auto i32Ty = builder.getI32Type(); + const auto i8PtrTy = LLVM::LLVMPointerType::get(builder.getI8Type()); + const auto printfTy = + LLVM::LLVMFunctionType::get(i32Ty, i8PtrTy, /*isVarArgs=*/true); + printfFuncOp = builder.create(moduleOp->getLoc(), + "printf", printfTy); + } + + // Insert output ops where `qcf.finalize` is called. + // We do not erase `qcf.finalize` because a subsequent pass may use it + // for the translation. + void insertOutputCRegs(ModuleOp moduleOp) { + // Assume that `qcf.finalize` is called only once. + moduleOp->walk([&](qcs::SystemFinalizeOp op) { + OpBuilder builder(op); + + // Define constant strings for printing globally. + if (globalStrs.find("\n") == globalStrs.end()) { + const auto varName = std::string{"str_endline"}; + const auto value = std::string{"\n\0", 2}; + globalStrs["\n"] = LLVM::createGlobalString( + op->getLoc(), builder, varName, value, LLVM::Linkage::Private); + } + if (globalStrs.find("%d") == globalStrs.end()) { + const auto varName = std::string{"str_digit"}; + const auto value = std::string{"%d\0", 3}; + globalStrs["%d"] = LLVM::createGlobalString( + op->getLoc(), builder, varName, value, LLVM::Linkage::Private); + } + + // Print the values of classical registers in the declared order. + for (auto &[name, declOp] : cbitDecls) { + if (globalStrs.find(name) == globalStrs.end()) { + const auto varName = std::string{"str_creg_"} + name; + const auto value = std::string{" "} + name + std::string{" : \0", 4}; + globalStrs[name] = LLVM::createGlobalString( + op->getLoc(), builder, varName, value, LLVM::Linkage::Private); + } + + builder.create(op->getLoc(), printfFuncOp, + globalStrs[name]); + auto cBitTy = declOp.type().dyn_cast(); + const int width = cBitTy.getWidth(); + const auto boolTy = builder.getI1Type(); + auto loaded = + builder.create(op->getLoc(), cBitTy, name); + for (int i = width - 1; i >= 0; --i) { + auto indexAttr = builder.getIndexAttr(i); + auto bit = builder.create(op->getLoc(), boolTy, + loaded, indexAttr); + builder.create(op->getLoc(), printfFuncOp, + ValueRange{globalStrs["%d"], bit}); + } + builder.create(op->getLoc(), printfFuncOp, + globalStrs["\n"]); + } + }); + } + +private: + std::vector> cbitDecls; + std::map globalStrs; + LLVM::LLVMFuncOp printfFuncOp; +}; + +OutputCRegsPass::OutputCRegsPass() : PassWrapper() { + impl = std::make_shared(); +} + +void OutputCRegsPass::runOnOperation(AerSimulator &system) { + auto *context = &getContext(); + auto moduleOp = getOperation(); + impl->runOnOperation(context, moduleOp); +} + +void OutputCRegsPass::getDependentDialects(DialectRegistry ®istry) const { + registry.insert(); +} + +llvm::StringRef OutputCRegsPass::getArgument() const { + return "simulator-output-cregs"; +} + +llvm::StringRef OutputCRegsPass::getDescription() const { + return "Insert output ops for classical registers"; +} + +} // namespace qssc::targets::simulators::aer::transforms diff --git a/targets/simulators/aer/Transforms/OutputClassicalRegisters.h b/targets/simulators/aer/Transforms/OutputClassicalRegisters.h new file mode 100644 index 000000000..d30c5ebf0 --- /dev/null +++ b/targets/simulators/aer/Transforms/OutputClassicalRegisters.h @@ -0,0 +1,52 @@ +//===- OutputClassicalRegisters.h -------------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file declares the pass to output classical register values +// +//===----------------------------------------------------------------------===// + +#ifndef SIMULATOR_TRANSFORMS_OUTPUT_CLASSICAL_REGISTERS_H +#define SIMULATOR_TRANSFORMS_OUTPUT_CLASSICAL_REGISTERS_H + +#include "AerSimulator.h" + +#include "HAL/TargetOperationPass.h" + +#include "mlir/IR/BuiltinOps.h" +#include "mlir/Pass/Pass.h" + +namespace qssc::targets::simulators::aer::transforms { + +class OutputCRegsPassImpl; + +struct OutputCRegsPass + : public mlir::PassWrapper< + OutputCRegsPass, + hal::TargetOperationPass> { + void runOnOperation(AerSimulator &system) override; + void getDependentDialects(mlir::DialectRegistry ®istry) const override; + + OutputCRegsPass(); + + llvm::StringRef getArgument() const override; + llvm::StringRef getDescription() const override; + +private: + std::shared_ptr impl; +}; +} // namespace qssc::targets::simulators::aer::transforms + +#endif // SIMULATOR_TRANSFORMS_OUTPUT_CLASSICAL_REGISTERS_H diff --git a/targets/simulators/aer/test/CMakeLists.txt b/targets/simulators/aer/test/CMakeLists.txt new file mode 100644 index 000000000..ab8495acf --- /dev/null +++ b/targets/simulators/aer/test/CMakeLists.txt @@ -0,0 +1,17 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +qssc_add_lit_test_suite(check-aer-simulator + "Simulator Static" + "${CMAKE_CURRENT_SOURCE_DIR}" + "${LIT_TEST_EXTRA_ARGS} -v -DTEST_CFG=${CMAKE_CURRENT_SOURCE_DIR}/test.cfg" +) diff --git a/targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir b/targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir new file mode 100644 index 000000000..167ee0bbb --- /dev/null +++ b/targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir @@ -0,0 +1,93 @@ +// RUN: touch test.cfg +// RUN: echo "{}" >> text.cfg +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --simulator-quir-to-aer | FileCheck %s + +// +// This file was generated by: +// $ qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg bell.qasm \ +// $ --simulator-output-cregs --quir-eliminate-variables +// + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +module { + llvm.mlir.global private constant @str_creg_c1(" c1 : \00") + llvm.mlir.global private constant @str_creg_c0(" c0 : \00") + llvm.mlir.global private constant @str_digit("%d\00") + llvm.mlir.global private constant @str_endline("\0A\00") + llvm.func @printf(!llvm.ptr, ...) -> i32 + func @cx(%arg0: !quir.qubit<1>, %arg1: !quir.qubit<1>) { + return + } + func @main() -> i32 { + + // CHECK: {{.*}} = llvm.call @aer_state() : () -> !llvm.ptr + + %angle = quir.constant #quir.angle<1.57079632679 : !quir.angle<64>> + %angle_0 = quir.constant #quir.angle<0.000000e+00 : !quir.angle<64>> + %angle_1 = quir.constant #quir.angle<3.1415926535900001 : !quir.angle<64>> + // CHECK: %[[angle:.*]] = arith.constant 1.57079632679 : f64 + // CHECK: %[[angle0:.*]] = arith.constant 0.000000e+00 : f64 + // CHECK: %[[angle1:.*]] = arith.constant 3.1415926535900001 : f64 + %c0_i32 = arith.constant 0 : i32 + %0 = memref.alloca() : memref + %1 = memref.alloca() : memref + + // CHECK: llvm.call @aer_state_configure{{.*$}} + // CHECK-NEXT: llvm.call @aer_state_configure{{.*$}} + // CHECK-NEXT: llvm.call @aer_state_configure{{.*$}} + + qcs.init + qcs.shot_init {qcs.num_shots = 1 : i32} + %2 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> + // CHECK: %[[q0:.*]] = llvm.call @aer_allocate_qubits{{.*$}} + %3 = quir.declare_qubit {id = 1 : i32} : !quir.qubit<1> + // CHECK: %[[q1:.*]] = llvm.call @aer_allocate_qubits{{.*$}} + quir.builtin_U %2, %angle, %angle_0, %angle_1 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q0]], %[[angle]], %[[angle0]], %[[angle1]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + quir.builtin_CX %2, %3 : !quir.qubit<1>, !quir.qubit<1> + // CHECK: llvm.call @aer_apply_cx({{.*}}, %[[q0]], %[[q1]]) : (!llvm.ptr, i64, i64) -> () + %4 = quir.measure(%2) : (!quir.qubit<1>) -> i1 + // CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} + affine.store %4, %1[] : memref + %5 = quir.measure(%3) : (!quir.qubit<1>) -> i1 + // CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} + affine.store %5, %0[] : memref + %6 = llvm.mlir.addressof @str_endline : !llvm.ptr> + %7 = llvm.mlir.constant(0 : index) : i64 + %8 = llvm.getelementptr %6[%7, %7] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %9 = llvm.mlir.addressof @str_digit : !llvm.ptr> + %10 = llvm.mlir.constant(0 : index) : i64 + %11 = llvm.getelementptr %9[%10, %10] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %12 = llvm.mlir.addressof @str_creg_c0 : !llvm.ptr> + %13 = llvm.mlir.constant(0 : index) : i64 + %14 = llvm.getelementptr %12[%13, %13] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %15 = llvm.call @printf(%14) : (!llvm.ptr) -> i32 + %16 = affine.load %1[] : memref + %17 = llvm.call @printf(%11, %16) : (!llvm.ptr, i1) -> i32 + %18 = llvm.call @printf(%8) : (!llvm.ptr) -> i32 + %19 = llvm.mlir.addressof @str_creg_c1 : !llvm.ptr> + %20 = llvm.mlir.constant(0 : index) : i64 + %21 = llvm.getelementptr %19[%20, %20] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %22 = llvm.call @printf(%21) : (!llvm.ptr) -> i32 + %23 = affine.load %0[] : memref + %24 = llvm.call @printf(%11, %23) : (!llvm.ptr, i1) -> i32 + %25 = llvm.call @printf(%8) : (!llvm.ptr) -> i32 + qcs.finalize + // CHECK: llvm.call @aer_state_finalize{{.*$}} + return %c0_i32 : i32 + } +} + diff --git a/targets/simulators/aer/test/Conversion/bell.qasm b/targets/simulators/aer/test/Conversion/bell.qasm new file mode 100644 index 000000000..c38466960 --- /dev/null +++ b/targets/simulators/aer/test/Conversion/bell.qasm @@ -0,0 +1,45 @@ +// RUN: touch test.cfg +// RUN: echo "{}" >> test.cfg +// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +OPENQASM 3.0; + +gate cx control, target {} + +qubit $0; +qubit $1; +bit c0; +bit c1; +// Allocations of classical bits are moved forward +// CHECK: {{.*}} = memref.alloca() : memref +// CHECK: {{.*}} = memref.alloca() : memref +// CHECK: {{.*}} = llvm.call @aer_allocate_qubits{{.*$}} +// CHECK: {{.*}} = llvm.call @aer_allocate_qubits{{.*$}} + + + +U(1.57079632679, 0.0, 3.14159265359) $0; +cx $0, $1; +// CHECK: llvm.call @aer_apply_u3{{.*$}} +// CHECK: llvm.call @aer_apply_cx{{.*$}} + +measure $0 -> c0; +measure $1 -> c1; +// CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} +// CHECK: affine.store %{{[0-9]+}}, %{{[0-9]+}}[] : memref +// CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} +// CHECK: affine.store %{{[0-9]+}}, %{{[0-9]+}}[] : memref diff --git a/targets/simulators/aer/test/Conversion/output-cregs.mlir b/targets/simulators/aer/test/Conversion/output-cregs.mlir new file mode 100644 index 000000000..6213a5def --- /dev/null +++ b/targets/simulators/aer/test/Conversion/output-cregs.mlir @@ -0,0 +1,85 @@ +// RUN: touch test.cfg +// Use default configuration values. +// RUN: echo "{}" >> test.cfg +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --simulator-output-cregs | FileCheck %s + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +module { + oq3.declare_variable @c0 : !quir.cbit<1> + oq3.declare_variable @c1 : !quir.cbit<1> + func @cx(%arg0: !quir.qubit<1>, %arg1: !quir.qubit<1>) { + return + } + + // CHECK: llvm.func @printf(!llvm.ptr, ...) -> i32 + + func @main() -> i32 { + qcs.init + qcs.shot_init {qcs.num_shots = 1 : i32} + %0 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> + %1 = quir.declare_qubit {id = 1 : i32} : !quir.qubit<1> + %false = arith.constant false + %2 = "oq3.cast"(%false) : (i1) -> !quir.cbit<1> + oq3.variable_assign @c0 : !quir.cbit<1> = %2 + %false_0 = arith.constant false + %3 = "oq3.cast"(%false_0) : (i1) -> !quir.cbit<1> + oq3.variable_assign @c1 : !quir.cbit<1> = %3 + %angle = quir.constant #quir.angle<1.57079632679 : !quir.angle<64>> + %angle_1 = quir.constant #quir.angle<0.000000e+00 : !quir.angle<64>> + %angle_2 = quir.constant #quir.angle<3.1415926535900001 : !quir.angle<64>> + quir.builtin_U %0, %angle, %angle_1, %angle_2 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + quir.builtin_CX %0, %1 : !quir.qubit<1>, !quir.qubit<1> + %4 = quir.measure(%0) : (!quir.qubit<1>) -> i1 + oq3.cbit_assign_bit @c0<1> [0] : i1 = %4 + %5 = quir.measure(%1) : (!quir.qubit<1>) -> i1 + oq3.cbit_assign_bit @c1<1> [0] : i1 = %5 + + // CHECK: func @main() -> i32 { + // CHECK-NEXT: qcs.init + // CHECK-NEXT: qcs.shot_init {qcs.num_shots = 1 : i32} + // CHECK-NEXT: %0 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> + // CHECK-NEXT: %1 = quir.declare_qubit {id = 1 : i32} : !quir.qubit<1> + // CHECK-NEXT: %false = arith.constant false + // CHECK-NEXT: %2 = "oq3.cast"(%false) : (i1) -> !quir.cbit<1> + // CHECK-NEXT: oq3.variable_assign @c0 : !quir.cbit<1> = %2 + // CHECK-NEXT: %false_0 = arith.constant false + // CHECK-NEXT: %3 = "oq3.cast"(%false_0) : (i1) -> !quir.cbit<1> + // CHECK-NEXT: oq3.variable_assign @c1 : !quir.cbit<1> = %3 + // CHECK-NEXT: %angle = quir.constant #quir.angle<1.57079632679 : !quir.angle<64>> + // CHECK-NEXT: %angle_1 = quir.constant #quir.angle<0.000000e+00 : !quir.angle<64>> + // CHECK-NEXT: %angle_2 = quir.constant #quir.angle<3.1415926535900001 : !quir.angle<64>> + // CHECK-NEXT: quir.builtin_U %0, %angle, %angle_1, %angle_2 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + // CHECK-NEXT: quir.builtin_CX %0, %1 : !quir.qubit<1>, !quir.qubit<1> + // CHECK-NEXT: %4 = quir.measure(%0) : (!quir.qubit<1>) -> i1 + // CHECK-NEXT: oq3.cbit_assign_bit @c0<1> [0] : i1 = %4 + // CHECK-NEXT: %5 = quir.measure(%1) : (!quir.qubit<1>) -> i1 + // CHECK-NEXT: oq3.cbit_assign_bit @c1<1> [0] : i1 = %5 + + // CHECK: %[[C0:.*]] = oq3.variable_load @c0 : !quir.cbit<1> + // CHECK-NEXT: %[[RES0:.*]] = oq3.cbit_extractbit(%[[C0]] : !quir.cbit<1>) [0] : i1 + // CHECK: llvm.call @printf({{.*}}, %[[RES0]]) : (!llvm.ptr, i1) -> i32 + // CHECK: %[[C1:.*]] = oq3.variable_load @c1 : !quir.cbit<1> + // CHECK-NEXT: %[[RES1:.*]] = oq3.cbit_extractbit(%[[C1]] : !quir.cbit<1>) [0] : i1 + // CHECK: llvm.call @printf({{.*}}, %[[RES1]]) : (!llvm.ptr, i1) -> i32 + + qcs.finalize + %c0_i32 = arith.constant 0 : i32 + return %c0_i32 : i32 + // CHECK: qcs.finalize + // CHECK-NEXT: %c0_i32 = arith.constant 0 : i32 + // CHECK-NEXT: return %c0_i32 : i32 + } +} diff --git a/targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir b/targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir new file mode 100644 index 000000000..e7abd0236 --- /dev/null +++ b/targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir @@ -0,0 +1,121 @@ +// RUN: touch test.cfg +// RUN: echo "{}" >> test.cfg +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --simulator-quir-to-aer | FileCheck %s + +// +// This file was generated by: +// $ qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg teleport.qasm \ +// $ --simulator-output-cregs --quir-eliminate-variables +// + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +module { + llvm.mlir.global private constant @str_creg_c(" c : \00") + llvm.mlir.global private constant @str_digit("%d\00") + llvm.mlir.global private constant @str_endline("\0A\00") + llvm.func @printf(!llvm.ptr, ...) -> i32 + func @cx(%arg0: !quir.qubit<1>, %arg1: !quir.qubit<1>) { + return + } + func @main() -> i32 { + // CHECK: {{.*}} = llvm.call @aer_state() : () -> !llvm.ptr + + %c0_i3 = arith.constant 0 : i3 + %angle = quir.constant #quir.angle<3.000000e-01 : !quir.angle<64>> + %angle_0 = quir.constant #quir.angle<2.000000e-01 : !quir.angle<64>> + %angle_1 = quir.constant #quir.angle<1.000000e-01 : !quir.angle<64>> + %angle_2 = quir.constant #quir.angle<1.57079632679 : !quir.angle<64>> + %angle_3 = quir.constant #quir.angle<0.000000e+00 : !quir.angle<64>> + %angle_4 = quir.constant #quir.angle<3.1415926535900001 : !quir.angle<64>> + // CHECK: %[[angle:.*]] = arith.constant 3.000000e-01 : f64 + // CHECK-NEXT: %[[angle0:.*]] = arith.constant 2.000000e-01 : f64 + // CHECK-NEXT: %[[angle1:.*]] = arith.constant 1.000000e-01 : f64 + // CHECK-NEXT: %[[angle2:.*]] = arith.constant 1.57079632679 : f64 + // CHECK-NEXT: %[[angle3:.*]] = arith.constant 0.000000e+00 : f64 + // CHECK-NEXT: %[[angle4:.*]] = arith.constant 3.1415926535900001 : f64 + %c1_i32 = arith.constant 1 : i32 + %c0_i32 = arith.constant 0 : i32 + %0 = memref.alloca() : memref + + qcs.init + // CHECK: llvm.call @aer_state_configure{{.*$}} + // CHECK-NEXT: llvm.call @aer_state_configure{{.*$}} + // CHECK-NEXT: llvm.call @aer_state_configure{{.*$}} + + qcs.shot_init {qcs.num_shots = 1 : i32} + %1 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> + %2 = quir.declare_qubit {id = 1 : i32} : !quir.qubit<1> + %3 = quir.declare_qubit {id = 2 : i32} : !quir.qubit<1> + // CHECK: %[[q0:.*]] = llvm.call @aer_allocate_qubits{{.*$}} + // CHECK: %[[q1:.*]] = llvm.call @aer_allocate_qubits{{.*$}} + // CHECK: %[[q2:.*]] = llvm.call @aer_allocate_qubits{{.*$}} + quir.builtin_U %1, %angle, %angle_0, %angle_1 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + quir.builtin_U %2, %angle_2, %angle_3, %angle_4 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + quir.builtin_CX %2, %3 : !quir.qubit<1>, !quir.qubit<1> + quir.builtin_CX %1, %2 : !quir.qubit<1>, !quir.qubit<1> + quir.builtin_U %1, %angle_2, %angle_3, %angle_4 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q0]], %[[angle]], %[[angle0]], %[[angle1]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q1]], %[[angle2]], %[[angle3]], %[[angle4]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + // CHECK: llvm.call @aer_apply_cx({{.*}}, %[[q1]], %[[q2]]) : (!llvm.ptr, i64, i64) -> () + // CHECK: llvm.call @aer_apply_cx({{.*}}, %[[q0]], %[[q1]]) : (!llvm.ptr, i64, i64) -> () + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q0]], %[[angle2]], %[[angle3]], %[[angle4]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + %4 = quir.measure(%1) : (!quir.qubit<1>) -> i1 + // CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} + %5 = oq3.cbit_insertbit(%c0_i3 : i3) [0] = %4 : i3 + %6 = quir.measure(%2) : (!quir.qubit<1>) -> i1 + // CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} + %7 = oq3.cbit_insertbit(%5 : i3) [1] = %6 : i3 + affine.store %7, %0[] : memref + %8 = "oq3.cast"(%4) : (i1) -> i32 + %9 = arith.cmpi eq, %8, %c1_i32 : i32 + scf.if %9 { + // CHECK: scf.if {{.*}} { + quir.builtin_U %3, %angle_3, %angle_3, %angle_4 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q2]], %[[angle3]], %[[angle3]], %[[angle4]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + // CHECK-NEXT: } + } + %10 = "oq3.cast"(%6) : (i1) -> i32 + %11 = arith.cmpi eq, %10, %c1_i32 : i32 + scf.if %11 { + // CHECK: scf.if {{.*}} { + quir.builtin_U %3, %angle_4, %angle_3, %angle_4 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + // CHECK: llvm.call @aer_apply_u3({{.*}}, %[[q2]], %[[angle4]], %[[angle3]], %[[angle4]]) : (!llvm.ptr, i64, f64, f64, f64) -> () + // CHECK-NEXT: } + } + %12 = llvm.mlir.addressof @str_endline : !llvm.ptr> + %13 = llvm.mlir.constant(0 : index) : i64 + %14 = llvm.getelementptr %12[%13, %13] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %15 = llvm.mlir.addressof @str_digit : !llvm.ptr> + %16 = llvm.mlir.constant(0 : index) : i64 + %17 = llvm.getelementptr %15[%16, %16] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %18 = llvm.mlir.addressof @str_creg_c : !llvm.ptr> + %19 = llvm.mlir.constant(0 : index) : i64 + %20 = llvm.getelementptr %18[%19, %19] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %21 = llvm.call @printf(%20) : (!llvm.ptr) -> i32 + %22 = affine.load %0[] : memref + %23 = oq3.cbit_extractbit(%22 : i3) [2] : i1 + %24 = llvm.call @printf(%17, %23) : (!llvm.ptr, i1) -> i32 + %25 = oq3.cbit_extractbit(%22 : i3) [1] : i1 + %26 = llvm.call @printf(%17, %25) : (!llvm.ptr, i1) -> i32 + %27 = oq3.cbit_extractbit(%22 : i3) [0] : i1 + %28 = llvm.call @printf(%17, %27) : (!llvm.ptr, i1) -> i32 + %29 = llvm.call @printf(%14) : (!llvm.ptr) -> i32 + qcs.finalize + // CHECK: llvm.call @aer_state_finalize{{.*$}} + return %c0_i32 : i32 + } +} + diff --git a/targets/simulators/aer/test/Conversion/teleport.qasm b/targets/simulators/aer/test/Conversion/teleport.qasm new file mode 100644 index 000000000..786e03b99 --- /dev/null +++ b/targets/simulators/aer/test/Conversion/teleport.qasm @@ -0,0 +1,62 @@ +// RUN: touch test.cfg +// RUN: echo "{}" >> test.cfg +// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --break-reset --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +OPENQASM 3.0; + +gate cx control, target { } + +qubit $0; +qubit $1; +qubit $2; +bit[3] c; +// Allocations of classical bits are moved forward +// CHECK: {{.*}} = memref.alloca() : memref +// CHECK: {{.*}} = llvm.call @aer_allocate_qubits{{.*$}} +// CHECK: {{.*}} = llvm.call @aer_allocate_qubits{{.*$}} +// CHECK: {{.*}} = llvm.call @aer_allocate_qubits{{.*$}} + +U(0.3, 0.2, 0.1) $0; +// CHECK: llvm.call @aer_apply_u3{{.*$}} + +U(1.57079632679, 0, 3.14159265359) $1; // h $1; +cx $1, $2; +cx $0, $1; +U(1.57079632679, 0, 3.14159265359) $0; // h $0; +// CHECK: llvm.call @aer_apply_u3{{.*$}} +// CHECK: llvm.call @aer_apply_cx{{.*$}} +// CHECK: llvm.call @aer_apply_cx{{.*$}} +// CHECK: llvm.call @aer_apply_u3{{.*$}} + +c[0] = measure $0; +c[1] = measure $1; +// CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} +// CHECK: {{.*}} = llvm.call @aer_apply_measure{{.*$}} +// Consecutive store to classical registers are merged: +// CHECK: affine.store %{{[0-9]+}}, %{{[0-9]+}}[] : memref + +if (c[0]==1) { +// CHECK: scf.if %{{[0-9]+}} { + U(0, 0, 3.14159265359) $2; // z $2; + // CHECK: llvm.call @aer_apply_u3{{.*$}} +} + +if (c[1]==1) { +// CHECK: scf.if %{{[0-9]+}} { + U(3.14159265359, 0, 3.14159265359) $2; // x $2; + // CHECK: llvm.call @aer_apply_u3{{.*$}} +} diff --git a/targets/simulators/aer/test/lit.local.cfg.py b/targets/simulators/aer/test/lit.local.cfg.py new file mode 100644 index 000000000..d3c759e29 --- /dev/null +++ b/targets/simulators/aer/test/lit.local.cfg.py @@ -0,0 +1,17 @@ +# ===- lit.local.cfg.py --------------------------------------*- Python -*-===// +# +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +# +# ===----------------------------------------------------------------------===// + +config.substitutions.append(("%TEST_CFG", lit_config.params["TEST_CFG"])) diff --git a/targets/simulators/aer/test/python_lib/conftest.py b/targets/simulators/aer/test/python_lib/conftest.py new file mode 100644 index 000000000..964bc2052 --- /dev/null +++ b/targets/simulators/aer/test/python_lib/conftest.py @@ -0,0 +1,106 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Pytest configuration and fixtures +""" + +import multiprocessing +import pathlib +import pytest +import zipfile +from typing import Iterable + + +@pytest.fixture(scope="session", autouse=True) +def set_multiprocessing_start_method(): + multiprocessing.set_start_method("spawn", force=True) + + +@pytest.fixture +def example_qasm3_str(): + return """OPENQASM 3.0; + qubit $0; + bit c0; + U(1.57079632679, 0.0, 3.14159265359) $0; + measure $0 -> c0; + """ + + +def __create_tmpfile(tmp_path, source): + tmp_path = tmp_path / "example.qasm" + with open(tmp_path, "w") as tmpfile: + tmpfile.write(source) + tmpfile.flush() + return tmp_path + + +@pytest.fixture +def example_qasm3_tmpfile(tmp_path, example_qasm3_str): + return __create_tmpfile(tmp_path, example_qasm3_str) + + +@pytest.fixture +def example_invalid_qasm3_str(): + return """FOOBAR 3.0; + crambit $0; + fit c0 = ~0; + """ + + +@pytest.fixture +def example_invalid_qasm3_tmpfile(tmp_path, example_invalid_qasm3_str): + return __create_tmpfile(tmp_path, example_invalid_qasm3_str) + + +@pytest.fixture +def example_unsupported_qasm3_str(): + return """OPENQASM 3.0; + int a; + int b; + int c; + c = a + b; + """ + + +@pytest.fixture +def example_unsupported_qasm3_tmpfile(tmp_path, example_unsupported_qasm3_str): + return __create_tmpfile(tmp_path, example_unsupported_qasm3_str) + + +@pytest.fixture +def mock_config_file(): + pytest_dir = pathlib.Path(__file__).parent.resolve() + mock_test_cfg = pytest_dir.parent / "test.cfg" + assert mock_test_cfg.exists() + return str(mock_test_cfg) + + +@pytest.fixture +def check_payload(): + def check_payload_(payload_filelike, expected_files: Iterable[str] = ()): + zf = zipfile.ZipFile(payload_filelike, "r") + + # check checksums in zip file (i.e., no corruption) + first_bad_file = zf.testzip() + assert first_bad_file is None, "found corrupted file in payload: " + str(first_bad_file) + + # check that payload contains manifest + assert "manifest/manifest.json" in zf.namelist() + + for expected_file in expected_files: + assert expected_file in zf.namelist() + + return check_payload_ diff --git a/targets/simulators/aer/test/python_lib/pytest.ini b/targets/simulators/aer/test/python_lib/pytest.ini new file mode 100644 index 000000000..129050bc3 --- /dev/null +++ b/targets/simulators/aer/test/python_lib/pytest.ini @@ -0,0 +1,14 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +[pytest] +asyncio_mode = strict diff --git a/targets/simulators/aer/test/python_lib/test_compile.py b/targets/simulators/aer/test/python_lib/test_compile.py new file mode 100644 index 000000000..3f6da8562 --- /dev/null +++ b/targets/simulators/aer/test/python_lib/test_compile.py @@ -0,0 +1,264 @@ +# (C) Copyright IBM 2023. +# +# This code is part of Qiskit. +# +# This code is licensed under the Apache License, Version 2.0 with LLVM +# Exceptions. You may obtain a copy of this license in the LICENSE.txt +# file in the root directory of this source tree. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +Unit tests for the compiler API using mock targets. +""" + +import asyncio +from datetime import datetime, timedelta +import io +import os +import pytest + +from qss_compiler import ( + compile_file, + compile_file_async, + compile_str, + compile_str_async, + InputType, + OutputType, + CompileOptions, +) +from qss_compiler.exceptions import QSSCompilationFailure + +compiler_extra_args = ["--enable-circuits=false"] + + +def check_mlir_string(mlir): + assert isinstance(mlir, str) + assert "module" in mlir + assert "qcs.init" in mlir + + +def test_compile_file_to_qem(example_qasm3_tmpfile, mock_config_file, check_payload): + """Test that we can compile a file input via the interface compile_file + to a QEM payload""" + + qem = compile_file( + example_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + # QEM payload is returned as byte sequence + assert isinstance(qem, bytes) + + # check that payload is a zip file + payload_filelike = io.BytesIO(qem) + check_payload(payload_filelike) + + +def test_compile_str_to_qem(mock_config_file, example_qasm3_str, check_payload): + """Test that we can compile an OpenQASM3 string via the interface + compile_file to a QEM payload""" + + qem = compile_str( + example_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + # QEM payload is returned as byte sequence + assert isinstance(qem, bytes) + + # check that payload is a zip file + payload_filelike = io.BytesIO(qem) + check_payload(payload_filelike) + + +def test_compile_file_to_qem_file( + example_qasm3_tmpfile, mock_config_file, tmp_path, check_payload +): + """Test that we can compile a file input via the interface compile_file + to a QEM payload into a file""" + tmpfile = tmp_path / "payload.qem" + + result = compile_file( + example_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=tmpfile, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + + # no direct return + assert result is None + file_stat = os.stat(tmpfile) + assert file_stat.st_size > 0 + + with open(tmpfile, "rb") as payload: + check_payload(payload) + + +def test_compile_str_to_qem_file( + mock_config_file, tmp_path, example_qasm3_str, check_payload +): + """Test that we can compile an OpenQASM3 string via the interface + compile_file to a QEM payload in an output file""" + tmpfile = tmp_path / "payload.qem" + + result = compile_str( + example_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=tmpfile, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + + # no direct return + assert result is None + file_stat = os.stat(tmpfile) + assert file_stat.st_size > 0 + + with open(tmpfile, "rb") as payload: + check_payload(payload) + + +def test_compile_failing_str_to_qem( + mock_config_file, example_unsupported_qasm3_str, example_qasm3_str, check_payload +): + """Test that compiling an invalid OpenQASM3 string via the interface + compile_str to a QEM payload will fail and result in an empty payload.""" + + with pytest.raises(QSSCompilationFailure): + compile_str( + example_unsupported_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + + +def test_compile_failing_file_to_qem( + example_unsupported_qasm3_tmpfile, mock_config_file, tmp_path, check_payload +): + """Test that compiling an invalid file input via the interface compile_file + to a QEM payload will fail and result in an empty payload.""" + + with pytest.raises(QSSCompilationFailure): + compile_file( + example_unsupported_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + + +def test_compile_options(mock_config_file, example_qasm3_str): + """Test compilation with explicit CompileOptions construction.""" + + compile_options = CompileOptions( + input_type=InputType.QASM3, + output_type=OutputType.MLIR, + target="mock", + config_path=mock_config_file, + shot_delay=100, + num_shots=10000, + extra_args=compiler_extra_args + ["--pass-statistics"], + ) + + mlir = compile_str(example_qasm3_str, compile_options=compile_options) + check_mlir_string(mlir) + + +async def sleep_a_little(): + await asyncio.sleep(1) + return datetime.now() + + +@pytest.mark.asyncio +async def test_async_compile_str(mock_config_file, example_qasm3_str, check_payload): + """Test that async wrapper produces correct output and does not block the even loop.""" + async_compile = compile_str_async( + example_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + # Start a task that sleeps shorter than the compilation and then takes a + # timestamp. If the compilation blocks the event loop, then the timestamp + # will be delayed further than the intended sleep duration. + sleeper = asyncio.create_task(sleep_a_little()) + timestamp_launched = datetime.now() + qem = await async_compile + timestamp_sleeped = await sleeper + + sleep_duration = timestamp_sleeped - timestamp_launched + milliseconds_waited = sleep_duration / timedelta(microseconds=1000) + assert ( + milliseconds_waited <= 1100 + ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ + event loop probably got blocked!" + + # QEM payload is returned as byte sequence + assert isinstance(qem, bytes) + + # check that payload is a zip file + payload_filelike = io.BytesIO(qem) + check_payload(payload_filelike) + + +@pytest.mark.asyncio +async def test_async_compile_file( + example_qasm3_tmpfile, mock_config_file, check_payload +): + """Test that async wrapper produces correct output and does not block the even loop.""" + async_compile = compile_file_async( + example_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="mock", + config_path=mock_config_file, + extra_args=compiler_extra_args, + ) + # Start a task that sleeps shorter than the compilation and then takes a + # timestamp. If the compilation blocks the event loop, then the timestamp + # will be delayed further than the intended sleep duration. + sleeper = asyncio.create_task(sleep_a_little()) + timestamp_launched = datetime.now() + qem = await async_compile + timestamp_sleeped = await sleeper + + sleep_duration = timestamp_sleeped - timestamp_launched + milliseconds_waited = sleep_duration / timedelta(microseconds=1000) + assert ( + milliseconds_waited <= 1100 + ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ + event loop probably got blocked!" + + # QEM payload is returned as byte sequence + assert isinstance(qem, bytes) + + # check that payload is a zip file + payload_filelike = io.BytesIO(qem) + check_payload(payload_filelike) diff --git a/targets/simulators/aer/test/test.cfg b/targets/simulators/aer/test/test.cfg new file mode 100644 index 000000000..54d8e8b15 --- /dev/null +++ b/targets/simulators/aer/test/test.cfg @@ -0,0 +1,5 @@ +{ + "method" : "statevector", + "device" : "cpu", + "precision" : "double" +} \ No newline at end of file From 6ff097f2b34c8003bf5e8c4f7196bdd17039e502 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Mon, 25 Sep 2023 15:56:21 +0900 Subject: [PATCH 02/15] Update tests --- targets/simulators/aer/AerSimulator.cpp | 14 ++- targets/simulators/aer/test/CMakeLists.txt | 2 +- .../aer/test/Conversion/aer_configure.mlir | 89 +++++++++++++++++++ .../simulators/aer/test/Conversion/bell.qasm | 5 +- ...quir-to-aer.mlir => bell_quir_to_aer.mlir} | 5 +- .../{output-cregs.mlir => output_cregs.mlir} | 6 +- .../aer/test/Conversion/teleport.qasm | 5 +- ...-to-aer.mlir => teleport_quir_to_aer.mlir} | 5 +- .../simulators/aer/test/Conversion/test.cfg | 5 ++ .../python_lib/aer-simulator}/conftest.py | 18 +--- .../python_lib/aer-simulator}/pytest.ini | 0 test/python_lib/aer-simulator/test.cfg | 5 ++ .../aer-simulator/test_aer_compile.py | 66 +++++++------- 13 files changed, 154 insertions(+), 71 deletions(-) create mode 100644 targets/simulators/aer/test/Conversion/aer_configure.mlir rename targets/simulators/aer/test/Conversion/{bell-quir-to-aer.mlir => bell_quir_to_aer.mlir} (97%) rename targets/simulators/aer/test/Conversion/{output-cregs.mlir => output_cregs.mlir} (96%) rename targets/simulators/aer/test/Conversion/{teleport-quir-to-aer.mlir => teleport_quir_to_aer.mlir} (98%) create mode 100644 targets/simulators/aer/test/Conversion/test.cfg rename {targets/simulators/aer/test/python_lib => test/python_lib/aer-simulator}/conftest.py (89%) rename {targets/simulators/aer/test/python_lib => test/python_lib/aer-simulator}/pytest.ini (100%) create mode 100644 test/python_lib/aer-simulator/test.cfg rename targets/simulators/aer/test/python_lib/test_compile.py => test/python_lib/aer-simulator/test_aer_compile.py (79%) diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index 6866c9f61..5a1ffda9a 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -213,6 +213,10 @@ namespace { void simulatorPipelineBuilder(mlir::OpPassManager &pm) { pm.addPass(mlir::createCanonicalizerPass()); pm.addPass(std::make_unique()); + // `OutputCRegsPass` must be applied before `VariableEliminationPass`. + // It inserts classical `oq3` instructions for printing the values + // of classical registers. These instructions will be converted into + // standard ops by `VariableEliminationPass`. pm.addPass(std::make_unique()); pm.addPass(std::make_unique(false)); pm.addPass(std::make_unique()); @@ -221,7 +225,7 @@ void simulatorPipelineBuilder(mlir::OpPassManager &pm) { llvm::Error AerSimulator::registerTargetPipelines() { mlir::PassPipelineRegistration<> pipeline( - "simulator-conversion", "Run Simulator-specific conversions", + "aer-simulator-conversion", "Run Aer simulator specific conversions", simulatorPipelineBuilder); return llvm::Error::success(); @@ -270,14 +274,6 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, mlir::applyPassManagerCLOptions(pm); mlir::applyDefaultTimingPassManagerCLOptions(pm); - // `OutputCRegsPass` must be applied before `VariableEliminationPass`. - // It inserts classical `oq3` instructions for printing the values - // of classical registers. These instructions will be converted into - // standard ops by `VariableEliminationPass`. - pm.addPass(std::make_unique()); - pm.addPass(std::make_unique(false)); - pm.addPass(std::make_unique()); - pm.addPass(mlir::createCanonicalizerPass()); pm.addPass(mlir::createLowerToLLVMPass()); pm.addPass(mlir::LLVM::createLegalizeForExportPass()); if (failed(pm.run(moduleOp))) { diff --git a/targets/simulators/aer/test/CMakeLists.txt b/targets/simulators/aer/test/CMakeLists.txt index ab8495acf..9b4520ef2 100644 --- a/targets/simulators/aer/test/CMakeLists.txt +++ b/targets/simulators/aer/test/CMakeLists.txt @@ -11,7 +11,7 @@ # that they have been altered from the originals. qssc_add_lit_test_suite(check-aer-simulator - "Simulator Static" + "Aer Simulator Static" "${CMAKE_CURRENT_SOURCE_DIR}" "${LIT_TEST_EXTRA_ARGS} -v -DTEST_CFG=${CMAKE_CURRENT_SOURCE_DIR}/test.cfg" ) diff --git a/targets/simulators/aer/test/Conversion/aer_configure.mlir b/targets/simulators/aer/test/Conversion/aer_configure.mlir new file mode 100644 index 000000000..dd43d3663 --- /dev/null +++ b/targets/simulators/aer/test/Conversion/aer_configure.mlir @@ -0,0 +1,89 @@ +// RUN: echo "{" > %t +// RUN: echo " \"method\" : \"MPS\"," >> %t +// RUN: echo " \"device\" : \"gpu\"," >> %t +// RUN: echo " \"precision\" : \"double\"" >> %t +// RUN: echo "}" >> %t +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config %t %s --simulator-quir-to-aer | FileCheck %s + +// +// This file was generated by: +// $ qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg bell.qasm \ +// $ --simulator-output-cregs --quir-eliminate-variables +// + +// +// This code is part of Qiskit. +// +// (C) Copyright IBM 2023. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// + +module { +// CHECK: module { + llvm.mlir.global private constant @str_creg_c1(" c1 : \00") + llvm.mlir.global private constant @str_creg_c0(" c0 : \00") + llvm.mlir.global private constant @str_digit("%d\00") + llvm.mlir.global private constant @str_endline("\0A\00") + llvm.func @printf(!llvm.ptr, ...) -> i32 + func @cx(%arg0: !quir.qubit<1>, %arg1: !quir.qubit<1>) { + return + } + + // CHECK: llvm.mlir.global {{.*}}("double\00") + // CHECK-NEXT: llvm.mlir.global {{.*}}("precision\00") + // CHECK-NEXT: llvm.mlir.global {{.*}}("GPU\00") + // CHECK-NEXT: llvm.mlir.global {{.*}}("device\00") + // CHECK-NEXT: llvm.mlir.global {{.*}}("matrix_product_state\00") + // CHECK-NEXT: llvm.mlir.global {{.*}}("method\00") + + // CHECK: func @main() -> i32 { + + func @main() -> i32 { + %angle = quir.constant #quir.angle<1.57079632679 : !quir.angle<64>> + %angle_0 = quir.constant #quir.angle<0.000000e+00 : !quir.angle<64>> + %angle_1 = quir.constant #quir.angle<3.1415926535900001 : !quir.angle<64>> + %c0_i32 = arith.constant 0 : i32 + %0 = memref.alloca() : memref + %1 = memref.alloca() : memref + qcs.init + qcs.shot_init {qcs.num_shots = 1 : i32} + %2 = quir.declare_qubit {id = 0 : i32} : !quir.qubit<1> + %3 = quir.declare_qubit {id = 1 : i32} : !quir.qubit<1> + quir.builtin_U %2, %angle, %angle_0, %angle_1 : !quir.qubit<1>, !quir.angle<64>, !quir.angle<64>, !quir.angle<64> + quir.builtin_CX %2, %3 : !quir.qubit<1>, !quir.qubit<1> + %4 = quir.measure(%2) : (!quir.qubit<1>) -> i1 + affine.store %4, %1[] : memref + %5 = quir.measure(%3) : (!quir.qubit<1>) -> i1 + affine.store %5, %0[] : memref + %6 = llvm.mlir.addressof @str_endline : !llvm.ptr> + %7 = llvm.mlir.constant(0 : index) : i64 + %8 = llvm.getelementptr %6[%7, %7] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %9 = llvm.mlir.addressof @str_digit : !llvm.ptr> + %10 = llvm.mlir.constant(0 : index) : i64 + %11 = llvm.getelementptr %9[%10, %10] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %12 = llvm.mlir.addressof @str_creg_c0 : !llvm.ptr> + %13 = llvm.mlir.constant(0 : index) : i64 + %14 = llvm.getelementptr %12[%13, %13] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %15 = llvm.call @printf(%14) : (!llvm.ptr) -> i32 + %16 = affine.load %1[] : memref + %17 = llvm.call @printf(%11, %16) : (!llvm.ptr, i1) -> i32 + %18 = llvm.call @printf(%8) : (!llvm.ptr) -> i32 + %19 = llvm.mlir.addressof @str_creg_c1 : !llvm.ptr> + %20 = llvm.mlir.constant(0 : index) : i64 + %21 = llvm.getelementptr %19[%20, %20] : (!llvm.ptr>, i64, i64) -> !llvm.ptr + %22 = llvm.call @printf(%21) : (!llvm.ptr) -> i32 + %23 = affine.load %0[] : memref + %24 = llvm.call @printf(%11, %23) : (!llvm.ptr, i1) -> i32 + %25 = llvm.call @printf(%8) : (!llvm.ptr) -> i32 + qcs.finalize + return %c0_i32 : i32 + } +} + diff --git a/targets/simulators/aer/test/Conversion/bell.qasm b/targets/simulators/aer/test/Conversion/bell.qasm index c38466960..f6e6b5f0b 100644 --- a/targets/simulators/aer/test/Conversion/bell.qasm +++ b/targets/simulators/aer/test/Conversion/bell.qasm @@ -1,6 +1,5 @@ -// RUN: touch test.cfg -// RUN: echo "{}" >> test.cfg -// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s +// RUN: echo "{}" > %t +// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config %t %s --num-shots=1 --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s // // This code is part of Qiskit. diff --git a/targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir b/targets/simulators/aer/test/Conversion/bell_quir_to_aer.mlir similarity index 97% rename from targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir rename to targets/simulators/aer/test/Conversion/bell_quir_to_aer.mlir index 167ee0bbb..97faf6e7f 100644 --- a/targets/simulators/aer/test/Conversion/bell-quir-to-aer.mlir +++ b/targets/simulators/aer/test/Conversion/bell_quir_to_aer.mlir @@ -1,6 +1,5 @@ -// RUN: touch test.cfg -// RUN: echo "{}" >> text.cfg -// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --simulator-quir-to-aer | FileCheck %s +// RUN: echo "{}" > %t +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config %t %s --simulator-quir-to-aer | FileCheck %s // // This file was generated by: diff --git a/targets/simulators/aer/test/Conversion/output-cregs.mlir b/targets/simulators/aer/test/Conversion/output_cregs.mlir similarity index 96% rename from targets/simulators/aer/test/Conversion/output-cregs.mlir rename to targets/simulators/aer/test/Conversion/output_cregs.mlir index 6213a5def..da1d73621 100644 --- a/targets/simulators/aer/test/Conversion/output-cregs.mlir +++ b/targets/simulators/aer/test/Conversion/output_cregs.mlir @@ -1,7 +1,5 @@ -// RUN: touch test.cfg -// Use default configuration values. -// RUN: echo "{}" >> test.cfg -// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --simulator-output-cregs | FileCheck %s +// RUN: echo "{}" > %t +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config %t %s --simulator-output-cregs | FileCheck %s // // This code is part of Qiskit. diff --git a/targets/simulators/aer/test/Conversion/teleport.qasm b/targets/simulators/aer/test/Conversion/teleport.qasm index 786e03b99..fb84c4124 100644 --- a/targets/simulators/aer/test/Conversion/teleport.qasm +++ b/targets/simulators/aer/test/Conversion/teleport.qasm @@ -1,6 +1,5 @@ -// RUN: touch test.cfg -// RUN: echo "{}" >> test.cfg -// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --break-reset --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s +// RUN: echo "{}" > %t +// RUN: qss-compiler -X=qasm --emit=mlir --target aer-simulator --config %t %s --num-shots=1 --break-reset --simulator-output-cregs --quir-eliminate-variables --simulator-quir-to-aer | FileCheck %s // // This code is part of Qiskit. diff --git a/targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir b/targets/simulators/aer/test/Conversion/teleport_quir_to_aer.mlir similarity index 98% rename from targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir rename to targets/simulators/aer/test/Conversion/teleport_quir_to_aer.mlir index e7abd0236..389c89158 100644 --- a/targets/simulators/aer/test/Conversion/teleport-quir-to-aer.mlir +++ b/targets/simulators/aer/test/Conversion/teleport_quir_to_aer.mlir @@ -1,6 +1,5 @@ -// RUN: touch test.cfg -// RUN: echo "{}" >> test.cfg -// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config test.cfg %s --num-shots=1 --simulator-quir-to-aer | FileCheck %s +// RUN: echo "{}" > %t +// RUN: qss-compiler -X=mlir --emit=mlir --target aer-simulator --config %t %s --num-shots=1 --simulator-quir-to-aer | FileCheck %s // // This file was generated by: diff --git a/targets/simulators/aer/test/Conversion/test.cfg b/targets/simulators/aer/test/Conversion/test.cfg new file mode 100644 index 000000000..d66dd5bac --- /dev/null +++ b/targets/simulators/aer/test/Conversion/test.cfg @@ -0,0 +1,5 @@ +{ + "method" : "MPS", + "device" : "gpu", + "precision" : "double" +} diff --git a/targets/simulators/aer/test/python_lib/conftest.py b/test/python_lib/aer-simulator/conftest.py similarity index 89% rename from targets/simulators/aer/test/python_lib/conftest.py rename to test/python_lib/aer-simulator/conftest.py index 964bc2052..8ced2d549 100644 --- a/targets/simulators/aer/test/python_lib/conftest.py +++ b/test/python_lib/aer-simulator/conftest.py @@ -65,27 +65,17 @@ def example_invalid_qasm3_tmpfile(tmp_path, example_invalid_qasm3_str): return __create_tmpfile(tmp_path, example_invalid_qasm3_str) -@pytest.fixture -def example_unsupported_qasm3_str(): - return """OPENQASM 3.0; - int a; - int b; - int c; - c = a + b; - """ - - @pytest.fixture def example_unsupported_qasm3_tmpfile(tmp_path, example_unsupported_qasm3_str): return __create_tmpfile(tmp_path, example_unsupported_qasm3_str) @pytest.fixture -def mock_config_file(): +def simulator_config_file(): pytest_dir = pathlib.Path(__file__).parent.resolve() - mock_test_cfg = pytest_dir.parent / "test.cfg" - assert mock_test_cfg.exists() - return str(mock_test_cfg) + simulator_test_cfg = pytest_dir.parent / "aer-simulator/test.cfg" + assert simulator_test_cfg.exists() + return str(simulator_test_cfg) @pytest.fixture diff --git a/targets/simulators/aer/test/python_lib/pytest.ini b/test/python_lib/aer-simulator/pytest.ini similarity index 100% rename from targets/simulators/aer/test/python_lib/pytest.ini rename to test/python_lib/aer-simulator/pytest.ini diff --git a/test/python_lib/aer-simulator/test.cfg b/test/python_lib/aer-simulator/test.cfg new file mode 100644 index 000000000..54d8e8b15 --- /dev/null +++ b/test/python_lib/aer-simulator/test.cfg @@ -0,0 +1,5 @@ +{ + "method" : "statevector", + "device" : "cpu", + "precision" : "double" +} \ No newline at end of file diff --git a/targets/simulators/aer/test/python_lib/test_compile.py b/test/python_lib/aer-simulator/test_aer_compile.py similarity index 79% rename from targets/simulators/aer/test/python_lib/test_compile.py rename to test/python_lib/aer-simulator/test_aer_compile.py index 3f6da8562..949eebe0b 100644 --- a/targets/simulators/aer/test/python_lib/test_compile.py +++ b/test/python_lib/aer-simulator/test_aer_compile.py @@ -11,7 +11,7 @@ # that they have been altered from the originals. """ -Unit tests for the compiler API using mock targets. +Unit tests for the compiler API using Aer simulator targets. """ import asyncio @@ -37,10 +37,14 @@ def check_mlir_string(mlir): assert isinstance(mlir, str) assert "module" in mlir - assert "qcs.init" in mlir + assert "@aer_state" in mlir + assert "@aer_state_configure" in mlir + assert "@aer_allocate_qubits" in mlir + assert "@aer_state_initialize" in mlir + assert "@aer_state_finalize" in mlir -def test_compile_file_to_qem(example_qasm3_tmpfile, mock_config_file, check_payload): +def test_compile_file_to_qem(example_qasm3_tmpfile, simulator_config_file, check_payload): """Test that we can compile a file input via the interface compile_file to a QEM payload""" @@ -49,8 +53,8 @@ def test_compile_file_to_qem(example_qasm3_tmpfile, mock_config_file, check_payl input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) # QEM payload is returned as byte sequence @@ -61,7 +65,7 @@ def test_compile_file_to_qem(example_qasm3_tmpfile, mock_config_file, check_payl check_payload(payload_filelike) -def test_compile_str_to_qem(mock_config_file, example_qasm3_str, check_payload): +def test_compile_str_to_qem(simulator_config_file, example_qasm3_str, check_payload): """Test that we can compile an OpenQASM3 string via the interface compile_file to a QEM payload""" @@ -70,8 +74,8 @@ def test_compile_str_to_qem(mock_config_file, example_qasm3_str, check_payload): input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) # QEM payload is returned as byte sequence @@ -83,7 +87,7 @@ def test_compile_str_to_qem(mock_config_file, example_qasm3_str, check_payload): def test_compile_file_to_qem_file( - example_qasm3_tmpfile, mock_config_file, tmp_path, check_payload + example_qasm3_tmpfile, simulator_config_file, tmp_path, check_payload ): """Test that we can compile a file input via the interface compile_file to a QEM payload into a file""" @@ -94,8 +98,8 @@ def test_compile_file_to_qem_file( input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=tmpfile, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) @@ -109,7 +113,7 @@ def test_compile_file_to_qem_file( def test_compile_str_to_qem_file( - mock_config_file, tmp_path, example_qasm3_str, check_payload + simulator_config_file, tmp_path, example_qasm3_str, check_payload ): """Test that we can compile an OpenQASM3 string via the interface compile_file to a QEM payload in an output file""" @@ -120,8 +124,8 @@ def test_compile_str_to_qem_file( input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=tmpfile, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) @@ -135,7 +139,7 @@ def test_compile_str_to_qem_file( def test_compile_failing_str_to_qem( - mock_config_file, example_unsupported_qasm3_str, example_qasm3_str, check_payload + simulator_config_file, example_unsupported_qasm3_str, example_qasm3_str, check_payload ): """Test that compiling an invalid OpenQASM3 string via the interface compile_str to a QEM payload will fail and result in an empty payload.""" @@ -146,14 +150,14 @@ def test_compile_failing_str_to_qem( input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) def test_compile_failing_file_to_qem( - example_unsupported_qasm3_tmpfile, mock_config_file, tmp_path, check_payload + example_unsupported_qasm3_tmpfile, simulator_config_file, tmp_path, check_payload ): """Test that compiling an invalid file input via the interface compile_file to a QEM payload will fail and result in an empty payload.""" @@ -164,23 +168,23 @@ def test_compile_failing_file_to_qem( input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) -def test_compile_options(mock_config_file, example_qasm3_str): +def test_compile_options(simulator_config_file, example_qasm3_str): """Test compilation with explicit CompileOptions construction.""" compile_options = CompileOptions( input_type=InputType.QASM3, output_type=OutputType.MLIR, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, shot_delay=100, - num_shots=10000, - extra_args=compiler_extra_args + ["--pass-statistics"], + num_shots=1, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) mlir = compile_str(example_qasm3_str, compile_options=compile_options) @@ -193,15 +197,15 @@ async def sleep_a_little(): @pytest.mark.asyncio -async def test_async_compile_str(mock_config_file, example_qasm3_str, check_payload): +async def test_async_compile_str(simulator_config_file, example_qasm3_str, check_payload): """Test that async wrapper produces correct output and does not block the even loop.""" async_compile = compile_str_async( example_qasm3_str, input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) # Start a task that sleeps shorter than the compilation and then takes a @@ -229,7 +233,7 @@ async def test_async_compile_str(mock_config_file, example_qasm3_str, check_payl @pytest.mark.asyncio async def test_async_compile_file( - example_qasm3_tmpfile, mock_config_file, check_payload + example_qasm3_tmpfile, simulator_config_file, check_payload ): """Test that async wrapper produces correct output and does not block the even loop.""" async_compile = compile_file_async( @@ -237,8 +241,8 @@ async def test_async_compile_file( input_type=InputType.QASM3, output_type=OutputType.QEM, output_file=None, - target="mock", - config_path=mock_config_file, + target="aer-simulator", + config_path=simulator_config_file, extra_args=compiler_extra_args, ) # Start a task that sleeps shorter than the compilation and then takes a From 621a85a23e1e9995a78f1597d08d939b9ae8d104 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 13:31:41 +0900 Subject: [PATCH 03/15] Improve error infomation --- targets/simulators/aer/AerSimulator.cpp | 37 +++++++++++---------- targets/simulators/aer/AerSimulator.h | 3 +- targets/simulators/aer/CMakeLists.txt | 1 + targets/simulators/aer/Errors.cpp | 23 +++++++++++++ targets/simulators/aer/Errors.h | 43 +++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 targets/simulators/aer/Errors.cpp create mode 100644 targets/simulators/aer/Errors.h diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index 5a1ffda9a..7f2069789 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -20,6 +20,7 @@ #include "Conversion/QUIRToAer.h" #include "Conversion/QUIRToLLVM/QUIRToLLVM.h" +#include "Errors.h" #include "Transforms/OutputClassicalRegisters.h" #include "Dialect/QUIR/Transforms/Passes.h" @@ -61,8 +62,8 @@ using namespace qssc::targets::simulators::aer; using json = nlohmann::json; -// The space below at the front of the string causes this category to be printed -// first +// The space below at the front of the string causes this category to be +// printed first static llvm::cl::OptionCategory simulatorCat( " QSS Compiler Options for the Simulator target", "Options that control Simulator-specific behavior of the Simulator QSS " @@ -253,14 +254,11 @@ auto AerSimulator::payloadPassesFound(mlir::PassManager &pm) -> bool { llvm::Error AerSimulator::addToPayload(mlir::ModuleOp &moduleOp, qssc::payload::Payload &payload) { - buildLLVMPayload(moduleOp, payload); - - // TODO: if buildLLVMPayload failed? - return llvm::Error::success(); + return buildLLVMPayload(moduleOp, payload); } // AerSimulator::addToPayload -void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, - payload::Payload &payload) { +llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, + payload::Payload &payload) { auto *context = moduleOp.getContext(); assert(context); @@ -278,7 +276,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, pm.addPass(mlir::LLVM::createLegalizeForExportPass()); if (failed(pm.run(moduleOp))) { llvm::errs() << "Problems converting `Simulator` module to AER!\n"; - return; + return llvm::make_error(); } llvm::InitializeNativeTarget(); @@ -294,7 +292,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, llvm::TargetRegistry::lookupTarget(targetTriple, errorMessage); if (!target) { llvm::errs() << "Unable to find target: " << errorMessage << "\n"; - return; + return llvm::make_error(); } std::string cpu("generic"); @@ -305,7 +303,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, if (auto err = quir::translateModuleToLLVMDialect(moduleOp, dataLayout)) { llvm::errs() << err; - return; + return llvm::make_error(); } // Build LLVM payload @@ -315,7 +313,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, if (!llvmModule) { llvm::errs() << "Error converting LLVM module to LLVM IR!\n"; llvm::errs() << moduleOp << "\n"; - return; + return llvm::make_error(); } llvmModule->setDataLayout(dataLayout); @@ -325,7 +323,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, auto optPipeline = mlir::makeOptimizingTransformer(0, 0, nullptr); if (auto err = optPipeline(llvmModule.get())) { llvm::errs() << "Failed to optimize LLVM IR " << err << "\n"; - return; + return llvm::make_error(); } llvm::SmallString<128> objPath; @@ -334,14 +332,14 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, objFd, objPath)) { llvm::errs() << "Failed to create temporary object file for simulator module.\n"; - return; + return llvm::make_error(); } auto obj = std::make_unique(objPath, objFd); llvm::legacy::PassManager pass; if (machine->addPassesToEmitFile(pass, obj->os(), nullptr, llvm::CodeGenFileType::CGFT_ObjectFile)) { llvm::errs() << "Cannot emit object files with TargetMachine.\n"; - return; + return llvm::make_error(); } pass.run(*llvmModule); obj->os().flush(); @@ -353,7 +351,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, llvm::SmallString<128> outputPath; if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "out", outputPath)) - return; + return llvm::make_error(); llvm::SmallVector lld_argv{"ld", objPath, AERLIB, "-o", outputPath}; @@ -361,7 +359,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, llvm::SmallString<128> stdErrPath; if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "err", stdErrPath)) - return; + return llvm::make_error(); llvm::Optional redirects[] = { {""}, {""}, llvm::StringRef(stdErrPath)}; @@ -374,14 +372,14 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, } else { llvm::errs() << "call linker error: ret=" << err; } - return; + return llvm::make_error(); } std::ifstream output(outputPath.c_str(), std::ios_base::binary); if (!output) { llvm::errs() << "Failed to open generated simulator object file " << outputPath; - return; + return llvm::make_error(); } std::string outputContents{std::istreambuf_iterator(output), @@ -389,6 +387,7 @@ void AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, payload.getFile("simulator.bin")->assign(std::move(outputContents)); + return llvm::Error::success(); } // AerSimulator::buildLLVMPayload llvm::Error AerSimulator::callTool( diff --git a/targets/simulators/aer/AerSimulator.h b/targets/simulators/aer/AerSimulator.h index 758f4804c..ba511b792 100644 --- a/targets/simulators/aer/AerSimulator.h +++ b/targets/simulators/aer/AerSimulator.h @@ -97,7 +97,8 @@ class AerSimulator : public qssc::hal::TargetSystem { std::unique_ptr simulatorConfig; private: - void buildLLVMPayload(mlir::ModuleOp &moduleOp, payload::Payload &payload); + llvm::Error buildLLVMPayload(mlir::ModuleOp &moduleOp, + payload::Payload &payload); }; // class AerSimulatorSystem } // namespace qssc::targets::simulators::aer diff --git a/targets/simulators/aer/CMakeLists.txt b/targets/simulators/aer/CMakeLists.txt index f5dda0e1f..c7f2770ab 100644 --- a/targets/simulators/aer/CMakeLists.txt +++ b/targets/simulators/aer/CMakeLists.txt @@ -39,6 +39,7 @@ qssc_add_plugin(QSSCTargetAerSimulator QSSC_TARGET_PLUGIN Conversion/QUIRToAer.cpp Conversion/TypeConversion.cpp Transforms/OutputClassicalRegisters.cpp +Errors.cpp AerSimulator.cpp ADDITIONAL_HEADER_DIRS diff --git a/targets/simulators/aer/Errors.cpp b/targets/simulators/aer/Errors.cpp new file mode 100644 index 000000000..40024374d --- /dev/null +++ b/targets/simulators/aer/Errors.cpp @@ -0,0 +1,23 @@ +//===- Errors.cpp -----------------------------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// + +#include "Errors.h" + +namespace qssc::targets::simulators::aer { + +char LLVMBuildFailure::ID; + +} // namespace qssc::targets::simulators::aer diff --git a/targets/simulators/aer/Errors.h b/targets/simulators/aer/Errors.h new file mode 100644 index 000000000..e7bcb74f8 --- /dev/null +++ b/targets/simulators/aer/Errors.h @@ -0,0 +1,43 @@ +//===- Errors.h -------------------------------------------------*- C++ -*-===// +// +// (C) Copyright IBM 2023. +// +// This code is part of Qiskit. +// +// This code is licensed under the Apache License, Version 2.0 with LLVM +// Exceptions. You may obtain a copy of this license in the LICENSE.txt +// file in the root directory of this source tree. +// +// Any modifications or derivative works of this code must retain this +// copyright notice, and modified files need to carry a notice indicating +// that they have been altered from the originals. +// +//===----------------------------------------------------------------------===// +// +// This file declares the classes for errors of aer-simulator +// +//===----------------------------------------------------------------------===// +#ifndef TARGETS_SIMULATORS_AER_ERRORS_H +#define TARGETS_SIMULATORS_AER_ERRORS_H + +#include + +namespace qssc::targets::simulators::aer { + +class LLVMBuildFailure : public llvm::ErrorInfo { +public: + static char ID; + + LLVMBuildFailure() {} + + void log(llvm::raw_ostream &os) const override { + os << "Failed to generate a binary file for Aer simulator"; + } + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } +}; + +} // namespace qssc::targets::simulators::aer + +#endif // TARGETS_SIMULATORS_AER_ERRORS_H From 2cdfc7afbc7743318ea4f0e611e0a70cb544c524 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 14:23:13 +0900 Subject: [PATCH 04/15] Fix tests --- test/python_lib/aer-simulator/conftest.py | 36 --- .../aer-simulator/test_aer_compile.py | 217 ++++++++---------- 2 files changed, 92 insertions(+), 161 deletions(-) diff --git a/test/python_lib/aer-simulator/conftest.py b/test/python_lib/aer-simulator/conftest.py index 8ced2d549..83dbe2661 100644 --- a/test/python_lib/aer-simulator/conftest.py +++ b/test/python_lib/aer-simulator/conftest.py @@ -52,45 +52,9 @@ def example_qasm3_tmpfile(tmp_path, example_qasm3_str): return __create_tmpfile(tmp_path, example_qasm3_str) -@pytest.fixture -def example_invalid_qasm3_str(): - return """FOOBAR 3.0; - crambit $0; - fit c0 = ~0; - """ - - -@pytest.fixture -def example_invalid_qasm3_tmpfile(tmp_path, example_invalid_qasm3_str): - return __create_tmpfile(tmp_path, example_invalid_qasm3_str) - - -@pytest.fixture -def example_unsupported_qasm3_tmpfile(tmp_path, example_unsupported_qasm3_str): - return __create_tmpfile(tmp_path, example_unsupported_qasm3_str) - - @pytest.fixture def simulator_config_file(): pytest_dir = pathlib.Path(__file__).parent.resolve() simulator_test_cfg = pytest_dir.parent / "aer-simulator/test.cfg" assert simulator_test_cfg.exists() return str(simulator_test_cfg) - - -@pytest.fixture -def check_payload(): - def check_payload_(payload_filelike, expected_files: Iterable[str] = ()): - zf = zipfile.ZipFile(payload_filelike, "r") - - # check checksums in zip file (i.e., no corruption) - first_bad_file = zf.testzip() - assert first_bad_file is None, "found corrupted file in payload: " + str(first_bad_file) - - # check that payload contains manifest - assert "manifest/manifest.json" in zf.namelist() - - for expected_file in expected_files: - assert expected_file in zf.namelist() - - return check_payload_ diff --git a/test/python_lib/aer-simulator/test_aer_compile.py b/test/python_lib/aer-simulator/test_aer_compile.py index 949eebe0b..00d2950e1 100644 --- a/test/python_lib/aer-simulator/test_aer_compile.py +++ b/test/python_lib/aer-simulator/test_aer_compile.py @@ -31,7 +31,7 @@ ) from qss_compiler.exceptions import QSSCompilationFailure -compiler_extra_args = ["--enable-circuits=false"] +compiler_extra_args = ["--num-shots=1", "--enable-circuits=false"] def check_mlir_string(mlir): @@ -44,151 +44,128 @@ def check_mlir_string(mlir): assert "@aer_state_finalize" in mlir -def test_compile_file_to_qem(example_qasm3_tmpfile, simulator_config_file, check_payload): +def test_compile_file_to_qem(example_qasm3_tmpfile, simulator_config_file): """Test that we can compile a file input via the interface compile_file to a QEM payload""" - qem = compile_file( - example_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - # QEM payload is returned as byte sequence - assert isinstance(qem, bytes) - - # check that payload is a zip file - payload_filelike = io.BytesIO(qem) - check_payload(payload_filelike) + # To generate a QEM payload, $LD_PATH and $LIBAER_PATH has to be specified. + # Currently qss-compiler does not have `libaer.so` so this test must be failed. + # Also, in future, a single binary file will be generated for the aer-simulator target. + with pytest.raises(QSSCompilationFailure): + compile_file( + example_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="aer-simulator", + config_path=simulator_config_file, + extra_args=compiler_extra_args, + ) -def test_compile_str_to_qem(simulator_config_file, example_qasm3_str, check_payload): +def test_compile_str_to_qem(simulator_config_file, example_qasm3_str): """Test that we can compile an OpenQASM3 string via the interface compile_file to a QEM payload""" - qem = compile_str( - example_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, + # To generate a QEM payload, $LD_PATH and $LIBAER_PATH has to be specified. + # Currently qss-compiler does not have `libaer.so` so this test must be failed. + # Also, in future, a single binary file will be generated for the aer-simulator target. + with pytest.raises(QSSCompilationFailure): + compile_str( + example_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.QEM, + output_file=None, + target="aer-simulator", + config_path=simulator_config_file, + extra_args=compiler_extra_args, + ) + + +def test_compile_file_to_mlir(example_qasm3_tmpfile, simulator_config_file): + """Test that we can compile a file input via the interface compile_file + to a MLIR file""" + + mlir = compile_file( + example_qasm3_tmpfile, + input_type=InputType.QASM3, + output_type=OutputType.MLIR, + output_file=None, + target="aer-simulator", + config_path=simulator_config_file, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) - # QEM payload is returned as byte sequence - assert isinstance(qem, bytes) - # check that payload is a zip file - payload_filelike = io.BytesIO(qem) - check_payload(payload_filelike) + check_mlir_string(mlir) + +def test_compile_str_to_mlir(example_qasm3_str, simulator_config_file): + """Test that we can compile a file input via the interface compile_file + to a MLIR file""" -def test_compile_file_to_qem_file( - example_qasm3_tmpfile, simulator_config_file, tmp_path, check_payload + mlir = compile_str( + example_qasm3_str, + input_type=InputType.QASM3, + output_type=OutputType.MLIR, + output_file=None, + target="aer-simulator", + config_path=simulator_config_file, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + ) + + check_mlir_string(mlir) + + +def test_compile_file_to_mlir_file( + example_qasm3_tmpfile, simulator_config_file, tmp_path ): """Test that we can compile a file input via the interface compile_file to a QEM payload into a file""" - tmpfile = tmp_path / "payload.qem" + tmpfile = tmp_path / "output.mlir" result = compile_file( example_qasm3_tmpfile, input_type=InputType.QASM3, - output_type=OutputType.QEM, + output_type=OutputType.MLIR, output_file=tmpfile, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) - + # no direct return assert result is None file_stat = os.stat(tmpfile) assert file_stat.st_size > 0 + + with open(tmpfile) as mlir_str: + check_mlir_string(mlir_str) + - with open(tmpfile, "rb") as payload: - check_payload(payload) - - -def test_compile_str_to_qem_file( - simulator_config_file, tmp_path, example_qasm3_str, check_payload +def test_compile_str_to_mlir_file( + example_qasm3_str, simulator_config_file, tmp_path ): - """Test that we can compile an OpenQASM3 string via the interface - compile_file to a QEM payload in an output file""" - tmpfile = tmp_path / "payload.qem" + """Test that we can compile a file input via the interface compile_file + to a QEM payload into a file""" + tmpfile = tmp_path / "output.mlir" result = compile_str( example_qasm3_str, input_type=InputType.QASM3, - output_type=OutputType.QEM, + output_type=OutputType.MLIR, output_file=tmpfile, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) - + # no direct return assert result is None file_stat = os.stat(tmpfile) assert file_stat.st_size > 0 - - with open(tmpfile, "rb") as payload: - check_payload(payload) - - -def test_compile_failing_str_to_qem( - simulator_config_file, example_unsupported_qasm3_str, example_qasm3_str, check_payload -): - """Test that compiling an invalid OpenQASM3 string via the interface - compile_str to a QEM payload will fail and result in an empty payload.""" - - with pytest.raises(QSSCompilationFailure): - compile_str( - example_unsupported_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - -def test_compile_failing_file_to_qem( - example_unsupported_qasm3_tmpfile, simulator_config_file, tmp_path, check_payload -): - """Test that compiling an invalid file input via the interface compile_file - to a QEM payload will fail and result in an empty payload.""" - - with pytest.raises(QSSCompilationFailure): - compile_file( - example_unsupported_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - -def test_compile_options(simulator_config_file, example_qasm3_str): - """Test compilation with explicit CompileOptions construction.""" - - compile_options = CompileOptions( - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - target="aer-simulator", - config_path=simulator_config_file, - shot_delay=100, - num_shots=1, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], - ) - - mlir = compile_str(example_qasm3_str, compile_options=compile_options) - check_mlir_string(mlir) + + with open(tmpfile) as mlir_str: + check_mlir_string(mlir_str) async def sleep_a_little(): @@ -197,23 +174,23 @@ async def sleep_a_little(): @pytest.mark.asyncio -async def test_async_compile_str(simulator_config_file, example_qasm3_str, check_payload): +async def test_async_compile_str(simulator_config_file, example_qasm3_str): """Test that async wrapper produces correct output and does not block the even loop.""" async_compile = compile_str_async( example_qasm3_str, input_type=InputType.QASM3, - output_type=OutputType.QEM, + output_type=OutputType.MLIR, output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) # Start a task that sleeps shorter than the compilation and then takes a # timestamp. If the compilation blocks the event loop, then the timestamp # will be delayed further than the intended sleep duration. sleeper = asyncio.create_task(sleep_a_little()) timestamp_launched = datetime.now() - qem = await async_compile + mlir = await async_compile timestamp_sleeped = await sleeper sleep_duration = timestamp_sleeped - timestamp_launched @@ -223,34 +200,29 @@ async def test_async_compile_str(simulator_config_file, example_qasm3_str, check ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ event loop probably got blocked!" - # QEM payload is returned as byte sequence - assert isinstance(qem, bytes) - - # check that payload is a zip file - payload_filelike = io.BytesIO(qem) - check_payload(payload_filelike) + check_mlir_string(mlir) @pytest.mark.asyncio async def test_async_compile_file( - example_qasm3_tmpfile, simulator_config_file, check_payload + example_qasm3_tmpfile, simulator_config_file ): """Test that async wrapper produces correct output and does not block the even loop.""" async_compile = compile_file_async( example_qasm3_tmpfile, input_type=InputType.QASM3, - output_type=OutputType.QEM, + output_type=OutputType.MLIR, output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args, + extra_args=compiler_extra_args + ["--aer-simulator-conversion"], ) # Start a task that sleeps shorter than the compilation and then takes a # timestamp. If the compilation blocks the event loop, then the timestamp # will be delayed further than the intended sleep duration. sleeper = asyncio.create_task(sleep_a_little()) timestamp_launched = datetime.now() - qem = await async_compile + mlir = await async_compile timestamp_sleeped = await sleeper sleep_duration = timestamp_sleeped - timestamp_launched @@ -260,9 +232,4 @@ async def test_async_compile_file( ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ event loop probably got blocked!" - # QEM payload is returned as byte sequence - assert isinstance(qem, bytes) - - # check that payload is a zip file - payload_filelike = io.BytesIO(qem) - check_payload(payload_filelike) + check_mlir_string(mlir) From 7c3341149f30cbe4facf89bf96be06e103c9d061 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 16:52:56 +0900 Subject: [PATCH 05/15] Reflect formatter suggestions --- targets/simulators/aer/AerSimulator.cpp | 6 +++--- .../simulators/aer/Conversion/QUIRToAer.cpp | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index 7f2069789..43ffed3bc 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -411,10 +411,10 @@ llvm::Error AerSimulator::callTool( llvm::Twine("Failed to execute ") + program + " " + executeError); - if (ret == 0) - return llvm::Error::success(); - else + if (ret != 0) return llvm::createStringError( std::error_code{ret, std::generic_category()}, "%*s failed with return code %d", program.size(), program.data(), ret); + + return llvm::Error::success(); } // callTool diff --git a/targets/simulators/aer/Conversion/QUIRToAer.cpp b/targets/simulators/aer/Conversion/QUIRToAer.cpp index 3220f2a08..3e2992fd8 100644 --- a/targets/simulators/aer/Conversion/QUIRToAer.cpp +++ b/targets/simulators/aer/Conversion/QUIRToAer.cpp @@ -89,7 +89,7 @@ void declareAerFunctions(ModuleOp moduleOp) { aerFuncTable.insert({name, f}); }; - auto context = moduleOp->getContext(); + auto *context = moduleOp->getContext(); builder.setInsertionPointToStart(moduleOp.getBody()); // common types const auto voidType = LLVM::LLVMVoidType::get(context); @@ -144,8 +144,8 @@ void createAerState(MLIRContext *ctx, ModuleOp moduleOp) { /*alignment=*/8); aerState = AerStateWrapper(globalState); - auto mainFunc = mlir::quir::getMainFunction(moduleOp); - auto mainBody = &mainFunc->getRegion(0).getBlocks().front(); + auto *mainFunc = mlir::quir::getMainFunction(moduleOp); + auto *mainBody = &mainFunc->getRegion(0).getBlocks().front(); builder.setInsertionPointToStart(mainBody); auto addr = aerState.getAddr(builder); auto call = @@ -204,9 +204,9 @@ void prepareArrayForMeas(ModuleOp moduleOp) { // Assume qcs.init is called before all quir.declare_qubit operations struct QCSInitConversionPat : public OpConversionPattern { explicit QCSInitConversionPat(MLIRContext *ctx, TypeConverter &typeConverter, - AerSimulatorConfig const &config) + AerSimulatorConfig config) : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1), - config(config) {} + config(std::move(config)) {} LogicalResult matchAndRewrite(qcs::SystemInitOp initOp, qcs::SystemInitOp::Adaptor adapter, @@ -217,12 +217,12 @@ struct QCSInitConversionPat : public OpConversionPattern { const auto method = config.getMethod(); const auto device = config.getDevice(); const auto precision = config.getPrecision(); - const auto methodStr = toStringInAer(method); - const auto deviceStr = toStringInAer(device); - const auto precisionStr = toStringInAer(precision); + const auto *const methodStr = toStringInAer(method); + const auto *const deviceStr = toStringInAer(device); + const auto *const precisionStr = toStringInAer(precision); const auto config_strs = {"method", methodStr, "device", deviceStr, "precision", precisionStr}; - for (auto config_str : config_strs) { + for (const auto *config_str : config_strs) { const auto var_name = std::string("aer_conf_") + config_str; const auto with_null = config_str + std::string("\0", 1); globals[config_str] = From b19f375aaee614b4f492e2b8f456726ff7ab0bba Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 17:10:51 +0900 Subject: [PATCH 06/15] Fix type-error --- test/python_lib/aer-simulator/test_aer_compile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/python_lib/aer-simulator/test_aer_compile.py b/test/python_lib/aer-simulator/test_aer_compile.py index 00d2950e1..a7993ed4b 100644 --- a/test/python_lib/aer-simulator/test_aer_compile.py +++ b/test/python_lib/aer-simulator/test_aer_compile.py @@ -138,8 +138,8 @@ def test_compile_file_to_mlir_file( file_stat = os.stat(tmpfile) assert file_stat.st_size > 0 - with open(tmpfile) as mlir_str: - check_mlir_string(mlir_str) + with open(tmpfile) as mlir_file: + check_mlir_string(mlir_file.read()) def test_compile_str_to_mlir_file( @@ -164,8 +164,8 @@ def test_compile_str_to_mlir_file( file_stat = os.stat(tmpfile) assert file_stat.st_size > 0 - with open(tmpfile) as mlir_str: - check_mlir_string(mlir_str) + with open(tmpfile) as mlir_file: + check_mlir_string(mlir_file.read()) async def sleep_a_little(): From 0a8035b63493d87ba56812b12b333137207a8b0b Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 18:52:46 +0900 Subject: [PATCH 07/15] Remove unnecessary tests --- .../aer-simulator/test_aer_compile.py | 56 ++++--------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/test/python_lib/aer-simulator/test_aer_compile.py b/test/python_lib/aer-simulator/test_aer_compile.py index a7993ed4b..c9b02d89d 100644 --- a/test/python_lib/aer-simulator/test_aer_compile.py +++ b/test/python_lib/aer-simulator/test_aer_compile.py @@ -31,7 +31,11 @@ ) from qss_compiler.exceptions import QSSCompilationFailure -compiler_extra_args = ["--num-shots=1", "--enable-circuits=false"] +compiler_extra_args = [ + "--aer-simulator-conversion", + "--num-shots=1", + "--enable-circuits=false" +] def check_mlir_string(mlir): @@ -44,44 +48,6 @@ def check_mlir_string(mlir): assert "@aer_state_finalize" in mlir -def test_compile_file_to_qem(example_qasm3_tmpfile, simulator_config_file): - """Test that we can compile a file input via the interface compile_file - to a QEM payload""" - - # To generate a QEM payload, $LD_PATH and $LIBAER_PATH has to be specified. - # Currently qss-compiler does not have `libaer.so` so this test must be failed. - # Also, in future, a single binary file will be generated for the aer-simulator target. - with pytest.raises(QSSCompilationFailure): - compile_file( - example_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - -def test_compile_str_to_qem(simulator_config_file, example_qasm3_str): - """Test that we can compile an OpenQASM3 string via the interface - compile_file to a QEM payload""" - - # To generate a QEM payload, $LD_PATH and $LIBAER_PATH has to be specified. - # Currently qss-compiler does not have `libaer.so` so this test must be failed. - # Also, in future, a single binary file will be generated for the aer-simulator target. - with pytest.raises(QSSCompilationFailure): - compile_str( - example_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.QEM, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - def test_compile_file_to_mlir(example_qasm3_tmpfile, simulator_config_file): """Test that we can compile a file input via the interface compile_file to a MLIR file""" @@ -93,7 +59,7 @@ def test_compile_file_to_mlir(example_qasm3_tmpfile, simulator_config_file): output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) check_mlir_string(mlir) @@ -110,7 +76,7 @@ def test_compile_str_to_mlir(example_qasm3_str, simulator_config_file): output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) check_mlir_string(mlir) @@ -130,7 +96,7 @@ def test_compile_file_to_mlir_file( output_file=tmpfile, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) # no direct return @@ -156,7 +122,7 @@ def test_compile_str_to_mlir_file( output_file=tmpfile, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) # no direct return @@ -183,7 +149,7 @@ async def test_async_compile_str(simulator_config_file, example_qasm3_str): output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) # Start a task that sleeps shorter than the compilation and then takes a # timestamp. If the compilation blocks the event loop, then the timestamp @@ -215,7 +181,7 @@ async def test_async_compile_file( output_file=None, target="aer-simulator", config_path=simulator_config_file, - extra_args=compiler_extra_args + ["--aer-simulator-conversion"], + extra_args=compiler_extra_args, ) # Start a task that sleeps shorter than the compilation and then takes a # timestamp. If the compilation blocks the event loop, then the timestamp From 786b4cad73831a8911c905d5a8e8c82b8bb0020a Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 27 Sep 2023 19:13:01 +0900 Subject: [PATCH 08/15] Remove the part of linking phase --- targets/simulators/aer/AerSimulator.cpp | 77 ++++--------------------- 1 file changed, 11 insertions(+), 66 deletions(-) diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index 43ffed3bc..ffab9fca0 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -257,6 +257,7 @@ llvm::Error AerSimulator::addToPayload(mlir::ModuleOp &moduleOp, return buildLLVMPayload(moduleOp, payload); } // AerSimulator::addToPayload +// FUTURE: Support to lower completely to LLVM-IR and generate a binary file llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, payload::Payload &payload) { @@ -328,7 +329,7 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, llvm::SmallString<128> objPath; int objFd; - if (auto err = llvm::sys::fs::createTemporaryFile("simulatorModule", "obj", + if (auto err = llvm::sys::fs::createTemporaryFile("simulatorModule", "o", objFd, objPath)) { llvm::errs() << "Failed to create temporary object file for simulator module.\n"; @@ -344,77 +345,21 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, pass.run(*llvmModule); obj->os().flush(); - // Link the generated obj with a dynamic library of qiskit Aer - char *LD = getenv("LD_PATH"); - char *AERLIB = getenv("LIBAER_PATH"); + // TODO: In future, we need to link the generated object file + // with `libaer.{so, dylib}` to create a executable file here. + // An external linker (e.g. ld) may have to be called and also + // we have to specify the path to the linker and the shared library. - llvm::SmallString<128> outputPath; - if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "out", - outputPath)) - return llvm::make_error(); - - llvm::SmallVector lld_argv{"ld", objPath, AERLIB, "-o", - outputPath}; - - llvm::SmallString<128> stdErrPath; - if (auto EC = llvm::sys::fs::createTemporaryFile("simulatorModule", "err", - stdErrPath)) - return llvm::make_error(); - - llvm::Optional redirects[] = { - {""}, {""}, llvm::StringRef(stdErrPath)}; - - if (auto err = callTool(LD, lld_argv, redirects, true)) { - auto bufOrError = llvm::MemoryBuffer::getFile(stdErrPath); - if (!bufOrError) { - llvm::errs() << "call linker error: " << bufOrError.getError().message() - << ", ret=" << err; - } else { - llvm::errs() << "call linker error: ret=" << err; - } - return llvm::make_error(); - } - - std::ifstream output(outputPath.c_str(), std::ios_base::binary); - if (!output) { - llvm::errs() << "Failed to open generated simulator object file " - << outputPath; + std::ifstream binary(objPath.c_str(), std::ios_base::binary); + if (!binary) { + llvm::errs() << "Failed to open generated object file."; return llvm::make_error(); } - std::string outputContents{std::istreambuf_iterator(output), + std::string binaryContents{std::istreambuf_iterator(binary), std::istreambuf_iterator()}; - payload.getFile("simulator.bin")->assign(std::move(outputContents)); + payload.getFile("simulator.bin")->assign(std::move(binaryContents)); return llvm::Error::success(); } // AerSimulator::buildLLVMPayload - -llvm::Error AerSimulator::callTool( - llvm::StringRef program, llvm::ArrayRef args, - llvm::ArrayRef> redirects, bool dumpArgs) { - - if (dumpArgs) { - llvm::errs() << "Calling " << program << " with args"; - for (auto const ¶m : args) - llvm::errs() << " " << param; - llvm::errs() << "\n"; - } - - std::string executeError; - int ret = - llvm::sys::ExecuteAndWait(program, args, /* environment */ llvm::None, - redirects, 0, 0, &executeError); - - if (ret < 0 || executeError.size() > 0) - return llvm::createStringError(llvm::inconvertibleErrorCode(), - llvm::Twine("Failed to execute ") + program + - " " + executeError); - - if (ret != 0) - return llvm::createStringError( - std::error_code{ret, std::generic_category()}, - "%*s failed with return code %d", program.size(), program.data(), ret); - - return llvm::Error::success(); -} // callTool From 91fc0ea6ed6e9c31ad72fb67865d2714577967e8 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 11 Oct 2023 17:09:39 +0900 Subject: [PATCH 09/15] Remove global variables --- .../simulators/aer/Conversion/QUIRToAer.cpp | 153 ++++++++++++------ targets/simulators/aer/Errors.h | 2 +- 2 files changed, 104 insertions(+), 51 deletions(-) diff --git a/targets/simulators/aer/Conversion/QUIRToAer.cpp b/targets/simulators/aer/Conversion/QUIRToAer.cpp index 3e2992fd8..bcd42261f 100644 --- a/targets/simulators/aer/Conversion/QUIRToAer.cpp +++ b/targets/simulators/aer/Conversion/QUIRToAer.cpp @@ -71,16 +71,13 @@ class AerStateWrapper { LLVM::GlobalOp mem; }; -AerStateWrapper aerState; -std::map aerFuncTable; - +using AerFunctionTable = std::map; // Declare Aer runtime API functions globally. // The definitions of those functions are given externally by a linker. -void declareAerFunctions(ModuleOp moduleOp) { +AerFunctionTable declareAerFunctions(ModuleOp moduleOp) { using LLVM::LLVMFunctionType; - aerFuncTable.clear(); - + AerFunctionTable aerFuncTable; OpBuilder builder(moduleOp); auto registerFunc = [&](const char *name, LLVMFunctionType ty) { @@ -129,10 +126,13 @@ void declareAerFunctions(ModuleOp moduleOp) { const auto aerStateFinalizeType = LLVMFunctionType::get(voidType, aerStateType); registerFunc("aer_state_finalize", aerStateFinalizeType); + + return aerFuncTable; } // Create an Aer state globally and then wrap the state value. -void createAerState(MLIRContext *ctx, ModuleOp moduleOp) { +AerStateWrapper createAerState(MLIRContext *ctx, ModuleOp moduleOp, + AerFunctionTable aerFuncTable) { const auto i8Type = IntegerType::get(ctx, 8); OpBuilder builder(moduleOp); @@ -142,7 +142,7 @@ void createAerState(MLIRContext *ctx, ModuleOp moduleOp) { moduleOp->getLoc(), aerStateTy, /*isConstant=*/false, LLVM::Linkage::Weak, "aer_state_handler", Attribute{}, /*alignment=*/8); - aerState = AerStateWrapper(globalState); + auto aerState = AerStateWrapper(globalState); auto *mainFunc = mlir::quir::getMainFunction(moduleOp); auto *mainBody = &mainFunc->getRegion(0).getBlocks().front(); @@ -154,16 +154,12 @@ void createAerState(MLIRContext *ctx, ModuleOp moduleOp) { aerFuncTable.at("aer_state"), ValueRange{}) .getResult(0); builder.create(builder.getUnknownLoc(), call, addr); -} -// Aer C API requires an array of measured qubits. This provides a common -// array for the measurements that can avoid a stack allocation for each -// function call of the Aer measurement. -// Note that the size of this array must be large enough to perform all -// the measurements appeared in a given program. -LLVM::AllocaOp arrayForMeas; + return aerState; +} -void insertAerStateInitialize(ModuleOp moduleOp) { +void insertAerStateInitialize(ModuleOp moduleOp, AerStateWrapper aerState, + AerFunctionTable aerFuncTable) { OpBuilder builder(moduleOp); // Insert Aer runtime initialization after qubit declarations. @@ -183,8 +179,14 @@ void insertAerStateInitialize(ModuleOp moduleOp) { aerFuncTable.at("aer_state_initialize"), state); } +using ArrayForMeas = mlir::Value; // Allocate an array for measurements globally. -void prepareArrayForMeas(ModuleOp moduleOp) { +// @note Aer C API requires an array of measured qubits. This provides a common +// array for the measurements that can avoid a stack allocation for each +// function call of the Aer measurement. +// Note that the size of this array must be large enough to perform all +// the measurements appeared in a given program. +ArrayForMeas prepareArrayForMeas(ModuleOp moduleOp) { OpBuilder builder(moduleOp); const auto i64Type = builder.getI64Type(); @@ -194,9 +196,11 @@ void prepareArrayForMeas(ModuleOp moduleOp) { auto arrSizeOp = builder.create( builder.getUnknownLoc(), i64Type, builder.getIntegerAttr(i64Type, arraySize)); - arrayForMeas = builder.create( - builder.getUnknownLoc(), LLVM::LLVMPointerType::get(i64Type), arrSizeOp, - /*alignment=*/8); + return builder + .create(builder.getUnknownLoc(), + LLVM::LLVMPointerType::get(i64Type), arrSizeOp, + /*alignment=*/8) + .getResult(); } } // namespace @@ -204,9 +208,12 @@ void prepareArrayForMeas(ModuleOp moduleOp) { // Assume qcs.init is called before all quir.declare_qubit operations struct QCSInitConversionPat : public OpConversionPattern { explicit QCSInitConversionPat(MLIRContext *ctx, TypeConverter &typeConverter, - AerSimulatorConfig config) + AerSimulatorConfig config, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable) : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1), - config(std::move(config)) {} + config(std::move(config)), aerFuncTable(std::move(aerFuncTable)), + aerState(std::move(aerState)) {} LogicalResult matchAndRewrite(qcs::SystemInitOp initOp, qcs::SystemInitOp::Adaptor adapter, @@ -249,6 +256,8 @@ struct QCSInitConversionPat : public OpConversionPattern { private: const AerSimulatorConfig config; + const AerFunctionTable aerFuncTable; + AerStateWrapper aerState; }; // Currently the simulator target does not support shot iterations. @@ -268,8 +277,11 @@ struct RemoveQCSShotInitConversionPat struct FinalizeConversionPat : public OpConversionPattern { - explicit FinalizeConversionPat(MLIRContext *ctx, TypeConverter &typeConverter) - : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1) {} + explicit FinalizeConversionPat(MLIRContext *ctx, TypeConverter &typeConverter, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable) + : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1), + aerState(aerState), aerFuncTable(std::move(aerFuncTable)) {} LogicalResult matchAndRewrite(qcs::SystemFinalizeOp finOp, @@ -283,6 +295,10 @@ struct FinalizeConversionPat rewriter.eraseOp(finOp); return success(); } + +private: + AerStateWrapper aerState; + const AerFunctionTable aerFuncTable; }; struct DeclareQubitConversionPat @@ -290,8 +306,11 @@ struct DeclareQubitConversionPat using Adaptor = quir::DeclareQubitOp::Adaptor; explicit DeclareQubitConversionPat(MLIRContext *ctx, - TypeConverter &typeConverter) - : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + TypeConverter &typeConverter, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1), + aerState(aerState), aerFuncTable(std::move(aerFuncTable)) {} LogicalResult matchAndRewrite(quir::DeclareQubitOp op, Adaptor adaptor, @@ -309,12 +328,19 @@ struct DeclareQubitConversionPat rewriter.replaceOp(op, alloc.getResults()); return success(); } + +private: + AerStateWrapper aerState; + const AerFunctionTable aerFuncTable; }; struct BuiltinUopConversionPat : public OpConversionPattern { explicit BuiltinUopConversionPat(MLIRContext *ctx, - TypeConverter &typeConverter) - : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + TypeConverter &typeConverter, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1), + aerState(aerState), aerFuncTable(std::move(aerFuncTable)) {} LogicalResult matchAndRewrite(quir::Builtin_UOp op, quir::Builtin_UOp::Adaptor adaptor, @@ -328,12 +354,19 @@ struct BuiltinUopConversionPat : public OpConversionPattern { rewriter.eraseOp(op); return success(); } + +private: + AerStateWrapper aerState; + const AerFunctionTable aerFuncTable; }; struct BuiltinCXConversionPat : public OpConversionPattern { explicit BuiltinCXConversionPat(MLIRContext *ctx, - TypeConverter &typeConverter) - : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + TypeConverter &typeConverter, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1), + aerState(aerState), aerFuncTable(std::move(aerFuncTable)) {} LogicalResult matchAndRewrite(quir::BuiltinCXOp op, quir::BuiltinCXOp::Adaptor adaptor, @@ -347,35 +380,52 @@ struct BuiltinCXConversionPat : public OpConversionPattern { rewriter.eraseOp(op); return success(); } + +private: + AerStateWrapper aerState; + const AerFunctionTable aerFuncTable; }; struct MeasureOpConversionPat : public OpConversionPattern { explicit MeasureOpConversionPat(MLIRContext *ctx, - TypeConverter &typeConverter) - : OpConversionPattern(typeConverter, ctx, /*benefit=*/1) {} + TypeConverter &typeConverter, + AerStateWrapper aerState, + AerFunctionTable aerFuncTable, + ArrayForMeas arrayForMeas) + : OpConversionPattern(typeConverter, ctx, /*benefit=*/1), + aerState(aerState), aerFuncTable(std::move(aerFuncTable)), + arrayForMeas(arrayForMeas) {} LogicalResult matchAndRewrite(quir::MeasureOp op, quir::MeasureOp::Adaptor adaptor, ConversionPatternRewriter &rewriter) const override { assert(op->getNumOperands() == 1 && "Multi-body measurement have not been supported yet."); + // How to cast? + auto allocaArrForMeas = + llvm::dyn_cast(arrayForMeas.getDefiningOp()); const auto i64Type = rewriter.getI64Type(); const unsigned arrSize = 1; // TODO const IntegerAttr arraySizeAttr = rewriter.getIntegerAttr(i64Type, arrSize); const auto qubit = *adaptor.getOperands().begin(); auto arrSizeOp = rewriter.create(op->getLoc(), i64Type, arraySizeAttr); - rewriter.create(op->getLoc(), qubit, arrayForMeas); + rewriter.create(op->getLoc(), qubit, allocaArrForMeas); auto state = aerState.access(rewriter); auto meas = rewriter.create( op->getLoc(), aerFuncTable.at("aer_apply_measure"), - ValueRange{state, arrayForMeas.getResult(), arrSizeOp}); + ValueRange{state, arrayForMeas, arrSizeOp}); auto casted = rewriter.create( op->getLoc(), meas.getResult(0), rewriter.getI1Type()); rewriter.replaceOp(op, casted.getResult()); return success(); } + +private: + AerStateWrapper aerState; + const AerFunctionTable aerFuncTable; + ArrayForMeas arrayForMeas; }; struct ConstConversionPat : public OpConversionPattern { @@ -460,27 +510,30 @@ void QUIRToAERPass::runOnOperation(AerSimulator &system) { target.addDynamicallyLegalOp( [&](FuncOp op) { return typeConverter.isSignatureLegal(op.getType()); }); + // Aer initialization + auto aerFuncTable = declareAerFunctions(moduleOp); + auto aerState = createAerState(context, moduleOp, aerFuncTable); + insertAerStateInitialize(moduleOp, aerState, aerFuncTable); + auto arrayForMeas = prepareArrayForMeas(moduleOp); + RewritePatternSet patterns(context); populateFunctionOpInterfaceTypeConversionPattern(patterns, typeConverter); populateCallOpTypeConversionPattern(patterns, typeConverter); oq3::populateOQ3ToStandardConversionPatterns(typeConverter, patterns); - patterns - .add, // TODO: Support noise models - RemoveConversionPat, // TODO: Support noise models - RemoveConversionPat, // TODO: Support custom gates - DeclareQubitConversionPat, ConstConversionPat, FinalizeConversionPat, - BuiltinUopConversionPat, BuiltinCXConversionPat, - MeasureOpConversionPat, FunctionConversionPat>(context, - typeConverter); - patterns.add(context, typeConverter, simulatorConfig); - - // Aer initialization - declareAerFunctions(moduleOp); - createAerState(context, moduleOp); - insertAerStateInitialize(moduleOp); - prepareArrayForMeas(moduleOp); + patterns.add(context, typeConverter, simulatorConfig, + aerState, aerFuncTable); + patterns.add( + context, typeConverter, aerState, aerFuncTable); + patterns.add(context, typeConverter, aerState, + aerFuncTable, arrayForMeas); + patterns.add< + RemoveQCSShotInitConversionPat, ConstConversionPat, FunctionConversionPat, + RemoveConversionPat, // TODO: Support noise models + RemoveConversionPat, // TODO: Support noise models + RemoveConversionPat>( // TODO: Support custom gates + context, typeConverter); // With the target and rewrite patterns defined, we can now attempt the // conversion. The conversion will signal failure if any of our `illegal` diff --git a/targets/simulators/aer/Errors.h b/targets/simulators/aer/Errors.h index e7bcb74f8..0d661d397 100644 --- a/targets/simulators/aer/Errors.h +++ b/targets/simulators/aer/Errors.h @@ -28,7 +28,7 @@ class LLVMBuildFailure : public llvm::ErrorInfo { public: static char ID; - LLVMBuildFailure() {} + LLVMBuildFailure() = default; void log(llvm::raw_ostream &os) const override { os << "Failed to generate a binary file for Aer simulator"; From 7516cb6a1848419a316d3f17f359b56f13f98fef Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Tue, 17 Oct 2023 16:28:32 +0900 Subject: [PATCH 10/15] Remove unused function --- targets/simulators/aer/AerSimulator.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/targets/simulators/aer/AerSimulator.h b/targets/simulators/aer/AerSimulator.h index ba511b792..b4cec9c07 100644 --- a/targets/simulators/aer/AerSimulator.h +++ b/targets/simulators/aer/AerSimulator.h @@ -88,11 +88,6 @@ class AerSimulator : public qssc::hal::TargetSystem { payload::Payload &payload) override; auto getConfig() -> AerSimulatorConfig & { return *simulatorConfig; } - static llvm::Error - callTool(llvm::StringRef program, llvm::ArrayRef args, - llvm::ArrayRef> redirects, - bool dumpArgs); - private: std::unique_ptr simulatorConfig; From 91fa2c578b1ee8b8a25e38c36ca1a7be78c7ab6d Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 13 Dec 2023 12:29:50 +0900 Subject: [PATCH 11/15] Reflect clang-tidy's suggestions --- targets/simulators/aer/Conversion/QUIRToAer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/simulators/aer/Conversion/QUIRToAer.cpp b/targets/simulators/aer/Conversion/QUIRToAer.cpp index bcd42261f..d036311ac 100644 --- a/targets/simulators/aer/Conversion/QUIRToAer.cpp +++ b/targets/simulators/aer/Conversion/QUIRToAer.cpp @@ -213,7 +213,7 @@ struct QCSInitConversionPat : public OpConversionPattern { AerFunctionTable aerFuncTable) : OpConversionPattern(typeConverter, ctx, /* benefit= */ 1), config(std::move(config)), aerFuncTable(std::move(aerFuncTable)), - aerState(std::move(aerState)) {} + aerState(aerState) {} LogicalResult matchAndRewrite(qcs::SystemInitOp initOp, qcs::SystemInitOp::Adaptor adapter, From 790f33a9fe9dad8f30c55d733677a8448acb7b99 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 13 Dec 2023 12:32:53 +0900 Subject: [PATCH 12/15] Fix the signature format of `payloadPassesFound` --- targets/simulators/aer/AerSimulator.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index ffab9fca0..d1cc0d202 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -62,13 +62,6 @@ using namespace qssc::targets::simulators::aer; using json = nlohmann::json; -// The space below at the front of the string causes this category to be -// printed first -static llvm::cl::OptionCategory simulatorCat( - " QSS Compiler Options for the Simulator target", - "Options that control Simulator-specific behavior of the Simulator QSS " - "Compiler target"); - namespace qssc::targets::simulators::aer { int init() { @@ -244,7 +237,7 @@ llvm::Error AerSimulator::addPayloadPasses(mlir::PassManager &pm) { return llvm::Error::success(); } // AerSimulator::addPayloadPasses -auto AerSimulator::payloadPassesFound(mlir::PassManager &pm) -> bool { +bool AerSimulator::payloadPassesFound(mlir::PassManager &pm) { for (auto &pass : pm.getPasses()) if (pass.getName() == "qssc::targets::simulator::aer::conversion::QUIRToAerPass") From 3c8c9565592a8d6f9d5851118142ff49f1c90099 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 13 Dec 2023 12:47:57 +0900 Subject: [PATCH 13/15] Use llvm::StringError --- targets/simulators/aer/AerSimulator.cpp | 49 ++++++++++++++----------- targets/simulators/aer/CMakeLists.txt | 1 - targets/simulators/aer/Errors.cpp | 23 ------------ targets/simulators/aer/Errors.h | 43 ---------------------- 4 files changed, 28 insertions(+), 88 deletions(-) delete mode 100644 targets/simulators/aer/Errors.cpp delete mode 100644 targets/simulators/aer/Errors.h diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index d1cc0d202..5ad9e6548 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -20,7 +20,6 @@ #include "Conversion/QUIRToAer.h" #include "Conversion/QUIRToLLVM/QUIRToLLVM.h" -#include "Errors.h" #include "Transforms/OutputClassicalRegisters.h" #include "Dialect/QUIR/Transforms/Passes.h" @@ -269,8 +268,9 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, pm.addPass(mlir::createLowerToLLVMPass()); pm.addPass(mlir::LLVM::createLegalizeForExportPass()); if (failed(pm.run(moduleOp))) { - llvm::errs() << "Problems converting `Simulator` module to AER!\n"; - return llvm::make_error(); + return llvm::make_error( + "Problems converting `Simulator` module to AER!\n", + llvm::inconvertibleErrorCode()); } llvm::InitializeNativeTarget(); @@ -285,8 +285,9 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, const auto *target = llvm::TargetRegistry::lookupTarget(targetTriple, errorMessage); if (!target) { - llvm::errs() << "Unable to find target: " << errorMessage << "\n"; - return llvm::make_error(); + return llvm::make_error( + "Unable to find target: " + errorMessage + "\n", + llvm::inconvertibleErrorCode()); } std::string cpu("generic"); @@ -295,19 +296,20 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, targetTriple, cpu, features.getString(), {}, {})); auto dataLayout = machine->createDataLayout(); - if (auto err = quir::translateModuleToLLVMDialect(moduleOp, dataLayout)) { - llvm::errs() << err; - return llvm::make_error(); - } + if (auto err = quir::translateModuleToLLVMDialect(moduleOp, dataLayout)) + return err; // Build LLVM payload llvm::LLVMContext llvmContext; std::unique_ptr llvmModule = mlir::translateModuleToLLVMIR(moduleOp, llvmContext); if (!llvmModule) { - llvm::errs() << "Error converting LLVM module to LLVM IR!\n"; - llvm::errs() << moduleOp << "\n"; - return llvm::make_error(); + std::string msg; + llvm::raw_string_ostream os(msg); + os << "Error converting LLVM module to LLVM IR!\n"; + os << moduleOp << "\n"; + return llvm::make_error(msg, + llvm::inconvertibleErrorCode()); } llvmModule->setDataLayout(dataLayout); @@ -316,24 +318,28 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, // Optionally run an optimization pipeline over the llvm module. auto optPipeline = mlir::makeOptimizingTransformer(0, 0, nullptr); if (auto err = optPipeline(llvmModule.get())) { - llvm::errs() << "Failed to optimize LLVM IR " << err << "\n"; - return llvm::make_error(); + std::string msg; + llvm::raw_string_ostream os(msg); + os << "Failed to optimize LLVM IR: " << err << "\n"; + return llvm::make_error(msg, + llvm::inconvertibleErrorCode()); } llvm::SmallString<128> objPath; int objFd; if (auto err = llvm::sys::fs::createTemporaryFile("simulatorModule", "o", objFd, objPath)) { - llvm::errs() - << "Failed to create temporary object file for simulator module.\n"; - return llvm::make_error(); + return llvm::make_error( + "Failed to create temporary object file for simulator module.\n", + llvm::inconvertibleErrorCode()); } auto obj = std::make_unique(objPath, objFd); llvm::legacy::PassManager pass; if (machine->addPassesToEmitFile(pass, obj->os(), nullptr, llvm::CodeGenFileType::CGFT_ObjectFile)) { - llvm::errs() << "Cannot emit object files with TargetMachine.\n"; - return llvm::make_error(); + return llvm::make_error( + "Cannot emit object files with TargetMachine.\n", + llvm::inconvertibleErrorCode()); } pass.run(*llvmModule); obj->os().flush(); @@ -345,8 +351,9 @@ llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, std::ifstream binary(objPath.c_str(), std::ios_base::binary); if (!binary) { - llvm::errs() << "Failed to open generated object file."; - return llvm::make_error(); + return llvm::make_error( + "Failed to open generated object file.", + llvm::inconvertibleErrorCode()); } std::string binaryContents{std::istreambuf_iterator(binary), diff --git a/targets/simulators/aer/CMakeLists.txt b/targets/simulators/aer/CMakeLists.txt index c7f2770ab..f5dda0e1f 100644 --- a/targets/simulators/aer/CMakeLists.txt +++ b/targets/simulators/aer/CMakeLists.txt @@ -39,7 +39,6 @@ qssc_add_plugin(QSSCTargetAerSimulator QSSC_TARGET_PLUGIN Conversion/QUIRToAer.cpp Conversion/TypeConversion.cpp Transforms/OutputClassicalRegisters.cpp -Errors.cpp AerSimulator.cpp ADDITIONAL_HEADER_DIRS diff --git a/targets/simulators/aer/Errors.cpp b/targets/simulators/aer/Errors.cpp deleted file mode 100644 index 40024374d..000000000 --- a/targets/simulators/aer/Errors.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===- Errors.cpp -----------------------------------------------*- C++ -*-===// -// -// (C) Copyright IBM 2023. -// -// This code is part of Qiskit. -// -// This code is licensed under the Apache License, Version 2.0 with LLVM -// Exceptions. You may obtain a copy of this license in the LICENSE.txt -// file in the root directory of this source tree. -// -// Any modifications or derivative works of this code must retain this -// copyright notice, and modified files need to carry a notice indicating -// that they have been altered from the originals. -// -//===----------------------------------------------------------------------===// - -#include "Errors.h" - -namespace qssc::targets::simulators::aer { - -char LLVMBuildFailure::ID; - -} // namespace qssc::targets::simulators::aer diff --git a/targets/simulators/aer/Errors.h b/targets/simulators/aer/Errors.h deleted file mode 100644 index 0d661d397..000000000 --- a/targets/simulators/aer/Errors.h +++ /dev/null @@ -1,43 +0,0 @@ -//===- Errors.h -------------------------------------------------*- C++ -*-===// -// -// (C) Copyright IBM 2023. -// -// This code is part of Qiskit. -// -// This code is licensed under the Apache License, Version 2.0 with LLVM -// Exceptions. You may obtain a copy of this license in the LICENSE.txt -// file in the root directory of this source tree. -// -// Any modifications or derivative works of this code must retain this -// copyright notice, and modified files need to carry a notice indicating -// that they have been altered from the originals. -// -//===----------------------------------------------------------------------===// -// -// This file declares the classes for errors of aer-simulator -// -//===----------------------------------------------------------------------===// -#ifndef TARGETS_SIMULATORS_AER_ERRORS_H -#define TARGETS_SIMULATORS_AER_ERRORS_H - -#include - -namespace qssc::targets::simulators::aer { - -class LLVMBuildFailure : public llvm::ErrorInfo { -public: - static char ID; - - LLVMBuildFailure() = default; - - void log(llvm::raw_ostream &os) const override { - os << "Failed to generate a binary file for Aer simulator"; - } - std::error_code convertToErrorCode() const override { - return llvm::inconvertibleErrorCode(); - } -}; - -} // namespace qssc::targets::simulators::aer - -#endif // TARGETS_SIMULATORS_AER_ERRORS_H From 9b5308da73cfe61d86405dfd4f64fc7353e872e9 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 13 Dec 2023 12:48:44 +0900 Subject: [PATCH 14/15] Remove unnecessary comments --- targets/simulators/aer/AerSimulator.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/targets/simulators/aer/AerSimulator.cpp b/targets/simulators/aer/AerSimulator.cpp index 5ad9e6548..e1432352f 100644 --- a/targets/simulators/aer/AerSimulator.cpp +++ b/targets/simulators/aer/AerSimulator.cpp @@ -249,7 +249,6 @@ llvm::Error AerSimulator::addToPayload(mlir::ModuleOp &moduleOp, return buildLLVMPayload(moduleOp, payload); } // AerSimulator::addToPayload -// FUTURE: Support to lower completely to LLVM-IR and generate a binary file llvm::Error AerSimulator::buildLLVMPayload(mlir::ModuleOp &moduleOp, payload::Payload &payload) { From e5bb705f1821de8723b301390154faf2f334bae1 Mon Sep 17 00:00:00 2001 From: Ryo Wakizaka Date: Wed, 13 Dec 2023 12:50:46 +0900 Subject: [PATCH 15/15] Remove python tests for aer-simulator --- test/python_lib/aer-simulator/conftest.py | 60 ------ test/python_lib/aer-simulator/pytest.ini | 14 -- test/python_lib/aer-simulator/test.cfg | 5 - .../aer-simulator/test_aer_compile.py | 201 ------------------ 4 files changed, 280 deletions(-) delete mode 100644 test/python_lib/aer-simulator/conftest.py delete mode 100644 test/python_lib/aer-simulator/pytest.ini delete mode 100644 test/python_lib/aer-simulator/test.cfg delete mode 100644 test/python_lib/aer-simulator/test_aer_compile.py diff --git a/test/python_lib/aer-simulator/conftest.py b/test/python_lib/aer-simulator/conftest.py deleted file mode 100644 index 83dbe2661..000000000 --- a/test/python_lib/aer-simulator/conftest.py +++ /dev/null @@ -1,60 +0,0 @@ -# (C) Copyright IBM 2023. -# -# This code is part of Qiskit. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http:#www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Pytest configuration and fixtures -""" - -import multiprocessing -import pathlib -import pytest -import zipfile -from typing import Iterable - - -@pytest.fixture(scope="session", autouse=True) -def set_multiprocessing_start_method(): - multiprocessing.set_start_method("spawn", force=True) - - -@pytest.fixture -def example_qasm3_str(): - return """OPENQASM 3.0; - qubit $0; - bit c0; - U(1.57079632679, 0.0, 3.14159265359) $0; - measure $0 -> c0; - """ - - -def __create_tmpfile(tmp_path, source): - tmp_path = tmp_path / "example.qasm" - with open(tmp_path, "w") as tmpfile: - tmpfile.write(source) - tmpfile.flush() - return tmp_path - - -@pytest.fixture -def example_qasm3_tmpfile(tmp_path, example_qasm3_str): - return __create_tmpfile(tmp_path, example_qasm3_str) - - -@pytest.fixture -def simulator_config_file(): - pytest_dir = pathlib.Path(__file__).parent.resolve() - simulator_test_cfg = pytest_dir.parent / "aer-simulator/test.cfg" - assert simulator_test_cfg.exists() - return str(simulator_test_cfg) diff --git a/test/python_lib/aer-simulator/pytest.ini b/test/python_lib/aer-simulator/pytest.ini deleted file mode 100644 index 129050bc3..000000000 --- a/test/python_lib/aer-simulator/pytest.ini +++ /dev/null @@ -1,14 +0,0 @@ -# (C) Copyright IBM 2023. -# -# This code is part of Qiskit. -# -# This code is licensed under the Apache License, Version 2.0 with LLVM -# Exceptions. You may obtain a copy of this license in the LICENSE.txt -# file in the root directory of this source tree. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -[pytest] -asyncio_mode = strict diff --git a/test/python_lib/aer-simulator/test.cfg b/test/python_lib/aer-simulator/test.cfg deleted file mode 100644 index 54d8e8b15..000000000 --- a/test/python_lib/aer-simulator/test.cfg +++ /dev/null @@ -1,5 +0,0 @@ -{ - "method" : "statevector", - "device" : "cpu", - "precision" : "double" -} \ No newline at end of file diff --git a/test/python_lib/aer-simulator/test_aer_compile.py b/test/python_lib/aer-simulator/test_aer_compile.py deleted file mode 100644 index c9b02d89d..000000000 --- a/test/python_lib/aer-simulator/test_aer_compile.py +++ /dev/null @@ -1,201 +0,0 @@ -# (C) Copyright IBM 2023. -# -# This code is part of Qiskit. -# -# This code is licensed under the Apache License, Version 2.0 with LLVM -# Exceptions. You may obtain a copy of this license in the LICENSE.txt -# file in the root directory of this source tree. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" -Unit tests for the compiler API using Aer simulator targets. -""" - -import asyncio -from datetime import datetime, timedelta -import io -import os -import pytest - -from qss_compiler import ( - compile_file, - compile_file_async, - compile_str, - compile_str_async, - InputType, - OutputType, - CompileOptions, -) -from qss_compiler.exceptions import QSSCompilationFailure - -compiler_extra_args = [ - "--aer-simulator-conversion", - "--num-shots=1", - "--enable-circuits=false" -] - - -def check_mlir_string(mlir): - assert isinstance(mlir, str) - assert "module" in mlir - assert "@aer_state" in mlir - assert "@aer_state_configure" in mlir - assert "@aer_allocate_qubits" in mlir - assert "@aer_state_initialize" in mlir - assert "@aer_state_finalize" in mlir - - -def test_compile_file_to_mlir(example_qasm3_tmpfile, simulator_config_file): - """Test that we can compile a file input via the interface compile_file - to a MLIR file""" - - mlir = compile_file( - example_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - check_mlir_string(mlir) - - -def test_compile_str_to_mlir(example_qasm3_str, simulator_config_file): - """Test that we can compile a file input via the interface compile_file - to a MLIR file""" - - mlir = compile_str( - example_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - check_mlir_string(mlir) - - -def test_compile_file_to_mlir_file( - example_qasm3_tmpfile, simulator_config_file, tmp_path -): - """Test that we can compile a file input via the interface compile_file - to a QEM payload into a file""" - tmpfile = tmp_path / "output.mlir" - - result = compile_file( - example_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=tmpfile, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - # no direct return - assert result is None - file_stat = os.stat(tmpfile) - assert file_stat.st_size > 0 - - with open(tmpfile) as mlir_file: - check_mlir_string(mlir_file.read()) - - -def test_compile_str_to_mlir_file( - example_qasm3_str, simulator_config_file, tmp_path -): - """Test that we can compile a file input via the interface compile_file - to a QEM payload into a file""" - tmpfile = tmp_path / "output.mlir" - - result = compile_str( - example_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=tmpfile, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - - # no direct return - assert result is None - file_stat = os.stat(tmpfile) - assert file_stat.st_size > 0 - - with open(tmpfile) as mlir_file: - check_mlir_string(mlir_file.read()) - - -async def sleep_a_little(): - await asyncio.sleep(1) - return datetime.now() - - -@pytest.mark.asyncio -async def test_async_compile_str(simulator_config_file, example_qasm3_str): - """Test that async wrapper produces correct output and does not block the even loop.""" - async_compile = compile_str_async( - example_qasm3_str, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - # Start a task that sleeps shorter than the compilation and then takes a - # timestamp. If the compilation blocks the event loop, then the timestamp - # will be delayed further than the intended sleep duration. - sleeper = asyncio.create_task(sleep_a_little()) - timestamp_launched = datetime.now() - mlir = await async_compile - timestamp_sleeped = await sleeper - - sleep_duration = timestamp_sleeped - timestamp_launched - milliseconds_waited = sleep_duration / timedelta(microseconds=1000) - assert ( - milliseconds_waited <= 1100 - ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ - event loop probably got blocked!" - - check_mlir_string(mlir) - - -@pytest.mark.asyncio -async def test_async_compile_file( - example_qasm3_tmpfile, simulator_config_file -): - """Test that async wrapper produces correct output and does not block the even loop.""" - async_compile = compile_file_async( - example_qasm3_tmpfile, - input_type=InputType.QASM3, - output_type=OutputType.MLIR, - output_file=None, - target="aer-simulator", - config_path=simulator_config_file, - extra_args=compiler_extra_args, - ) - # Start a task that sleeps shorter than the compilation and then takes a - # timestamp. If the compilation blocks the event loop, then the timestamp - # will be delayed further than the intended sleep duration. - sleeper = asyncio.create_task(sleep_a_little()) - timestamp_launched = datetime.now() - mlir = await async_compile - timestamp_sleeped = await sleeper - - sleep_duration = timestamp_sleeped - timestamp_launched - milliseconds_waited = sleep_duration / timedelta(microseconds=1000) - assert ( - milliseconds_waited <= 1100 - ), f"sleep took longer than intended ({milliseconds_waited} ms instead of ~1000), \ - event loop probably got blocked!" - - check_mlir_string(mlir)