Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 19 additions & 24 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,35 @@ def quake_ConcatOp : QuakeOp<"concat", [Pure]> {
let summary = "Construct a veq from a list of other ref/veq values.";
let description = [{
The `concat` operation allows one to concatenate a list of SSA-values of
either type Ref or Veq into a new Veq vector.
either type `ref`, `struq`, or `veq` into a new `veq` vector.

`concat` can also perform size relaxation as a side-effect if the result
type is specified as `!quake.veq<?>`.

Example:
```mlir
%veq = quake.concat %r1, %v1, %r2 : (!quake.ref, !quake.veq<?>,
!quake.ref) -> !quake.veq<?>
%v1 = quake.concat %ra, %vb, %rc : (!quake.ref, !quake.veq<?>,
!quake.ref) -> !quake.veq<?>
%v2 = quake.concat %rd, %ve, %rf : (!quake.ref, !quake.veq<3>,
!quake.ref) -> !quake.veq<5>
%v3 = quake.concat %vg, %vh, %vi : (!quake.veq<3>, !quake.veq<2>,
!quake.veq<4>) -> !quake.veq<?>
%v4 = quake.concat %sj : (!quake.struq<!quake.veq<2>, !quake.ref>) ->
!quake.veq<3>
%v5 = quake.concat %sk : (!quake.struq<!quake.veq<2>, !quake.ref>) ->
!quake.veq<?>
```
}];

let arguments = (ins Variadic<AnyRefType>:$qbits);
let arguments = (ins Variadic<AnyRefType>:$targets);
let results = (outs VeqType);

let assemblyFormat = [{
$qbits attr-dict `:` functional-type(operands, results)
$targets attr-dict `:` functional-type(operands, results)
}];

let hasCanonicalizer = 1;
let hasVerifier = 1;
}

def quake_ExtractRefOp : QuakeOp<"extract_ref", [Pure]> {
Expand Down Expand Up @@ -679,10 +691,10 @@ def quake_SinkOp : QuakeOp<"sink"> {
}];

let arguments = (ins
Arg<WireType, "wire to sink", [MemRead, MemWrite]>:$target
Arg<AnyQLinearType, "wire to sink", [MemRead, MemWrite]>:$target
);
let assemblyFormat = [{
$target `:` qualified(type(operands)) attr-dict
$target `:` type(operands) attr-dict
}];
}

Expand Down Expand Up @@ -825,23 +837,6 @@ def quake_RightTeeOp : QuakeOp<"right_tee", [Pure]> {
let hasVerifier = 1;
}

def quake_SinkCableOp : QuakeOp<"sink_cable"> {
let summary = "Sink a cable of quantum wires.";
let description = [{
This operation can be used as syntactic sugar for connecting a set of wire
values to a series of `quake.sink` operations. It can be used, possibly, to
declutter code by combining a long sequence of `quake.sink` operations.
}];

let arguments = (ins
Arg<CableType, "cable destroyed", [MemRead, MemWrite]>:$cable
);

let assemblyFormat = [{
$cable `:` type($cable) attr-dict
}];
}

//===----------------------------------------------------------------------===//
// Qubit assignment: WireSet, BorrowWire, ReturnWire
//===----------------------------------------------------------------------===//
Expand Down
19 changes: 13 additions & 6 deletions include/cudaq/Optimizer/Dialect/Quake/QuakeTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class QuakeType<string name, string typeMnemonic, list<Trait> traits = [],
//===----------------------------------------------------------------------===//

def WireType : QuakeType<"Wire", "wire"> {
let summary = "A quantum value. Wires are a linear type.";
let summary = "a linear type for a quantum value";
let description = [{
A `wire` is a primitive quantum "SSA-value" that differs from a traditional
SSA-value in that it can and will be modified when used in any target
Expand Down Expand Up @@ -66,7 +66,7 @@ def WireType : QuakeType<"Wire", "wire"> {
//===----------------------------------------------------------------------===//

def ControlType : QuakeType<"Control", "control"> {
let summary = "A quantum control value.";
let summary = "an SSA type for a quantum control value";
let description = [{
A value having type `control` is quite similar to a value of type `wire`.
The distinction is that a value of type `control` must be used in a control
Expand All @@ -92,7 +92,7 @@ def ControlType : QuakeType<"Control", "control"> {
//===----------------------------------------------------------------------===//

def RefType : QuakeType<"Ref", "ref"> {
let summary = "reference to a quantum wire";
let summary = "a reference to a quantum wire";
let description = [{
A `ref` represents a reference to a value of `wire` type. One can view the
values of this type as the horizontal lines in the following quantum circuit
Expand Down Expand Up @@ -138,7 +138,7 @@ def RefType : QuakeType<"Ref", "ref"> {
//===----------------------------------------------------------------------===//

def VeqType : QuakeType<"Veq", "veq"> {
let summary = "an aggregate of quantum references";
let summary = "an sequence of quantum references";
let description = [{
A value of type `veq` is a (linear) collection of values of type `ref`.
These aggregates are a convenience for referring to an entire group of
Expand Down Expand Up @@ -171,7 +171,7 @@ def VeqType : QuakeType<"Veq", "veq"> {
//===----------------------------------------------------------------------===//

def StruqType : QuakeType<"Struq", "struq"> {
let summary = "a product type of quantum references";
let summary = "a flattened product type of quantum references";
let description = [{
This type allows one to group veqs of quantum references together in a
single product type.
Expand Down Expand Up @@ -210,11 +210,18 @@ def StruqType : QuakeType<"Struq", "struq"> {
}

def CableType : QuakeType<"Cable", "cable"> {
let summary = "the type of a sequence of wires bound together";
let summary = "a sequence type for an aggregate of wires";
let description = [{
This is the type of an aggregate of wire types. It is simply syntactic
sugar that allows a group of wires to be passed around as a single cable
value.

There is a mapping from any constant sized quantum reference type to a cable
type. A `!quake.ref` can map to a `!quake.cable<1>`. A `!quake.veq<n>` can
be mapped to a `!quake.cable<n>` retaining relative position and indices. A
`!quake.struq<{m0, ...}>` can be mapped left-to-right in member order. Each
member must be a `!quake.ref` or a `!quake.veq<n>` and will be mapped as
above with each member concatenating to the previous member.
}];

let parameters = (ins "std::uint64_t":$size);
Expand Down
6 changes: 3 additions & 3 deletions include/cudaq/Optimizer/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ void createTargetFinalizePipeline(mlir::OpPassManager &pm);
/// Helper function for adding the `decompositon` pass as pass options of type
/// ListOption may not always be initialized properly resulting in mystery
/// crashes.
void addDecompositionPass(
mlir::OpPassManager &pm, mlir::ArrayRef<std::string> enabledPats,
mlir::ArrayRef<std::string> disabledPats = std::nullopt);
void addDecomposition(mlir::OpPassManager &pm,
mlir::ArrayRef<std::string> enabledPats,
mlir::ArrayRef<std::string> disabledPats = std::nullopt);

void registerAOTPipelines();
void registerJITPipelines();
Expand Down
75 changes: 51 additions & 24 deletions include/cudaq/Optimizer/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def ArgumentSynthesis : Pass<"argument-synthesis", "mlir::ModuleOp"> {
"mlir::cf::ControlFlowDialect"];
}

def BasisConversionPass : Pass<"basis-conversion", "mlir::ModuleOp"> {
def BasisConversion : Pass<"basis-conversion", "mlir::ModuleOp"> {
let summary = "Converts kernels to a set of basis operations.";
let description = [{
This pass takes as input a list of target (allowed) quantum operations.
Expand Down Expand Up @@ -160,6 +160,19 @@ def BasisConversionPass : Pass<"basis-conversion", "mlir::ModuleOp"> {
];
}

def CableRoughIn : Pass<"cable-rough-in", "mlir::func::FuncOp"> {
let summary = "Perform the rough-in cabling for value semantics conversion.";
let description = [{
Some higher-level aspects of the IR, such as function calls, may not be
obviously reducible to quake's value semantics form. This can impede other
passes and analyses.

This prerequisite pass will "rough-in" cabling abstractions, which are part
of value semantics to reduce the switching cost of having both value and
reference semantics present at the same time in the IR.
}];
}

def CheckKernelCalls : Pass<"check-kernel-calls", "mlir::func::FuncOp"> {
let summary = "Check calls between quantum kernels have been inlined.";
let description = [{
Expand All @@ -183,9 +196,8 @@ def ClassicalOptimization : Pass<"classical-optimization"> {
- cc-loop-unroll
}];

let dependentDialects = ["mlir::arith::ArithDialect",
"mlir::cf::ControlFlowDialect",
"cudaq::cc::CCDialect"];
let dependentDialects = ["mlir::arith::ArithDialect", "cudaq::cc::CCDialect",
"mlir::cf::ControlFlowDialect"];

let options = [
Option<"threshold", "maximum-iterations", "unsigned", /*default=*/"50",
Expand Down Expand Up @@ -260,7 +272,8 @@ def ConvertToDirectCalls : Pass<"indirect-to-direct-calls", "mlir::ModuleOp"> {
}];
}

def ResourceCountPreprocess : Pass<"resource-count-preprocess", "mlir::func::FuncOp"> {
def ResourceCountPreprocess :
Pass<"resource-count-preprocess", "mlir::func::FuncOp"> {
let summary = "Performs preprocessing to optimize for resource counter.";
let description = [{
This pass performs preprocessing to optimize the code for the resource
Expand Down Expand Up @@ -311,7 +324,7 @@ def DeadStoreRemoval : Pass<"dead-store-removal"> {
}];
}

def DecompositionPass: Pass<"decomposition", "mlir::ModuleOp"> {
def Decomposition : Pass<"decomposition", "mlir::ModuleOp"> {
let summary = "Break down quantum operations.";
let description = [{
This pass decomposes quantum operations by iteratively applying rewrite
Expand Down Expand Up @@ -938,8 +951,8 @@ def MemToReg : Pass<"memtoreg", "mlir::func::FuncOp"> {
];
}

def MultiControlDecompositionPass: Pass<"multicontrol-decomposition",
"mlir::func::FuncOp"> {
def MultiControlDecomposition :
Pass<"multicontrol-decomposition", "mlir::func::FuncOp"> {
let summary = "Break down multi-control quantum operations.";
let description = [{
This pass decomposes multi-control quantum operations. The decompostion
Expand Down Expand Up @@ -1092,12 +1105,13 @@ def QuakeAddMetadata : Pass<"quake-add-metadata", "mlir::func::FuncOp"> {
let constructor = "cudaq::opt::createQuakeAddMetadata()";
}

def QuakePropagateMetadata : Pass<"quake-propagate-metadata", "mlir::ModuleOp"> {
def QuakePropagateMetadata :
Pass<"quake-propagate-metadata", "mlir::ModuleOp"> {
let summary = "Propagate metadata from callees to callers when needed.";
let description = [{
This pass propagates function metadata attributes added by `quake-add-metadata`
pass, such as `qubitMeasurementFeedback`, to all the callers of the function.
This makes sure the attributes are not missing after inlining.
Propagates function metadata attributes added by `quake-add-metadata` pass,
such as `qubitMeasurementFeedback`, to all the callers of the function. This
insures these attributes are not missing after inlining.
}];
}

Expand All @@ -1120,17 +1134,26 @@ def RegToMem : Pass<"regtomem", "mlir::func::FuncOp"> {
where the wire cannot be wrapped uniquely. In those cases, this
transformation will take no action and leave the IR (partially) with wire
type values.

Note that, it is entirely possible for quake to obtain a cyclic structure
and otherwise reuse values of type `wire`. In the general case, there is
not necessarily a way to recover a DAG from a graph with arbitrary cycles.
However, if we assume the quake code fully enumerates a finite set of wires
(which is currently true by construction), it is theoretically possible to
use cloning to recover such a finite DAG representation. This pass does not,
however, attempt to perform such heroics.
}];
let dependentDialects = ["cudaq::cc::CCDialect", "quake::QuakeDialect"];
}

def ReplaceStateWithKernel : Pass<"replace-state-with-kernel", "mlir::func::FuncOp"> {
let summary =
"Replace `quake.init_state` instructions with call to the kernel generating the state";
def ReplaceStateWithKernel : Pass<"replace-state-with-kernel",
"mlir::func::FuncOp"> {
let summary = "Replace `init_state` operations with calls to a kernel.";
let description = [{
This optimization replaces `quake.init_state`, `quake.get_number_of_qubits`,
and `quake.materialize_state` operations invoked on state pointers during
argument synthesis for quantum devices.
Replaces `quake.init_state`, `quake.get_number_of_qubits`, and
`quake.materialize_state` operations invoked on state pointers during
argument synthesis for quantum devices with appropriate calls to support
routines and/or kernels to build the state.

Before this optimization, argument synthesis for state pointers for quantum
devices substituted a state created from the `quake.materialize_state`
Expand Down Expand Up @@ -1383,13 +1406,17 @@ def WriteAfterWriteElimination : Pass<"write-after-write-elimination"> {
}];
}

def QubitResetBeforeReuse : Pass<"qubit-reset-before-reuse", "mlir::func::FuncOp"> {
let summary = "Add qubit reset and conditional initialization after measurement if qubit is to be reused.";
def QubitResetBeforeReuse :
Pass<"qubit-reset-before-reuse", "mlir::func::FuncOp"> {
let summary = "After measurement of a ref, add reset (and initialization).";
let description = [{
This pass adds qubit reset and conditionally applies an X gate if the measurement result is 1
to initialize qubit into the correct state after measurement. This is only activated when the measured qubit
is to be reused.
Note: if the measurement is already accompanied by a reset, we won't add any extra reset.
Adds a `quake.reset` and conditionally applies an X gate if the measurement
result is 1 to initialize the reference back into the just measured state
afterward. This is only activated when the measured reference is to be
reused.

Note: if the measurement is already accompanied by a reset, this pass does
not add any additional operations.
}];

}
Expand Down
2 changes: 1 addition & 1 deletion lib/Frontend/nvqpp/ConvertExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1872,7 +1872,7 @@ bool QuakeBridgeVisitor::VisitCallExpr(clang::CallExpr *x) {
// Loop over the ctrlValues and negate (apply an XOp) those in the
// negations list.
if (auto concat = ctrlValues.getDefiningOp<quake::ConcatOp>()) {
for (auto v : concat.getQbits())
for (auto v : concat.getTargets())
if (std::find(negations.begin(), negations.end(), v) !=
negations.end()) {
if (isa<quake::VeqType>(v.getType())) {
Expand Down
Loading
Loading