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
8 changes: 8 additions & 0 deletions src/QirRuntime/lib/QIR/callables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ void QirCallable::Invoke(PTuple args, PTuple result)
}
}

void QirCallable::Invoke()
{
assert((this->appliedFunctor & QirCallable::Controlled) == 0 && "Cannot invoke controlled callable without args");
PTuple args = quantum__rt__tuple_create(0);
this->Invoke(args, nullptr);
quantum__rt__tuple_update_reference_count(args, -1);
}

// A + A = I; A + C = C + A = CA; C + C = C; CA + A = C; CA + C = CA
void QirCallable::ApplyFunctor(int functor)
{
Expand Down
15 changes: 4 additions & 11 deletions src/QirRuntime/lib/QIR/conditionals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
#include "QirTypes.hpp"
#include "quantum__rt.hpp"

static void Apply(QirCallable* clb)
{
PTuple argsTuple = quantum__rt__tuple_create(0);
quantum__rt__callable_invoke(clb, argsTuple /*args*/, nullptr /*result*/);
quantum__rt__tuple_update_reference_count(argsTuple, -1);
}

static bool ArraysContainEqualResults(QirArray* rs1, QirArray* rs2)
{
assert(rs1 != nullptr && rs2 != nullptr && rs1->count == rs2->count);
Expand All @@ -38,8 +31,8 @@ extern "C"
{
void quantum__qis__applyifelseintrinsic__body(RESULT* r, QirCallable* clbOnZero, QirCallable* clbOnOne)
{
QirCallable* clbApply = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne;
Apply(clbApply);
QirCallable* clb = quantum__rt__result_equal(r, quantum__rt__result_zero()) ? clbOnZero : clbOnOne;
clb->Invoke();
}

void quantum__qis__applyconditionallyintrinsic__body(
Expand All @@ -48,7 +41,7 @@ extern "C"
QirCallable* clbOnAllEqual,
QirCallable* clbOnSomeDifferent)
{
QirCallable* clbApply = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent;
Apply(clbApply);
QirCallable* clb = ArraysContainEqualResults(rs1, rs2) ? clbOnAllEqual : clbOnSomeDifferent;
clb->Invoke();
}
}
26 changes: 15 additions & 11 deletions src/QirRuntime/lib/Tracer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,24 @@ As the tracer is executing a sequential quantum program, it will compute a time
using the _conceptual_ algorithm, described below (aka "tetris algorithm"). The actual implementation of layering might
be done differently, as long as the resulting layering is the same as if running the conceptual algorithm.

A ___barrier___ is a layer that acts as if it was containing all currently allocated qubits and no operation can be added
into it.

A user can inject _barriers_ by calling `__quantum__qis__global_barrier` function. The user can choose duration of
a barrier which would affect start time of the following layers but no operations will be added to a barrier,
independent of its duration.
A layer _L(T,N)_ acts as a ___fence___ if it does _not_ accepts any new operations, even if these operations don't
involve qubits from _Qubits(T,N)_.

__Conditional execution on measurement results__: The Tracer will execute LLVM IR's branching structures "as is",
depending on the values of the corresponding variables at runtime. To enable estimation of branches that depend on a
measurement result, the source Q# program will have to be authored in such a way that the Q# compiler will translate the
conditionals into corresponding callbacks to the tracer. The tracer will add operations from _both branches_ into the
layers it creates to compute the upper bound estimate.
measurement result, the source Q# program must be authored in such a way that the Q# compiler will translate the
conditionals into corresponding callbacks to the tracer (`__quantum__qis__apply_conditionally`). The tracer will
execute _both branches_ of the conditional statement to compute the upper bound estimate. The conditional callbacks
will mark the layers that contain measurements that produced the results used in conditionals as _fences_ for the
duration of the conditional callback.

A user can create special layers that act as permanent _fences_ by calling `__quantum__qis__inject_barrier` function. The
user can choose duration of a barrier which would affect start time of the following layers but no operations will be
added to a barrier, independent of its duration. _Terminology note_: 'fence' is a role of layer, which might be assigned
to a layer temporarily or permanently; 'barrier' is a special layer the user can inject that has the role of a permanent
fence and contains no operations.

The following operations are _not_ supported inside conditional callbacks and would cause a runtime failure:
__TODO__: figure out which operations should or should _not_ be supported inside conditional callbacks. For example:

- nested conditional callbacks;
- measurements;
Expand Down Expand Up @@ -201,7 +205,7 @@ TBD but lower priority.
| `%Result* @__quantum__qis__single_qubit_measure(i32 %id, i32 %duration, %Qubit* %q)` | Function for counting measurements of a single qubit. The user can assign different operation ids for different measurement bases. |
| `%Result* @__quantum__qis__joint_measure(i32 %id, i32 %duration, %Array* %qs)` | Function for counting joint-measurements of qubits. The user can assign different operation ids for different measurement bases. |
| `void __quantum__qis__swap(%Qubit* %q1, %Qubit* %q2)` | See [Special handling of SWAP](#special-handling-of-swap) for details. |
| TODO: handling of conditionals on measurement results | |
| `void __quantum__qis__apply_conditionally(%Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different)` | The first two arguments contain arrays of results to be compared pairwise. The third argument is a callable that represents the branch that would be executed if all results compared equal and the forth argument is a callable that represents the branch that would be executed if any of the results compared different. The tracer executes _both_ branches.|

_Note on operation ids_: The user is responsible for using operation ids in a consistent manner. Operations with the
same id will be counted by the tracer as the _same_ operation, even accross invocations with different number of target
Expand Down
17 changes: 17 additions & 0 deletions src/QirRuntime/lib/Tracer/tracer-bridge.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
; QIR types
;
%Array = type opaque
%Callable = type opaque
%Qubit = type opaque
%Result = type opaque

Expand All @@ -15,6 +16,7 @@
%class.QUBIT = type opaque
%class.RESULT = type opaque
%struct.QirArray = type opaque
%struct.QirCallable = type opaque


;===============================================================================
Expand All @@ -28,6 +30,8 @@ declare void @quantum__qis__multi_qubit_op_ctl(i32 %id, i32 %duration, %struct.Q
declare void @quantum__qis__inject_barrier(i32 %id, i32 %duration)
declare %class.RESULT* @quantum__qis__single_qubit_measure(i32 %id, i32 %duration, %class.QUBIT*)
declare %class.RESULT* @quantum__qis__joint_measure(i32 %id, i32 %duration, %struct.QirArray*)
declare void @quantum__qis__apply_conditionally(
%struct.QirArray*, %struct.QirArray*, %struct.QirCallable*, %struct.QirCallable*)

;===============================================================================
; quantum__trc namespace implementations
Expand Down Expand Up @@ -82,4 +86,17 @@ define %Result* @__quantum__qis__joint_measure(i32 %id, i32 %duration, %Array* %
%r = call %class.RESULT* @quantum__qis__joint_measure(i32 %id, i32 %duration, %struct.QirArray* %qs)
%.r = bitcast %class.RESULT* %r to %Result*
ret %Result* %.r
}

define void @__quantum__qis__apply_conditionally(
%Array* %.rs1, %Array* %.rs2, %Callable* %.clb_on_equal, %Callable* %.clb_on_different) {

%rs1 = bitcast %Array* %.rs1 to %struct.QirArray*
%rs2 = bitcast %Array* %.rs2 to %struct.QirArray*
%clb_on_equal = bitcast %Callable* %.clb_on_equal to %struct.QirCallable*
%clb_on_different = bitcast %Callable* %.clb_on_different to %struct.QirCallable*
call void @quantum__qis__apply_conditionally(
%struct.QirArray* %rs1, %struct.QirArray* %rs2,
%struct.QirCallable* %clb_on_equal, %struct.QirCallable* %clb_on_different)
ret void
}
14 changes: 14 additions & 0 deletions src/QirRuntime/lib/Tracer/tracer-qis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,18 @@ extern "C"
{
return tracer->TraceMultiQubitMeasurement(id, duration, qs->count, reinterpret_cast<Qubit*>(qs->buffer));
}

void quantum__qis__apply_conditionally( // NOLINT
QirArray* rs1,
QirArray* rs2,
QirCallable* clbOnAllEqual,
QirCallable* clbOnSomeDifferent)
{
CTracer::FenceScope sf(
tracer.get(), rs1->count, reinterpret_cast<Result*>(rs1->buffer), rs2->count,
reinterpret_cast<Result*>(rs2->buffer));

clbOnAllEqual->Invoke();
clbOnSomeDifferent->Invoke();
}
}
Loading