Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support measurements of subveqs for braket #2416

Merged
merged 56 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
e436e95
Add missing passes for translation to OpenQasm2
annagrin Nov 15, 2024
4d7e74c
Merge branch 'main' of https://github.com/NVIDIA/cuda-quantum into br…
annagrin Nov 15, 2024
6744327
Fix failing tests
annagrin Nov 15, 2024
2c9e355
Merge branch 'main' of https://github.com/NVIDIA/cuda-quantum into br…
annagrin Nov 15, 2024
fec4e34
Fix failing tests and format
annagrin Nov 15, 2024
fc81076
Address CR comments
annagrin Nov 15, 2024
8918c92
Address CR comments
annagrin Nov 15, 2024
774d337
Fix failing tests
annagrin Nov 15, 2024
c1b4730
Fix failing tests
annagrin Nov 15, 2024
ec4edf7
* Added Python tests for the 'braket' target
khalatepradnya Nov 21, 2024
6c28a93
* Skip expand measurements pass
khalatepradnya Nov 21, 2024
c68ba3e
* Ignore classical operations in OpenQASM2.0 translation
khalatepradnya Nov 21, 2024
43a6cda
* Support U3 gate
khalatepradnya Nov 21, 2024
cfe5c12
* Test for other simulators
khalatepradnya Nov 21, 2024
ed228bf
* Remove the `combine-quantum-alloc` pass since multiple registers are
khalatepradnya Nov 21, 2024
35138a0
* Clean-up test, use 'Amazon Braket' in messages
khalatepradnya Nov 21, 2024
345960e
Merge branch 'main' into braket-fixes
khalatepradnya Nov 22, 2024
bcf9054
* Decomposition patterns for R1, CRz, Sdg
khalatepradnya Nov 22, 2024
2047b2b
Remove and add some passes to braket
annagrin Nov 22, 2024
96cfecb
Merge with main
annagrin Nov 22, 2024
50c0658
Merge with braket-fixes
annagrin Nov 22, 2024
48a3de3
* Added Python tests for the 'braket' target
khalatepradnya Nov 21, 2024
7634817
* Skip expand measurements pass
khalatepradnya Nov 21, 2024
6b2469f
* Ignore classical operations in OpenQASM2.0 translation
khalatepradnya Nov 21, 2024
45205be
* Support U3 gate
khalatepradnya Nov 21, 2024
2315af9
* Test for other simulators
khalatepradnya Nov 21, 2024
b79042e
* Remove the `combine-quantum-alloc` pass since multiple registers are
khalatepradnya Nov 21, 2024
1e653fe
* Clean-up test, use 'Amazon Braket' in messages
khalatepradnya Nov 21, 2024
fbad12a
* Decomposition patterns for R1, CRz, Sdg
khalatepradnya Nov 22, 2024
aef659e
* Addd `tdg` decomposition
khalatepradnya Nov 22, 2024
89ac10e
* Control modifier fixes
khalatepradnya Nov 23, 2024
304482e
* More tests
khalatepradnya Nov 23, 2024
499f420
* Failing test for multiple measurement ops
khalatepradnya Nov 23, 2024
69a0277
Update lib/Optimizer/Transforms/DecompositionPatterns.cpp
khalatepradnya Nov 23, 2024
9fd113f
Add combine-measurements pass
annagrin Nov 24, 2024
db0b361
* Remove the decomposition patterns for 'SAdjToSZ' and 'TAdjToR1' since
khalatepradnya Nov 25, 2024
258e0e9
* Restore 'translateOperatorName' function, and the corresponding test
khalatepradnya Nov 25, 2024
e33f2a8
* Correct the comment about global pahse on R1ToU3
khalatepradnya Nov 25, 2024
d6f84a5
Made the tests work end to end
annagrin Nov 25, 2024
b47c3b0
Merge with braket-fixes
annagrin Nov 25, 2024
af14f1b
Update translate tests
annagrin Nov 25, 2024
41d0aaa
support multimple qubit measurements
annagrin Nov 26, 2024
6280ed5
Merged with main
annagrin Nov 26, 2024
bb1b72f
Added tests
annagrin Nov 26, 2024
208102b
Address CR comments
annagrin Nov 26, 2024
4a780b0
Address CR comments and remove printing
annagrin Nov 26, 2024
5af0c8d
Address CR comments and remove printing
annagrin Nov 26, 2024
21888f4
Fix test failures and address CR comments
annagrin Nov 26, 2024
295b760
Fix test failures
annagrin Nov 26, 2024
24e73c9
Merge branch 'main' of https://github.com/NVIDIA/cuda-quantum into br…
annagrin Nov 26, 2024
f22fc33
Update combine-measurements after merging with main
annagrin Nov 27, 2024
b7c6ef5
Address CR comments
annagrin Nov 27, 2024
ae14c37
Format
annagrin Nov 27, 2024
3966ee3
Address CR comments
annagrin Nov 27, 2024
797ea89
DCO Remediation Commit for Anna Gringauze <[email protected]>
annagrin Nov 27, 2024
e92e5f1
Merge branch 'main' of https://github.com/NVIDIA/cuda-quantum into br…
annagrin Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/cudaq/Optimizer/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,32 @@ def CheckKernelCalls : Pass<"check-kernel-calls", "mlir::func::FuncOp"> {
}];
}

def CombineMeasurements :
Pass<"combine-measurements", "mlir::func::FuncOp"> {
let summary = "Extends mesurements on subveqs adds output names";
let description = [{
Replace a pattern such as:
```
func.func @kernel() attributes {"cudaq-entrypoint"} {
%1 = ... : !quake.veq<4>
%2 = quake.subveq %1, %c2, %c3 : (!quake.veq<4>, i32, i32) ->
!quake.veq<2>
%measOut = quake.mz %2 : (!quake.veq<2>) -> !cc.stdvec<!quake.measure>
}
```
with:
```
func.func @kernel() attributes {"cudaq-entrypoint", ["output_names",
"[[[0,[1,\22q0\22]],[1,[2,\22q1\22]]]]"]} {
annagrin marked this conversation as resolved.
Show resolved Hide resolved
%1 = ... : !quake.veq<4>
%measOut = quake.mz %1 : (!quake.veq<4>) -> !cc.stdvec<!quake.measure>
}
```
}];
let dependentDialects = ["cudaq::cc::CCDialect", "quake::QuakeDialect"];
}


def CombineQuantumAllocations :
Pass<"combine-quantum-alloc", "mlir::func::FuncOp"> {
let summary = "Combines quake alloca operations.";
Expand Down
13 changes: 0 additions & 13 deletions lib/Optimizer/CodeGen/Pipelines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,7 @@ void cudaq::opt::addPipelineTranslateToOpenQASM(PassManager &pm) {
pm.addPass(createCanonicalizerPass());
pm.addPass(createCSEPass());
pm.addNestedPass<func::FuncOp>(createClassicalMemToReg());
pm.addPass(createLoopUnroll());
pm.addPass(createCanonicalizerPass());
pm.addNestedPass<func::FuncOp>(createLiftArrayAlloc());
pm.addPass(createGlobalizeArrayValues());
pm.addPass(createStatePreparation());
pm.addNestedPass<func::FuncOp>(createGetConcreteMatrix());
pm.addPass(createUnitarySynthesis());
pm.addPass(createSymbolDCEPass());
pm.addPass(createCanonicalizerPass());
pm.addPass(createCSEPass());
pm.addNestedPass<func::FuncOp>(createMultiControlDecompositionPass());
pm.addPass(createDecompositionPass(
{.enabledPatterns = {"CCZToCX", "RxAdjToRx", "RyAdjToRy", "RzAdjToRz"}}));
pm.addPass(createCanonicalizerPass());
}

void cudaq::opt::addPipelineTranslateToIQMJson(PassManager &pm) {
Expand Down
3 changes: 1 addition & 2 deletions lib/Optimizer/CodeGen/TranslateToOpenQASM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,12 @@ using namespace cudaq;
//===----------------------------------------------------------------------===//
// Helper functions
//===----------------------------------------------------------------------===//

annagrin marked this conversation as resolved.
Show resolved Hide resolved
/// Translates operation names into OpenQASM gate names
static LogicalResult translateOperatorName(quake::OperatorInterface optor,
StringRef &name) {
StringRef qkeName = optor->getName().stripDialect();
if (optor.getControls().size() == 0) {
name = StringSwitch<StringRef>(qkeName).Case("r1", "u1").Default(qkeName);
name = StringSwitch<StringRef>(qkeName).Default(qkeName);
annagrin marked this conversation as resolved.
Show resolved Hide resolved
annagrin marked this conversation as resolved.
Show resolved Hide resolved
} else if (optor.getControls().size() == 1) {
name = StringSwitch<StringRef>(qkeName)
.Case("h", "ch")
Expand Down
1 change: 1 addition & 0 deletions lib/Optimizer/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_cudaq_library(OptTransforms
ApplyOpSpecialization.cpp
ArgumentSynthesis.cpp
BasisConversion.cpp
CombineMeasurements.cpp
CombineQuantumAlloc.cpp
ConstPropComplex.cpp
Decomposition.cpp
Expand Down
255 changes: 255 additions & 0 deletions lib/Optimizer/Transforms/CombineMeasurements.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/*******************************************************************************
* Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

#include "PassDetails.h"
#include "cudaq/Optimizer/CodeGen/QIRAttributeNames.h"
#include "cudaq/Optimizer/Dialect/CC/CCOps.h"
#include "cudaq/Optimizer/Dialect/Quake/QuakeOps.h"
#include "cudaq/Optimizer/Dialect/Quake/QuakeTypes.h"
#include "cudaq/Optimizer/Transforms/Passes.h"
#include "nlohmann/json.hpp"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "mlir/Transforms/Passes.h"

namespace cudaq::opt {
#define GEN_PASS_DEF_COMBINEMEASUREMENTS
#include "cudaq/Optimizer/Transforms/Passes.h.inc"
} // namespace cudaq::opt

#define DEBUG_TYPE "combine-measurements"

using namespace mlir;

namespace {

// After combine-quantum-alloc, we have one top allocation per function.
// The following type is used to store qubit mapping from result qubit
// index to the actual qubit index and register name.
// map[result] --> [qb,regName]
// Note: register name is currently not used in `OpenQasm2` backends,
// so we supply a bogus name.
using OutputNamesType =
std::map<std::size_t, std::pair<std::size_t, std::string>>;

struct Analysis {
Analysis() = default;
Analysis(const Analysis &) = delete;
Analysis(Analysis &&) = delete;
Analysis &operator=(const Analysis &) = delete;

mlir::DenseMap<mlir::Value, std::size_t> measurements;
OutputNamesType resultQubitVals;
quake::MzOp lastMeasurement;

bool empty() const { return measurements.empty(); }

LogicalResult analyze(func::FuncOp func) {
quake::AllocaOp qalloc;
std::size_t currentOffset = 0;

for (auto &block : func.getRegion()) {
for (auto &op : block) {
if (auto alloc = dyn_cast_or_null<quake::AllocaOp>(&op)) {
if (qalloc)
return op.emitError("Multiple qalloc statements found");

qalloc = alloc;
} else if (auto measure = dyn_cast_or_null<quake::MzOp>(&op)) {
if (!measure.use_empty()) {
measure.emitWarning("Measurements with uses are not supported");
return success();
}

auto veqOp = measure.getOperand(0);
auto ty = veqOp.getType();

std::size_t size = 0;
if (auto veqTy = dyn_cast<quake::RefType>(ty))
size = 1;
else if (auto veqTy = dyn_cast<quake::VeqType>(ty)) {
size = veqTy.getSize();
if (size == 0)
return op.emitError("Unknown measurement size");
}

measurements[measure.getMeasOut()] = currentOffset;
lastMeasurement = measure;
currentOffset += size;
}
}
}

return success();
}
};

class ExtendQubitMeasurePattern : public OpRewritePattern<quake::MzOp> {
public:
using OpRewritePattern::OpRewritePattern;

explicit ExtendQubitMeasurePattern(MLIRContext *ctx, Analysis &analysis)
: OpRewritePattern(ctx), analysis(analysis) {}

// Replace a pattern such as:
// ```
// %0 = ...: !quake.veq<2>
// %1 = quake.extract_ref %0[0] : (!quake.veq<2>) -> !quake.ref
// %measOut = quake.mz %1 : (!quake.ref) -> !quake.measure
// ```
// with:
// ```
// %1 = ... : !quake.veq<4>
// %measOut = quake.mz %1 : (!quake.veq<4>) -> !cc.stdvec<!quake.measure>
// ```
// And collect output names information: `"[[[0,[1,"q0"]],[1,[2,"q1"]]]]"`
annagrin marked this conversation as resolved.
Show resolved Hide resolved
LogicalResult matchAndRewrite(quake::MzOp measure,
PatternRewriter &rewriter) const override {

auto veqOp = measure.getOperand(0);
if (auto extract = veqOp.getDefiningOp<quake::ExtractRefOp>()) {
auto veq = extract.getVeq();
std::size_t idx;

if (extract.hasConstantIndex())
idx = extract.getConstantIndex();
else if (auto cst =
extract.getIndex().getDefiningOp<arith::ConstantIntOp>())
idx = static_cast<std::size_t>(cst.value());
else
return extract.emitError("Non-constant index in ExtractRef");

auto offset = idx + analysis.measurements[measure.getMeasOut()];
analysis.resultQubitVals[offset] =
std::make_pair(idx, std::to_string(idx));

auto resultType = cudaq::cc::StdvecType::get(measure.getType(0));
if (measure == analysis.lastMeasurement)
rewriter.replaceOpWithNewOp<quake::MzOp>(measure, TypeRange{resultType},
ValueRange{veq},
measure.getRegisterNameAttr());
else if (measure.use_empty())
rewriter.eraseOp(measure);
}

return failure();
}

private:
Analysis &analysis;
};

class ExtendVeqMeasurePattern : public OpRewritePattern<quake::MzOp> {
public:
using OpRewritePattern::OpRewritePattern;

explicit ExtendVeqMeasurePattern(MLIRContext *ctx, Analysis &analysis)
: OpRewritePattern(ctx), analysis(analysis) {}

// Replace a pattern such as:
// ```
// %1 = ... : !quake.veq<4>
// %2 = quake.subveq %1, %c1, %c2 : (!quake.veq<4>, i32, i32) ->
// !quake.veq<2>
// %measOut = quake.mz %2 : (!quake.veq<2>) -> !cc.stdvec<!quake.measure>
// ```
// with:
// ```
// %1 = ... : !quake.veq<4>
// %measOut = quake.mz %1 : (!quake.veq<4>) -> !cc.stdvec<!quake.measure>
// ```
// And collect output names information: `"[[[0,[1,"q0"]],[1,[2,"q1"]]]]"`
LogicalResult matchAndRewrite(quake::MzOp measure,
PatternRewriter &rewriter) const override {

auto veqOp = measure.getOperand(0);
if (auto subveq = veqOp.getDefiningOp<quake::SubVeqOp>()) {
Value lowOp = subveq.getLow();
Value highOp = subveq.getHigh();

auto constLow = lowOp.getDefiningOp<arith::ConstantIntOp>();
if (!constLow)
return subveq.emitError("Non-constant low index in subveq");
auto constHigh = highOp.getDefiningOp<arith::ConstantIntOp>();
if (!constHigh)
return subveq.emitError("Non-constant high index in subveq");

auto low = static_cast<std::size_t>(constLow.value());
auto high = static_cast<std::size_t>(constHigh.value());

for (std::size_t i = low; i <= high; i++) {
auto start = analysis.measurements[measure.getMeasOut()];
auto offset = i - low + start;
analysis.resultQubitVals[offset] = std::make_pair(i, std::to_string(i));
}
if (measure == analysis.lastMeasurement)
rewriter.replaceOpWithNewOp<quake::MzOp>(
measure, measure.getResultTypes(), ValueRange{subveq.getVeq()},
measure.getRegisterNameAttr());
else if (measure.use_empty())
rewriter.eraseOp(measure);

return success();
}

return failure();
}

private:
Analysis &analysis;
};

class CombineMeasurementsPass
: public cudaq::opt::impl::CombineMeasurementsBase<
CombineMeasurementsPass> {
public:
using CombineMeasurementsBase::CombineMeasurementsBase;

void runOnOperation() override {
auto *ctx = &getContext();
func::FuncOp func = getOperation();
OpBuilder builder(func);

LLVM_DEBUG(llvm::dbgs() << "Function before combining measurements:\n"
<< func << "\n\n");

// Analyze the function to find all qubit mappings.
Analysis analysis;
if (failed(analysis.analyze(func))) {
func.emitOpError("Combining measurements failed");
signalPassFailure();
}

if (analysis.empty())
return;

// Extend measurement into one last full measurement.
RewritePatternSet patterns(ctx);
patterns.insert<ExtendQubitMeasurePattern, ExtendVeqMeasurePattern>(
ctx, analysis);
if (failed(applyPatternsAndFoldGreedily(func.getOperation(),
std::move(patterns)))) {
func.emitOpError("Combining measurements failed");
signalPassFailure();
}

// Add output names mapping attribute.
if (!analysis.resultQubitVals.empty()) {
nlohmann::json resultQubitJSON{analysis.resultQubitVals};
func->setAttr(cudaq::opt::QIROutputNamesAttrName,
builder.getStringAttr(resultQubitJSON.dump()));
}

LLVM_DEBUG(llvm::dbgs() << "Function after combining measurements:\n"
<< func << "\n\n");
}
};
} // namespace
29 changes: 29 additions & 0 deletions lib/Optimizer/Transforms/DecompositionPatterns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,34 @@ struct SToR1 : public OpRewritePattern<quake::SOp> {
}
};

// quake.s<adj> target
// ─────────────────────────────────
// (quake.z * quake.s) target
annagrin marked this conversation as resolved.
Show resolved Hide resolved
struct SAdjToSZ : public OpRewritePattern<quake::SOp> {
using OpRewritePattern<quake::SOp>::OpRewritePattern;

void initialize() { setDebugName("SAdjToSZ"); }

LogicalResult matchAndRewrite(quake::SOp op,
PatternRewriter &rewriter) const override {
if (!op.isAdj())
return failure();

// Op info
auto loc = op->getLoc();
auto parameters = op.getParameters();
SmallVector<Value> controls(op.getControls());
Value target = op.getTarget();

QuakeOperatorCreator qRewriter(rewriter);
qRewriter.create<quake::ZOp>(loc, parameters, controls, target);
qRewriter.create<quake::SOp>(loc, parameters, controls, target);
qRewriter.selectWiresAndReplaceUses(op, controls, target);
rewriter.eraseOp(op);
return success();
}
};

//===----------------------------------------------------------------------===//
// TOp decompositions
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1557,6 +1585,7 @@ void cudaq::populateWithAllDecompositionPatterns(RewritePatternSet &patterns) {
// SOp patterns
SToPhasedRx,
SToR1,
SAdjToSZ,
// TOp patterns
TToPhasedRx,
TToR1,
Expand Down
1 change: 1 addition & 0 deletions python/cudaq/kernel/ast_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -4032,4 +4032,5 @@ def compile_to_mlir(astModule, metadata,
if len(bridge.dependentCaptureVars):
extraMetaData['dependent_captures'] = bridge.dependentCaptureVars

bridge.module.dump()
annagrin marked this conversation as resolved.
Show resolved Hide resolved
return bridge.module, bridge.argTypes, extraMetaData
Loading
Loading