Skip to content
Merged
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
5 changes: 5 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
* The `ResourceAnalysis` pass now supports IR in reference semantics natively, rather than requiring a conversion step.
[(#2923)](https://github.com/PennyLaneAI/catalyst/pull/2923)


* The `--adjoint-lowering` pass no longer turns statically bounded for loops into
dynamically bounded ones. In this way they remain analyzable by functionality like `qp.specs`.
[(#2959)](https://github.com/PennyLaneAI/catalyst/issues/2959)

* The `--decompose-lowering` pass can now handle decomposition rule functions whose quantum register
argument is at an arbitrary position in the argument list.
[(#2836)](https://github.com/PennyLaneAI/catalyst/pull/2836)
Expand Down
1 change: 0 additions & 1 deletion mlir/include/Quantum/Transforms/Patterns.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ mlir::Value getGlobalString(mlir::Location loc, mlir::OpBuilder &rewriter, mlir:

void populateGridsynthPatterns(mlir::RewritePatternSet &patterns, double epsilon, bool pprBasis);
void populateQIRConversionPatterns(mlir::TypeConverter &, mlir::RewritePatternSet &, bool);
void populateAdjointPatterns(mlir::RewritePatternSet &);
void populateCancelInversesPatterns(mlir::RewritePatternSet &);
void populateMergeRotationsPatterns(mlir::RewritePatternSet &);
void populateIonsDecompositionPatterns(mlir::RewritePatternSet &);
Expand Down
88 changes: 0 additions & 88 deletions mlir/include/Quantum/Utils/QuantumSplitting.h

This file was deleted.

103 changes: 103 additions & 0 deletions mlir/lib/Quantum/Transforms/AdjointLowering/AdjointLowering.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2026 Xanadu Quantum Technologies Inc.

// 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.

#include "AdjointLowering.hpp"

#include "llvm/ADT/STLExtras.h"
#include "mlir/Dialect/Complex/IR/Complex.h"
#include "mlir/Dialect/Index/IR/IndexDialect.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/IRMapping.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"

#include "Quantum/IR/QuantumOps.h"

#include "QuantumCache.hpp"

using namespace mlir;
using namespace catalyst::quantum;

namespace {

/// Orchestrates the adjoint lowering of a single `quantum.adjoint` op by running the forward pass
/// (recording the augmented circuit) followed by the reverse pass (emitting the adjoint circuit).
struct AdjointSingleOpRewritePattern : public OpRewritePattern<AdjointOp> {
using OpRewritePattern<AdjointOp>::OpRewritePattern;

/// We build a map from values mentioned in the source data flow to the values of
/// the program where quantum control flow is reversed. Most of the time, there is a 1-to-1
/// correspondence with a notable exception caused by `insert`/`extract` API asymmetry.
LogicalResult matchAndRewrite(AdjointOp adjoint, PatternRewriter &rewriter) const override
{
QuantumCache cache =
QuantumCache::initialize(adjoint.getRegion(), rewriter, adjoint.getLoc());

// Forward pass: copy the classical computations to the target insertion point and record
// the values needed to replay the circuit in reverse.
IRMapping oldToCloned;
generateAdjointForwardPass(adjoint.getRegion(), rewriter, oldToCloned, cache);

// Seed the reverse pass with the operands of the quantum.yield.
auto yieldOp = cast<YieldOp>(adjoint.getRegion().front().getTerminator());
for (auto [yieldVal, adjointOperand] :
llvm::zip_equal(yieldOp.getOperands(), adjoint.getArgs())) {
oldToCloned.map(yieldVal, adjointOperand);
}

// Reverse pass: emit the adjoint quantum operations and reversed control flow, using the
// cached values.
if (failed(generateAdjointReversePass(adjoint.getRegion(), rewriter, oldToCloned, cache))) {
return failure();
}

// Explicitly free the memory of the caches.
cache.emitDealloc(rewriter, adjoint.getLoc());
// The final quantum values are the re-mapped region arguments of the original adjoint op.
SmallVector<Value> reversedOutputs;
for (BlockArgument arg : adjoint.getRegion().getArguments()) {
reversedOutputs.push_back(oldToCloned.lookup(arg));
}
rewriter.replaceOp(adjoint, reversedOutputs);
return success();
}
};

} // namespace

namespace catalyst {
namespace quantum {

#define GEN_PASS_DEF_ADJOINTLOWERINGPASS
#include "Quantum/Transforms/Passes.h.inc"

struct AdjointLoweringPass : impl::AdjointLoweringPassBase<AdjointLoweringPass> {
using AdjointLoweringPassBase::AdjointLoweringPassBase;

void runOnOperation() final
{
RewritePatternSet patterns(&getContext());
patterns.add<AdjointSingleOpRewritePattern>(patterns.getContext(), 1);

if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {
return signalPassFailure();
}
}
};

} // namespace quantum
} // namespace catalyst
45 changes: 45 additions & 0 deletions mlir/lib/Quantum/Transforms/AdjointLowering/AdjointLowering.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2026 Xanadu Quantum Technologies Inc.

// 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.

#pragma once

#include "mlir/IR/IRMapping.h"

#include "QuantumCache.hpp"

namespace catalyst {
namespace quantum {

/// Generate the forward pass of the adjoint region, i.e. the classical portion (forwards).
///
/// This generates the forward computation: Classical preprocessing is cloned to the current
/// insertion point, and the information needed to deterministically replay the circuit in
/// reverse (gate parameters, dynamic wires, and control-flow structure) is recorded into the
/// `QuantumCache`. The reverse pass later consumes this cache.
void generateAdjointForwardPass(mlir::Region &region, mlir::OpBuilder &builder,
mlir::IRMapping &oldToCloned, QuantumCache &cache);

/// Generate the reverse pass of the adjoint region, i.e. the quantum portion (in reverse).
///
/// This generates the reverse computation: the quantum operations are cloned in reverse order
/// with the adjoint attribute applied, with any control flow reversed, and using recorded
/// values from the cache.
/// The `IRMapping` helps associating values in the original program to their counterpart in the
/// reversed program. It must be seeded with the adjoint operands before calling.
mlir::LogicalResult generateAdjointReversePass(mlir::Region &region, mlir::OpBuilder &builder,
mlir::IRMapping &remappedValues,
QuantumCache &cache);

} // namespace quantum
} // namespace catalyst
Loading
Loading