diff --git a/specification/under_development/output_schemas/Grammars.md b/specification/under_development/output_schemas/Grammars.md new file mode 100644 index 0000000..c461cb6 --- /dev/null +++ b/specification/under_development/output_schemas/Grammars.md @@ -0,0 +1,140 @@ +# ABNF Grammars For Schema Definitions + +The ABNF grammars for the [Labeled and Async](./Labeled_And_Async.md) and [No Labels and Ordered](./No_Labels_And_Ordered.md) define the technical content of the files. The details of the QIR calls and how they map to the input is coverred in the associated specifications. Type consistency cannot be defined for array elements and is a validation concern when consuming output. + +## Grammars + +### Shared Top Level + +The grammars share a basic structure and only vary in their definition of values, tuples, and arrays. The top level definitions are the same: + +```abnf +file = shot *(EOL shot) [EOL] + +shot = start *(EOL record) EOL end + +record = metadata / output + +metadata = METADATA-LIT TAB field [TAB field] + +output = (container / value) + +container = (tuple / array) +``` + +### Labeled and Async: + +```abnf +value = output-start (result / bool / int / double) TAB label + +tuple = output-start TUPLE-LIT TAB 1*DIGIT TAB label + +array = output-start ARRAY-LIT TAB 1*DIGIT TAB label +``` + +### No Labels and Ordered + +```abnf +value = output-start (result / bool / int / double) + +tuple = output-start tuple-start 1*collection-items + +tuple-start = TUPLE-LIT TAB 1*DIGIT + +array = output-start array-start collection-items + +array-start = ARRAY-LIT TAB 1*DIGIT + +collection-items = array-items / tuple-items / value-items + +value-items = 1*(EOL value) + +array-items = 1*(EOL array) + +tuple-items = 1*(EOL tuple) +``` + +### Shared Core Definitions + +```abnf +output-start = OUTPUT-LIT TAB + +result = RESULT-LIT TAB BIT + +bool = BOOL-LIT TAB (TRUE-LIT / FALSE-LIT) + +int = INT_LIT TAB [sign] 1*DIGIT + +double = DOUBLE-LIT TAB DOUBLE-VALUE + +DOUBLE-VALUE = [sign] (inf / nan / float) + +inf = INF-LIT / INFINITY-LIT + +nan = NAN-LIT + +float = ( 1*DIGIT / (*DIGIT "." 1*DIGIT) ) [exponent] + +exponent = "e" [sign] 1*DIGIT + +sign = "+" / "-" + +field = (escaped / non-escaped) + +label = (escaped / non-escaped) + +escaped = DQUOTE non-escaped DQUOTE + +non-escaped = *TEXTDATA + +TAB = %x09 + +LF = %x0A + +CR = %x0D + +CRLF = CR LF + +EOL = (LF / CR / CRLF) + +DQUOTE = %x22 + +; Codes %x20 to %x7E are known as the ASCII printable characters +; %x22, DQUOTE ("), is omitted here so that it can be the +; used to define escaped text. +TEXTDATA = %x20-21 / %x23-7E + +DIGIT = %x30-39 + +BIT = "0" / "1" + +start = START-LIT + +end = END-LIT TAB "0" + +TRUE-LIT = "T" "R" "U" "E" + +FALSE-LIT = "F" "A" "L" "S" "E" + +INF-LIT = "I" "N" "F" + +INFINITY-LIT = "I" "N" "F" "I" "N" "I" "T" "Y" + +NAN-LIT = "N" "A" "N" + +START-LIT = "S" "T" "A" "R" "T" + +METADATA-LIT = "M" "E" "T" "A" "D" "A" "T" "A" + +OUTPUT-LIT = "O" "U" "T" "P" "U" "T" + +BOOL-LIT = "B" "O" "O" "L" + +INT-LIT = "I" "N" "T" + +DOUBLE-LIT = "D" "O" "U" "B" "L" "E" + +RESULT-LIT = "R" "E" "S" "U" "L" "T" + +END-LIT = "E" "N" "D" +``` \ No newline at end of file diff --git a/specification/under_development/output_schemas/Labeled_And_Async.md b/specification/under_development/output_schemas/Labeled_And_Async.md index efd4d88..099c1d5 100644 --- a/specification/under_development/output_schemas/Labeled_And_Async.md +++ b/specification/under_development/output_schemas/Labeled_And_Async.md @@ -1,2 +1,245 @@ # Labeled Format for Asynchronous Output Generation +The labeled format for asynchronous output generation is the same as the [ordered format][] with the following changes: + +- `OUTPUT` records `TUPLE`, `ARRAY`, `RESULT`, `INT`, `BOOL`, and `DOUBLE` types have a fourth field indicating the label of the record. + +A [grammar](./Grammars.md#labeled-and-async) for this format is available which defines the structure and valid values. + +Labels are needed for reconstruction of asynchronous/parallel computation output and are assigned by the front-end QIR generator. Order is not important for the `OUTPUT` entries within a `START`/`END` block; however, the responsibility of reconstructing the output based on the defined labeling scheme belongs to the party permforming the output labeling. The usage of `t0_0a` and `t2_2a` (and other values) are examples of a labeling scheme, and are only used as an example. + +Consumers of the QIR need to map the associated labels for each recording call to its output entry label. The [base profile required attributes](../profiles/Base_Profile.md#attributes) define the minimum set of attributes which will appear. Examples can be found in the [notes for implementors examples](./Notes_For_Implementors.md#examples). + +Example log for a single shot: + +```log +START +METADATA\tentry_point +METADATA\tnum_required_qubits\t5 +METADATA\tnum_required_results\t5 +METADATA\toutput_labeling_schema +METADATA\tqir_profiles\tbase_profile +OUTPUT\tTUPLE\t2\t0_t +OUTPUT\tARRAY\t4\t1_t0a +OUTPUT\tRESULT\t0\t2_t0a0r +OUTPUT\tRESULT\t0\t3_t0a1r +OUTPUT\tRESULT\t0\t4_t0a2r +OUTPUT\tRESULT\t0\t5_t0a3r +OUTPUT\tTUPLE\t3\t6_t1t +OUTPUT\tBOOL\ttrue\t7_t1t0b +OUTPUT\tINT\t42\t8_t1t1i +OUTPUT\tDOUBLE\t3.1415\t9_t1t2d +END\t0 +``` + +## Declared Runtime Functions + +Each of these runtime functions are meant as signifiers to the provider as to how raw device results should be collected into the common output format. For simulation or future environments with full runtime support, these functions can be linked to implementations that directly perform the recording of output to the relevant stream. Each of these functions follow the naming pattern `__quantum__rt__*_record_output` where the initial part indicates the type of output to be recorded. + +### Primitive Result Records + +```llvm +void @__quantum__rt__result_record_output(%Result*, i8*) +``` + +Adds a measurement result to the generated output. It produces output records of exactly `"OUTPUT\tRESULT\t0\tlabel"` or `"OUTPUT\tRESULT\t1\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +```llvm +void @__quantum__rt__bool_record_output(i1, i8*) +``` + +produces output records of exactly `"OUTPUT\tBOOL\tfalse\tlabel"` or `"OUTPUT\tBOOL\ttrue\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +```llvm +void @__quantum__rt__integer_record_output(i64, i8*) +``` + +produces output records of the format `"OUTPUT\tINT\tn\tlabel"` where `n` is the string representation of the integer value, such as `"OUTPUT\tINT\t42\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +```llvm +void @__quantum__rt__double_record_output(double, i8*) +``` + +produces output records of the format `"OUTPUT\tDOUBLE\td\tlabel"` where `d` is the string representation of the double value, such as `"OUTPUT\tDOUBLE\t3.14159\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +### Tuple Type Records + +```llvm +void @__quantum__rt__tuple_record_output(i64, i8*) +``` + +Inserts a marker in the generated output that indicates the start of a tuple and how many tuple elements it has. It produces output records of exactly `"OUTPUT\tTUPLE\tn\tlabel"` where `n` is the string representation of the integer value, such as `"OUTPUT\tTUPLE\t4\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +### Array Type Records + +```llvm +void @__quantum__rt__array_record_output(i64, i8*) +``` + +Inserts a marker in the generated output that indicates the start of an array and how many array elements it has. It produces output records of exactly `"OUTPUT\tARRAY\tn\tlabel"` where `n` is the string representation of the integer value, such as `"OUTPUT\tARRAY\t4\tlabel"`. The second parameter defines a string label for the result value which is included in the output (`label`). + +## Examples + +### Basic Single Item Output + +The QIR program would include a single call to the runtime function matching the type of the return value: + +```llvm +@0 = internal constant [3 x i8] c"0_i\00" +call void @__quantum__rt__integer_record_output(i64 %5, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @0, i32 0, i32 0)) +ret void +``` + +Such that the provider output for `3` shots would be: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t42\t0_i +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t41\t0_i +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t42\t0_i +END\t0 +``` + +## Measurement Result Array Output + +For an array of measurement results (or classical values), the QIR program would include an array output recording call, where the first argument indicates the length of the array, followed by the corresponding output recording calls that represent each one of the array items (shown with static result allocation transformations performed): + +```llvm +@0 = internal constant [5 x i8] c"0_0a\00" +@1 = internal constant [7 x i8] c"1_0a0r\00" +@2 = internal constant [5 x i8] c"2_1a\00" +@3 = internal constant [7 x i8] c"3_1a0r\00" +@4 = internal constant [7 x i8] c"4_1a1r\00" +call void @__quantum__rt__array_record_output(i64 1, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @2, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @3, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @4, i32 0, i32 0)) +ret void +``` + +This would produce provider output for `3` shots of the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1\t0_0a +OUTPUT\tRESULT\t0\t1_0a0r +OUTPUT\tARRAY\t2\t2_1a +OUTPUT\tRESULT\t1\t3_1a0r +OUTPUT\tRESULT\t1\t4_1a1r +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1\t0_0a +OUTPUT\tRESULT\t1\t1_0a0r +OUTPUT\tARRAY\t2\t2_1a +OUTPUT\tRESULT\t1\t3_1a0r +OUTPUT\tRESULT\t1\t4_1a1r +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1\t0_0a +OUTPUT\tRESULT\t0\t1_0a0r +OUTPUT\tARRAY\t2\t2_1a +OUTPUT\tRESULT\t1\t3_1a0r +OUTPUT\tRESULT\t1\t4_1a1r +END\t0 +``` + +## Tuple Output + +Recording tuple output works much the same way as array output. So, a QIR program that returns a tuple of a measurement result and calculated double value would end with: + +```llvm +@0 = internal constant [4 x i8] c"0_t\00" +@1 = internal constant [6 x i8] c"1_t0r\00" +@2 = internal constant [6 x i8] c"2_t1d\00" +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__double_record_output(double %3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0)) +ret void +``` + +Here producing a provider output for `3` shots of the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2\t0_t +OUTPUT\tRESULT\t0\t1_t0r +OUTPUT\tDOUBLE\t0.42\t2_t1d +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2\t0_t +OUTPUT\tRESULT\t1\t1_t0r +OUTPUT\tDOUBLE\t0.42\t2_t1d +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2\t0_t +OUTPUT\tRESULT\t0\t1_t0r +OUTPUT\tDOUBLE\t0.25\t2_t1d +END\t0 +``` + +## Complex Output + +Combining the above techniques can allow for complex output with nested container types. For example, a program that returns an array of tuples each containing an integer and result might have QIR with a pattern of: + +```llvm +@0 = internal constant [4 x i8] c"0_a\00" +@1 = internal constant [6 x i8] c"1_a0t\00" +@2 = internal constant [8 x i8] c"2_a0t0i\00" +@3 = internal constant [8 x i8] c"3_a0t1r\00" +@4 = internal constant [6 x i8] c"4_a1t\00" +@5 = internal constant [8 x i8] c"5_a1t0i\00" +@6 = internal constant [8 x i8] c"6_a1t1r\00" +call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__integer_record_output(i64 %3, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* null, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @3, i32 0, i32 0)) +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @4, i32 0, i32 0)) +call void @__quantum__rt__integer_record_output(i64 %7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @5, i32 0, i32 0)) +call void @__quantum__rt__result_record_output((%Result* nonnull inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @6, i32 0, i32 0)) +ret void +``` + +Such a QIR program would produce output for a single shot in the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tARRAY\t2\t0_a +OUTPUT\tTUPLE\t2\t1_a0t +OUTPUT\tINT\t42\t2_a0t0i +OUTPUT\tRESULT\t3_a0t1r +OUTPUT\tTUPLE\t2\t4_a1t +OUTPUT\tINT\t33\t5_a1t0i +OUTPUT\tRESULT\t1\6_a1t1r +END\t0 +``` + +[ordered format]: No_Labels_And_Ordered.md diff --git a/specification/under_development/output_schemas/No_Labels_And_Ordered.md b/specification/under_development/output_schemas/No_Labels_And_Ordered.md index 9880e60..8b7ebe6 100644 --- a/specification/under_development/output_schemas/No_Labels_And_Ordered.md +++ b/specification/under_development/output_schemas/No_Labels_And_Ordered.md @@ -1 +1,266 @@ # Ordered Output Format with Optional Labels + +For systems that don't support string parameters, the ordered output format can be used. This format omits any labels that are passed through output recording functions. A [grammar](./Grammars.md#no-labels-and-ordered) for this format is available which defines the structure and valid values. + +The format specifies what the [output recording](../profiles/Base_Profile.md#output-recording) functions emit based on the selected schema and entry point [attributes][]. + +## Record types + +Records are expected to have 1-3 fields: + +- RecordType: an id that identifies the type of content in the record. The only supported RecordTypes will be `START`, `METADATA`, `OUTPUT`, and `END`. + - `START` indicates the beginning of a shot's output definition + - `METADATA` records have a name field and optional value field + - `OUTPUT` records have a type in the set: `RESULT`, `TUPLE`, `ARRAY`, `INT`, `BOOL`, or `DOUBLE`. + - `TUPLE` and `ARRAY` types have a third field indicating the number of items in the container. + - `RESULT`, `INT`, `BOOL`, `DOUBLE` types have a third field indicating the value of the record. + - `END` indicate the completion of a shot's output definition with the exit code for the entry point. + +Output for each shot is expected to have: + +- One `START` record +- `METADATA` records for each of the defined entry point attributes. The generation of these records is not part of the generated QIR, but written as part of the [base profile][] spec as pass-thru metadata. The [base profile required attributes](../profiles/Base_Profile.md#attributes) define the minimum set of attributes which will appear. Examples can be found in the [notes for implementors examples](./Notes_For_Implementors.md#examples). +- One or more `OUTPUT` records +- One `END` record + +The `START` and `END` records define a shot, but are not part of the generated QIR. They are produced as part of the provider's recording of a shot's output. The `START` record does not have any value entry. The `END` record has a value entry of `0`. The `OUTPUT` records will have a value corresponding to the runtime function called, either a result type record or a string representation of a supported primitive type. The `OUTPUT` records must appear in the same order as the calls in the QIR program. + +Example log for a single shot: + +```log +START +METADATA\tuser_metadata1_name_only +METADATA\tuser_metadata2_name\tuser_metadata2_value +METADATA\tuser_metadata3_name\tuser_metadata3_value +METADATA\tentry_point +METADATA\tnum_required_qubits\t5 +METADATA\tnum_required_results\t5 +METADATA\toutput_labeling_schema +METADATA\tqir_profiles\tbase_profile +OUTPUT\tTUPLE\t2 +OUTPUT\tARRAY\t4 +OUTPUT\tRESULT\t0 +OUTPUT\tRESULT\t0 +OUTPUT\tRESULT\t0 +OUTPUT\tRESULT\t0 +OUTPUT\tARRAY\t3 +OUTPUT\tBOOL\ttrue +OUTPUT\tINT\t42 +OUTPUT\tDOUBLE\t3.1415 +END\t0 +``` + +## Declared Runtime Functions + +Each of these runtime functions are meant as signifiers to the provider as to how raw device results should be collected into the common output format. For simulation or future environments with full runtime support, these functions can be linked to implementations that directly perform the recording of output to the relevant stream. Each of these functions follow the naming pattern `__quantum__rt__*_record_output` where the initial part indicates the type of output to be recorded. + +Though the output format is labeled, the label parameters for the output recording calls may still provide values which are treated as if the QIR had specified the label as `i8* null`. + +### Primitive Result Records + +```llvm +void @__quantum__rt__result_record_output(%Result*, i8*) +``` + +Adds a measurement result to the generated output. It produces output records of exactly `"OUTPUT\tRESULT\t0"` or `"OUTPUT\tRESULT\t1"`. The second parameter defines a string label for the result value. Depending on the output schema, the label is included in the output or omitted. + +```llvm +void @__quantum__rt__bool_record_output(i1, i8*) +``` + +produces output records of exactly `"OUTPUT\tBOOL\tfalse"` or `"OUTPUT\tBOOL\ttrue"` + +```llvm +void @__quantum__rt__integer_record_output(i64, i8*) +``` + +produces output records of the format `"OUTPUT\tINT\tn"` where `n` is the string representation of the integer value, such as `"OUTPUT\tINT\t42"` + +```llvm +void @__quantum__rt__double_record_output(double, i8*) +``` + +produces output records of the format `"OUTPUT\tDOUBLE\td"` where `d` is the string representation of the double value, such as `"OUTPUT\tDOUBLE\t3.14159"` + +### Tuple Type Records + +```llvm +void @__quantum__rt__tuple_record_output(i64, i8*) +``` + +Inserts a marker in the generated output that indicates the start of a tuple and how many tuple elements it has. It produces output records of exactly `"OUTPUT\tTUPLE\tn"` where `n` is the string representation of the integer value, such as `"OUTPUT\tTUPLE\t4"`. The second parameter defines a string label for the tuple. Depending on the output schema, the label is included in the output or omitted. + +### Array Type Records + +```llvm +void @__quantum__rt__array_record_output(i64, i8*) +``` + +Inserts a marker in the generated output that indicates the start of an array and how many array elements it has. It produces output records of exactly `"OUTPUT\tARRAY\tn"` where `n` is the string representation of the integer value, such as `"OUTPUT\tARRAY\t4"`. The second parameter defines a string label for the array. Depending on the output schema, the label is included in the output or omitted. + +## Examples + +### Basic Single Item Output + +The QIR program would include a single call to the runtime function matching the type of the return value: + +```llvm +@0 = internal constant [3 x i8] c"0_i\00" +call void @__quantum__rt__integer_record_output(i64 %5, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @0, i32 0, i32 0)) +ret void +``` + +Such that the provider output for `3` shots would be: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t42 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t41 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tINT\t42 +END\t0 +``` + +## Measurement Result Array Output + +For an array of measurement results (or classical values), the QIR program would include an array output recording call, where the first argument indicates the length of the array, followed by the corresponding output recording calls that represent each one of the array items (shown with static result allocation transformations performed): + +```llvm +@0 = internal constant [5 x i8] c"0_0a\00" +@1 = internal constant [7 x i8] c"1_0a0r\00" +@2 = internal constant [5 x i8] c"2_1a\00" +@3 = internal constant [7 x i8] c"3_1a0r\00" +@4 = internal constant [7 x i8] c"4_1a1r\00" +call void @__quantum__rt__array_record_output(i64 1, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([5 x i8], [5 x i8]* @2, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @3, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* nonnull inttoptr (i64 0 to %Result*), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @4, i32 0, i32 0)) +ret void +``` + +This would produce provider output for `3` shots of the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1 +OUTPUT\tRESULT\t0 +OUTPUT\tARRAY\t2 +OUTPUT\tRESULT\t1 +OUTPUT\tRESULT\t1 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1 +OUTPUT\tRESULT\t1 +OUTPUT\tARRAY\t2 +OUTPUT\tRESULT\t1 +OUTPUT\tRESULT\t1 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +OUTPUT\tARRAY\t1 +OUTPUT\tRESULT\t0 +OUTPUT\tARRAY\t2 +OUTPUT\tRESULT\t1 +OUTPUT\tRESULT\t1 +END\t0 +``` + +## Tuple Output + +Recording tuple output works much the same way as array output. So, a QIR program that returns a tuple of a measurement result and calculated double value would end with: + +```llvm +@0 = internal constant [4 x i8] c"0_t\00" +@1 = internal constant [6 x i8] c"1_t0r\00" +@2 = internal constant [6 x i8] c"2_t1d\00" +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* %2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__double_record_output(double %3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0)) +ret void +``` + +Here producing a provider output for `3` shots of the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2 +OUTPUT\tRESULT\t0 +OUTPUT\tDOUBLE\t0.42 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2 +OUTPUT\tRESULT\t1 +OUTPUT\tDOUBLE\t0.42 +END\t0 +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tTUPLE\t2 +OUTPUT\tRESULT\t0 +OUTPUT\tDOUBLE\t0.25 +END\t0 +``` + +## Complex Output + +Combining the above techniques can allow for complex output with nested container types. For example, a program that returns an array of tuples each containing an integer and result might have QIR with a pattern of: + +```llvm +@0 = internal constant [4 x i8] c"0_a\00" +@1 = internal constant [6 x i8] c"1_a0t\00" +@2 = internal constant [8 x i8] c"2_a0t0i\00" +@3 = internal constant [8 x i8] c"3_a0t1r\00" +@4 = internal constant [6 x i8] c"4_a1t\00" +@5 = internal constant [8 x i8] c"5_a1t0i\00" +@6 = internal constant [8 x i8] c"6_a1t1r\00" +call void @__quantum__rt__array_record_output(i64 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0)) +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0)) +call void @__quantum__rt__integer_record_output(i64 %3, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @2, i32 0, i32 0)) +call void @__quantum__rt__result_record_output(%Result* null, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @3, i32 0, i32 0)) +call void @__quantum__rt__tuple_record_output(i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @4, i32 0, i32 0)) +call void @__quantum__rt__integer_record_output(i64 %7, i8* getelementptr inbounds ([8 x i8], [8 x i8]* @5, i32 0, i32 0)) +call void @__quantum__rt__result_record_output((%Result* nonnull inttoptr (i64 1 to %Result*), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @6, i32 0, i32 0)) +ret void +``` + +Such a QIR program would produce output for a single shot in the form: + +```log +START +METADATA\tmetadata1_name_only +METADATA\tmetadata2_name\tmetadata2_value +METADATA\tmetadata3_name\tmetadata3_value +OUTPUT\tARRAY\t2 +OUTPUT\tTUPLE\t2 +OUTPUT\tINT\t42 +OUTPUT\tRESULT\t0 +OUTPUT\tTUPLE\t2 +OUTPUT\tINT\t33 +OUTPUT\tRESULT\t1 +END\t0 +``` + +[base profile]: ../profiles/Base_Profile.md +[attributes]: ../profiles/Base_Profile.md#attributes diff --git a/specification/under_development/output_schemas/Notes_For_Implementors.md b/specification/under_development/output_schemas/Notes_For_Implementors.md new file mode 100644 index 0000000..871dedd --- /dev/null +++ b/specification/under_development/output_schemas/Notes_For_Implementors.md @@ -0,0 +1,156 @@ +# Notes + +## Types + +The types chosen in the output schemas represent the base data types for expressing computation in the context of quantum processing. The `RESULT` and `BOOL` entries, while they could have been expressed as integers, describe core domain concepts that are unambiguous and clear in their intent. + +### Output Type + +The effective ouput type for labeled output formats is determined by the labeling scheme employed as order is not guaranteed. + +For unlabeled output, the output recording calls define an inferred type based on the order in which the output recording calls are made. If the ouput is defined and held within a container type, `TUPLE` or `ARRAY`, then the shot's output type is that containers type. + +`TUPLE` is a container for values that may or may not have the same type. `ARRAY` is a container whose values are intended to be all of the same type. Having mixed values in an `ARRAY` entry has undefined behavior when being processed. + +For output that isn't contained within a container type, the inferred output type is a `TUPLE` whose values are the entries found. + +#### Unlabeled Examples + +The inferred type of the following is `TUPLE(ARRAY[RESULT], ARRAY[RESULT])`: + +```console +START +METADATA entry_point +METADATA num_required_qubits 5 +METADATA num_required_results 5 +METADATA output_labeling_schema +METADATA qir_profiles base_profile +OUTPUT ARRAY 2 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT ARRAY 3 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +END 0 +``` + +The inferred type of the following is also `TUPLE(ARRAY[RESULT], ARRAY[RESULT])` as the `ARRAY` entries are wrapped in a container: + +```console +START +METADATA entry_point +METADATA num_required_qubits 5 +METADATA num_required_results 5 +METADATA output_labeling_schema +METADATA qir_profiles base_profile +OUTPUT TUPLE 2 +OUTPUT ARRAY 2 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT ARRAY 3 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +END 0 +``` + +The inferred type of the following is `TUPLE(ARRAY[RESULT], INT, DOUBLE)`: + +```console +START +METADATA entry_point +METADATA num_required_qubits 5 +METADATA num_required_results 5 +METADATA output_labeling_schema +METADATA qir_profiles base_profile +OUTPUT ARRAY 2 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT INT 5 +OUTPUT DOUBLE -0.5e3 +END 0 +``` + +The inferred type of the following is `ARRAY[ARRAY[RESULT]]`: + +```console +START +METADATA entry_point +METADATA num_required_qubits 5 +METADATA num_required_results 5 +METADATA output_labeling_schema +METADATA qir_profiles base_profile +OUTPUT ARRAY 2 +OUTPUT ARRAY 1 +OUTPUT RESULT 0 +OUTPUT ARRAY 3 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +END 0 +``` + +## Examples + +### Muliple Arrays + +```llvm +%Qubit = type opaque +%Result = type opaque + +define void @main() #0 { +entry: + call void @__quantum__rt__initialize(i8* null) + call void @__quantum__qis__h__body(%Qubit* null) + call void @__quantum__qis__mz__body(%Qubit* null, %Result* null) + call void @__quantum__qis__mz__body(%Qubit* null, %Result* inttoptr (i64 2 to %Result*)) + call void @__quantum__rt__array_record_output(i64 2, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* null, i8* null) + call void @__quantum__rt__array_record_output(i64 3, i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 4 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 3 to %Result*), i8* null) + call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 2 to %Result*), i8* null) + ret void +} + +declare void @__quantum__rt__initialize(i8*) + +declare void @__quantum__qis__h__body(%Qubit*) + +declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 + +declare void @__quantum__rt__array_record_output(i64, i8*) + +declare void @__quantum__rt__result_record_output(%Result*, i8*) + +attributes #0 = { "entry_point" "num_required_qubits"="5" "num_required_results"="5" "output_labeling_schema" "qir_profiles"="base_profile" } +attributes #1 = { "irreversible" } + +!llvm.module.flags = !{!0, !1, !2, !3} + +!0 = !{i32 1, !"qir_major_version", i32 1} +!1 = !{i32 7, !"qir_minor_version", i32 0} +!2 = !{i32 1, !"dynamic_qubit_management", i1 false} +!3 = !{i32 1, !"dynamic_result_management", i1 false} +``` + +Ouput: + +```console +START +METADATA entry_point +METADATA num_required_qubits 5 +METADATA num_required_results 5 +METADATA output_labeling_schema +METADATA qir_profiles base_profile +OUTPUT ARRAY 2 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT ARRAY 3 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +OUTPUT RESULT 0 +END 0 +```