Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
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
34 changes: 26 additions & 8 deletions Standard/src/Diagnostics/Allows.qs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ namespace Microsoft.Quantum.Diagnostics {
/// Between a call to this operation and its adjoint, asserts that
/// a given operation is called at most a certain number of times.
///
/// Operation calls are considered, if they contain the the specified
/// variant. For example, if `op` is `X`, `Adjoint X` or `Controlled X`
/// are also counted, but if `op` is `Controlled X`, only `Controlled X`
/// or `Controlled Adjoint X` are counted.
///
/// # Input
/// ## nTimes
/// The maximum number of times that `op` may be called.
Expand All @@ -19,14 +24,27 @@ namespace Microsoft.Quantum.Diagnostics {
/// The following snippet will fail when executed on machines which
/// support this diagnostic:
/// ```qsharp
/// using (register = Qubit[4]) {
/// within {
/// AllowAtMostNCallsCA(3, H, "Too many calls to H.");
/// } apply {
/// // Fails since this calls H four times, rather than the
/// // allowed maximum of three.
/// ApplyToEach(H, register);
/// }
/// within {
/// AllowAtMostNCallsCA(3, H, "Too many calls to H.");
/// } apply {
/// use register = Qubit[4];
/// // Fails since this calls H four times, rather than the
/// // allowed maximum of three.
/// ApplyToEach(H, register);
/// }
/// ```
///
/// Another example illustrates how restricted calls are handled.
/// ```qsharp
/// within {
/// // Both tests will pass in this case
/// AllowAtMostNCallsCA(1, Controlled H, "Too many calls to Controlled H.");
/// AllowAtMostNCallsCA(2, H, "Too many calls to H or Controlled H.");
/// } apply {
/// use (a, b) = (Qubit(), Qubit());
/// H(a);
/// Controlled H([a], b);
/// ResetAll([a, b]);
/// }
/// ```
///
Expand Down
15 changes: 14 additions & 1 deletion Standard/src/Diagnostics/Emulation/AllowOperationCalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ public Native(IOperationFactory m) : base(m)
bool IsSelf(ICallable callable) =>
callable.FullName == "Microsoft.Quantum.Diagnostics.AllowAtMostNCallsCA";

// Partial ordering on two variants; returns true, if `lhs` is included in `rhs`.
// For example, Body <= Body, and Body <= Adjoint, but Controlled is not less than or equal Adjoint.
bool LessThanOrEqual(OperationFunctor lhs, OperationFunctor rhs) => lhs switch {
OperationFunctor.ControlledAdjoint => rhs == OperationFunctor.ControlledAdjoint,
OperationFunctor.Controlled => rhs == OperationFunctor.ControlledAdjoint || rhs == OperationFunctor.Controlled,
OperationFunctor.Adjoint => rhs == OperationFunctor.ControlledAdjoint || rhs == OperationFunctor.Adjoint,
_ => true // OperationFunctor.Body
};

// Record whether or not the condition checked by this allow
// has failed, so that we can property unwind in the endOperation
// handler below.
Expand All @@ -65,7 +74,11 @@ bool IsSelf(ICallable callable) =>
{
if (IsSelf(callable)) return;
callStack = callStack.Push(callable.FullName);
if (callable.FullName == op.FullName)
// `callable` is callable we just entered on the call stack, `op` is the callable
// that we are monitoring with AllowAtMostNCallsCA. We only increment the counter,
// if both callables have the same fully qualified name and if the variant of `op`
// is less restrictive than the one of `callable`.
if (callable.FullName == op.FullName && LessThanOrEqual(op.Variant, callable.Variant))
{
callSites = callSites.Add(callStack);
if (callSites.Count > nTimes)
Expand Down
35 changes: 35 additions & 0 deletions Standard/tests/Diagnostics/AllowTests.qs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,41 @@ namespace Microsoft.Quantum.Tests {
}
}

@Diag.Test("QuantumSimulator")
operation CheckAllowNCallsDistinguishControlled() : Unit {
within {
Diag.AllowAtMostNCallsCA(1, Controlled X, "Too many calls to Controlled X.");
} apply {
use (q1, q2) = (Qubit(), Qubit());
// Should use one Controlled X, exactly as many times as allowed.
// X will not be accounted for.
X(q2);
Controlled X([q1], q2);
X(q2);
}
}

@Diag.Test("QuantumSimulator")
operation CheckAllowNCallsDistinguishAdjoint() : Unit {
within {
Diag.AllowAtMostNCallsCA(4, S, "Too many calls to S.");
Diag.AllowAtMostNCallsCA(2, Adjoint S, "Too many calls to Adjoint S.");
Diag.AllowAtMostNCallsCA(2, Controlled S, "Too many calls to Controlled S.");
Diag.AllowAtMostNCallsCA(1, Adjoint Controlled S, "Too many calls to Adjoint Controlled S.");
} apply {
use (q1, q2) = (Qubit(), Qubit());
// Should use two Adjoint S (one via Adjoint Controlled S), but exactly
// one Adjoint Controlled S.
S(q1);
Controlled S([q1], q2);
Adjoint Controlled S([q2], q1);
Adjoint S(q1);

Reset(q1);
Reset(q2);
}
}

@Diag.Test("ToffoliSimulator")
operation CheckAllowNQubitsWithNestedCalls() : Unit {
// Here, the total number of allocated qubits exceeds our policy,
Expand Down