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

Draft of dependency graph generation algorithm #1848

Closed
wants to merge 40 commits into from
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a447bd9
Add dependency analysis code
atgeller Jun 24, 2024
c465360
Formatting
atgeller Jun 24, 2024
0943b5a
Use lifetimes computed from dependency graph to map to physical qubits.
atgeller Jun 27, 2024
31a7d64
Use lifetimes to determine when it is safe to reuse qubits, and reord…
atgeller Jul 3, 2024
49edf9e
Insert into new block and whack old block after instead of erasing in…
atgeller Jul 3, 2024
7a31f75
Missed an erase
atgeller Jul 3, 2024
615ee75
Forgot to actually erase old block
atgeller Jul 3, 2024
7a752d9
Some cleanup and fixes
atgeller Jul 10, 2024
96fa396
Merge branch 'main' into dependency_analysis
atgeller Jul 10, 2024
3565142
Fix up scheduling to address bugs, includes two test cases for these …
atgeller Jul 11, 2024
fc5c485
Merge branch 'dependency_analysis' of github.com:atgeller/cuda-quantu…
atgeller Jul 11, 2024
0212dde
Add filecheck commands to tests
atgeller Jul 11, 2024
07050d7
Add assertions and validation functions, and tests for errors. Also i…
atgeller Jul 13, 2024
d959274
More assertions in place
atgeller Jul 15, 2024
2884c07
Add support for classical values
atgeller Jul 16, 2024
4b3136b
Merge branch 'main' into dependency_analysis
atgeller Jul 16, 2024
a9d4941
Fixes several bugs (mostly related to classical values) and adds test…
atgeller Jul 19, 2024
0f5f20a
Merge branch 'dependency_analysis' of github.com:atgeller/cuda-quantu…
atgeller Jul 19, 2024
68a056e
Some cleanup, remove unused functions
atgeller Jul 19, 2024
9a9f826
Remove unnecessary TODO
atgeller Jul 19, 2024
9d5d12d
Improve error messages, add missing test files
atgeller Jul 22, 2024
6526b5e
Improve errors a bit
atgeller Jul 24, 2024
24843c1
Fix performance issue with finding nodes at a given cycle
atgeller Jul 24, 2024
a61ac20
Ensure that classical operations after quantum values are generated
atgeller Jul 24, 2024
0d7160a
Preliminary addition of qubit management passes to JIT pipeline
atgeller Jul 24, 2024
622b8b2
Update pass descriptions
atgeller Jul 24, 2024
f4e93bc
Add qubit management pipeline
atgeller Jul 24, 2024
ffc4c4c
Merge remote-tracking branch 'upstream/main' into dependency_analysis
atgeller Jul 24, 2024
32ecc13
Add to yaml configs, add options to disable qubit management and to d…
atgeller Jul 24, 2024
02b45c9
Add targettests for qubit management
atgeller Jul 24, 2024
ffad80d
Fix bug counting number of virtual qubits
atgeller Jul 25, 2024
da4f915
Update how qubit-management is added to qir-base pipeline
atgeller Jul 29, 2024
6c0ff58
Forgot to remove debug code
atgeller Jul 30, 2024
3de2006
Run qubit management before mapping
atgeller Aug 2, 2024
c61600e
Impose ordering on assigning physical qubits
atgeller Aug 2, 2024
c11779c
Fix various issues in dependency tests
atgeller Aug 6, 2024
8c8f054
Fix bug with codegen for classical values
atgeller Aug 6, 2024
775239f
Avoid reliance on statistic variables to avoid multithreading issues
atgeller Aug 6, 2024
3f01e51
Missed a test file
atgeller Aug 6, 2024
ab3c9c4
Small fix to qubit management pipeline
atgeller Aug 7, 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
9 changes: 9 additions & 0 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ void genericOpPrinter(mlir::OpAsmPrinter &_odsPrinter, mlir::Operation *op,
// Utility functions to test the form of an operation.
//===----------------------------------------------------------------------===//

// Is \p op in the Quake dialect?
// TODO: Is this StringRef comparison faster than calling MLIRContext::
// getLoadedDialect("quake")?
atgeller marked this conversation as resolved.
Show resolved Hide resolved
inline bool isQuakeOperation(mlir::Operation *op) {
if (auto *dialect = op->getDialect())
return dialect->getNamespace().equals("quake");
return false;
}

namespace quake {
/// Returns true if and only if any quantum operand has type `!quake.ref` or
/// `!quake.veq`.
Expand Down
18 changes: 18 additions & 0 deletions include/cudaq/Optimizer/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,24 @@ def ApplySpecialization : Pass<"apply-op-specialization", "mlir::ModuleOp"> {
];
}

def AssignIDs : Pass<"assign-ids", "mlir::func::FuncOp"> {
let summary = "Generate and assign unique identifiers for qubits.";
let description = [{
Work in progress: Attachs unique ID attributes to qubit allocations.
}];

let dependentDialects = ["quake::QuakeDialect"];
}

def DependencyAnalysis : Pass<"dep-analysis", "mlir::func::FuncOp"> {
let summary = "Generates qubit dependency graphs based on quake code.";
let description = [{
Work in progress.
}];

let dependentDialects = ["quake::QuakeDialect"];
}

def BasisConversionPass: Pass<"basis-conversion", "mlir::ModuleOp"> {
let summary = "Converts kernels to a set of basis operations.";
let description = [{
Expand Down
9 changes: 0 additions & 9 deletions lib/Optimizer/Dialect/Quake/QuakeOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ namespace {
#include "cudaq/Optimizer/Dialect/Quake/Canonical.inc"
} // namespace

// Is \p op in the Quake dialect?
// TODO: Is this StringRef comparison faster than calling MLIRContext::
// getLoadedDialect("quake")?
static bool isQuakeOperation(Operation *op) {
if (auto *dialect = op->getDialect())
return dialect->getNamespace().equals("quake");
return false;
}

static LogicalResult verifyWireResultsAreLinear(Operation *op) {
for (Value v : op->getOpResults())
if (isa<quake::WireType>(v.getType())) {
Expand Down
171 changes: 171 additions & 0 deletions lib/Optimizer/Transforms/AssignIDs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*******************************************************************************
* 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 "cudaq/Frontend/nvqpp/AttributeNames.h"
#include "cudaq/Optimizer/Dialect/CC/CCDialect.h"
#include "cudaq/Optimizer/Dialect/Quake/QuakeDialect.h"
#include "cudaq/Optimizer/Dialect/Quake/QuakeOps.h"
#include "cudaq/Optimizer/Transforms/Passes.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/Threading.h"
#include "mlir/InitAllDialects.h"
#include "mlir/Rewrite/FrozenRewritePatternSet.h"
#include "mlir/Transforms/DialectConversion.h"

using namespace mlir;

//===----------------------------------------------------------------------===//
// Generated logic
//===----------------------------------------------------------------------===//
namespace cudaq::opt {
#define GEN_PASS_DEF_ASSIGNIDS
#include "cudaq/Optimizer/Transforms/Passes.h.inc"
} // namespace cudaq::opt

namespace {
inline bool isMeasureOp(Operation *op) {
return dyn_cast<quake::MxOp>(*op) || dyn_cast<quake::MyOp>(*op) ||
dyn_cast<quake::MzOp>(*op);
}

inline bool hasClassicalInput(Operation *op) {
return dyn_cast<quake::RxOp>(*op) || dyn_cast<quake::RyOp>(*op) ||
dyn_cast<quake::RzOp>(*op);
}

inline bool isBeginOp(Operation *op) {
return dyn_cast<quake::UnwrapOp>(*op) || dyn_cast<quake::ExtractRefOp>(*op) ||
dyn_cast<quake::NullWireOp>(*op);
}

inline bool isEndOp(Operation *op) {
return dyn_cast<quake::DeallocOp>(*op) || dyn_cast<quake::SinkOp>(*op);
}

class NullWirePat : public OpRewritePattern<quake::NullWireOp> {
public:
using OpRewritePattern::OpRewritePattern;
using Base = OpRewritePattern<quake::NullWireOp>;

unsigned *counter;

NullWirePat(MLIRContext *context, unsigned *c)
: OpRewritePattern<quake::NullWireOp>(context), counter(c) {}

LogicalResult matchAndRewrite(quake::NullWireOp alloc,
PatternRewriter &rewriter) const override {
if (alloc->hasAttr("qid"))
return failure();

auto qid = (*counter)++;

rewriter.startRootUpdate(alloc);
alloc->setAttr("qid", rewriter.getUI32IntegerAttr(qid));
rewriter.finalizeRootUpdate(alloc);

return success();
}
};

std::optional<uint> findQid(Value v) {
auto defop = v.getDefiningOp();
if (!defop)
return std::nullopt;

if (!isa<quake::WireType>(v.getType()))
return std::nullopt;

if (!quake::isLinearValueForm(defop))
defop->emitOpError("assign-ids requires operations to be in value form");

if (isBeginOp(defop)) {
assert(defop->hasAttr("qid") && "qid not present for beginOp");
uint qid = defop->getAttr("qid").cast<IntegerAttr>().getUInt();
return std::optional<uint>(qid);
}

// Figure out matching operand
size_t i = 0;
for (; i < defop->getNumResults(); i++)
if (defop->getResult(i) == v)
break;

// Special cases where result # != operand #
if (isMeasureOp(defop))
i = 0;
else if (hasClassicalInput(defop))
i++;

return findQid(defop->getOperand(i));
}

class SinkOpPat : public OpRewritePattern<quake::SinkOp> {
public:
using OpRewritePattern::OpRewritePattern;
using Base = OpRewritePattern<quake::SinkOp>;
atgeller marked this conversation as resolved.
Show resolved Hide resolved

SinkOpPat(MLIRContext *context) : OpRewritePattern<quake::SinkOp>(context) {}

LogicalResult matchAndRewrite(quake::SinkOp release,
PatternRewriter &rewriter) const override {
auto qid = findQid(release.getOperand());

if (!qid.has_value())
release->emitOpError(
"Corresponding null_wire not found for sink, illegal ops present");

rewriter.startRootUpdate(release);
release->setAttr("qid", rewriter.getUI32IntegerAttr(qid.value()));
rewriter.finalizeRootUpdate(release);

return success();
}
};

//===----------------------------------------------------------------------===//
// Pass implementation
//===----------------------------------------------------------------------===//

struct AssignIDsPass : public cudaq::opt::impl::AssignIDsBase<AssignIDsPass> {
using AssignIDsBase::AssignIDsBase;

void runOnOperation() override {
auto func = getOperation();

// Blocks will cause problems for assign-ids, ensure there's only one
if (func.getBlocks().size() != 1) {
func.emitOpError("multiple blocks not currently supported in assign-ids");
signalPassFailure();
return;
}

assign();
}

void assign() {
auto *ctx = &getContext();
func::FuncOp func = getOperation();
RewritePatternSet patterns(ctx);
unsigned x = 0;
patterns.insert<NullWirePat>(ctx, &x);
patterns.insert<SinkOpPat>(ctx);
ConversionTarget target(*ctx);
target.addLegalDialect<quake::QuakeDialect>();
target.addDynamicallyLegalOp<quake::NullWireOp>(
[&](quake::NullWireOp alloc) { return alloc->hasAttr("qid"); });
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The attribute approach is brittle as other transformations will not necessarily keep the information. We should really use WireSet, BorrowWire, ReturnWire for explicitly mapping the identity of a qubit from an infinite space (virtual registers) to a finite space (physical registers).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, right now Assign IDs and Dependency Analysis are very tangled passes, so they pretty much have to be run one right after another, and Dependency Analysis has to check that the attributes are present.

target.addDynamicallyLegalOp<quake::SinkOp>(
[&](quake::SinkOp sink) { return sink->hasAttr("qid"); });
if (failed(applyPartialConversion(func.getOperation(), target,
std::move(patterns)))) {
func.emitOpError("assigning qids failed");
signalPassFailure();
}
}
};

} // namespace
2 changes: 2 additions & 0 deletions lib/Optimizer/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_cudaq_library(OptTransforms
AggressiveEarlyInlining.cpp
ApplyControlNegations.cpp
ApplyOpSpecialization.cpp
AssignIDs.cpp
BasisConversion.cpp
CombineQuantumAlloc.cpp
Decomposition.cpp
Expand All @@ -38,6 +39,7 @@ add_cudaq_library(OptTransforms
LowerUnwind.cpp
Mapping.cpp
MemToReg.cpp
DependencyAnalysis.cpp
MultiControlDecomposition.cpp
ObserveAnsatz.cpp
PruneCtrlRelations.cpp
Expand Down
Loading
Loading