Code Monkey home page Code Monkey logo

qsharp-language's Introduction

DEPRECATION NOTICE

This repository is deprecated.

For the Modern QDK repository, please visit Microsoft/qsharp.

You can also try out the Modern QDK in VS Code for Web at vscode.dev/quantum.

For more information about the Modern QDK and Azure Quantum, visit https://aka.ms/AQ/Documentation.

Contributing

Suggestions for features and adaptions are filed and tracked in the form of issues on this repository. We greatly appreciate your feedback and contribution to the discussion in the form of comments and votes on open issues. Better understanding the needs of the community will help us make better decisions.

If you have a suggestion for a feature and would like to share your thoughts, we encourage you to file an issue following our suggestion template. The following section describes the process and workflow in more detail. For a suggestion to be adopted it needs to align with the general vision for Q# and the Q# language design principles. We do not generally revisit design decisions that have been made unless there is new information to consider, e.g. due to scientific or technical breakthroughs.

We also highly welcome contributions to help implement new features. Please take a look at the section on implementation for information regarding how to engage. We refer to this document regarding contributing and the Microsoft Open Source Code of Conduct.

Design Principles

A multitude of aspects ultimately factor into the decision to pursue a certain design direction. Given the early stages of quantum computing and the uncertainty around the architecture of future quantum hardware, designing a high-level language is challenging. Nonetheless, we believe it is worth the effort. The following list may give some insight into the principles guiding the Q# language design:

  1. Q# is hardware agnostic. We strive to design a language that provides the means to express and leverage powerful quantum computing concepts independent on how hardware evolves in the future.

  2. Q# is designed to scale to the full range of quantum applications. To be useable across a wide range of applications, Q# allows to build reusable components and layers of abstractions. To achieve performance with growing quantum hardware size we need automation. We want to ensure the scalability of both applications and development effort.

  3. Q# is meant to make quantum solutions accessible and shareable across disciplines. We are designing Q# to enable people to collaborate across disciplines, to make it easy to build on knowledge and share ideas, independent of background or education.

  4. Q# is focused on expressing information to optimize execution. Our goal is to ensure an efficient execution of quantum components, independent of the context within which they are invoked. Q# allows the developer to communicate their knowledge about a computation so that the compiler can make an informed decision regarding how to translate it into instructions, leveraging information about the end-to-end application that is not available to the developer.

  5. Q# is a living body of work that will grow and evolve over time. We share a vision of how quantum devices will revolutionize computing in the future. We also believe the quantum stack of the future will go beyond our current imagination. Correspondingly, our vision for Q# will adapt and change as the technology advances.

In addition to these principles, we try to adhere to a general set of good practices, and there are other aspects to consider that factor into a decision whether to pursue a certain feature. Please take a look at other considerations that factor into our decision as well as our FAQs and API design principles for further questions.

Process and Implementation

The development of a language feature consists of the following stages:

Suggestion:

An addition or modification to the Q# language starts with a suggestion. A suggestion is filed as issue on this repository following the suggestion template. Once a suggestion has been filed, it will be discussed on the issue, resulting in a first conclusion regarding whether to further pursue it. It will be labeled either with UnderReview or Declined by the Language Design Team depending on the outcome. This stage should be fairly quick and will take a couple of weeks to a month or two. If a conclusion can't be reached at this time, e.g. because it is not clear that it can be supported by hardware or it depends on other features that are currently under development, it will be labeled with OnHold.

Review:

Once a feature is labeled as UnderReview, the next step is to work out a more detailed proposal for how the feature should look like. Such a proposal is made by filling out the proposal template. For the purpose of discussion and collaboration when working out the details, and for us to give early feedback, we encourage to make a draft PR early on even when the template is not yet fully filled in. Once the template is sufficiently filled in, the PR is published and will be reviewed. Based on full proposal, the issue with the suggestion will either be labeled with ApprovedInPrinciple and the proposal is merged into the Approved folder, or it will be labeled with Declined and the PR is merged into the Declined folder for archiving purposes. The issue itself will be closed. How long it takes to work out the full proposal can vary a lot depending on the functionality.

Implementation:

All proposals that have been approved in principle and are ready to be implemented can be found in the Approved folder. When implementation starts, a new issue is created using the implementation template to track the progress. These issues are labeled with Implementation. The readme in the Approved folder also contains a list of all proposals and a link to the corresponding issue if development has already started. If you would like to contribute to an ongoing implementation, please indicate your interest and offer your help on the corresponding issue. If you would like to start the implementation of a proposal that is not actively being developed, please create a new issue following the implementation template. We will respond on the issue for an initial discussion on how to go about implementing it. Any revisions to the original proposal based on insights gained during implementation will be raised and discussed via comments on the issue.

Release:

Once the implementation is complete, the proposal that reflects the implemented functionality is moved from the Approved folder into the Implemented folder. As a last step before closing the corresponding issue, a PR to update the Q# language specification needs to be create and merged. The PR will only be merged once the functionality has been released as part of the QDK. We release a new QDK version at the end of each month and implemented features can be incorporated into any such release. At that time the corresponding issue will be labeled as Released and closed.

Repository Content

  • Specification of the Q# language shipped with the latest QDK version
  • Active proposals that are under review
  • Proposals that have been approved in principle but are not yet implemented
  • Features for which the implementation has started
  • Proposals for features that are implemented in the latest Q# version
  • Archived proposals for features that have been declined
  • Suggestions that are currently on hold and need to be evaluated once more information is available
  • Suggestions that have been reviewed in the past, i.e. all closed suggestions for which a conclusion has been reached
  • Templates for suggestions and proposals

qsharp-language's People

Contributors

anjbur avatar bamarsha avatar bettinaheim avatar bradben avatar dime10 avatar filipw avatar geduardo avatar k4rtik avatar microsoft-github-operations[bot] avatar microsoft-github-policy-service[bot] avatar microsoftopensource avatar nathanshammah avatar scottcarda-ms avatar sonialopezbravo avatar swernli avatar tcnickolas avatar viktorveis avatar yjt98765 avatar ziqi-ma avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qsharp-language's Issues

Add "if running forward" conditioning

Here is an operation that I've found myself defining:

    operation ForwardOnlyCCNot(a: Qubit, b: Qubit, c: Qubit) : Unit {
        body (...) {
            CCNOT(a, b, c);
        }
        adjoint (...) {
        }
    }

So that I can write code like this:

            within {
                for (t in 0..CeilLg2(n)-2) {
                    for (a in 0..2 <<< t..n) {
                        let b = a + (1 <<< t);
                        let c = a + (2 <<< t);
                        if (c < n) {
                            ...
                            init_and(t_b2c, t_a2b, centered_thresholds[b]);
                            ForwardOnlyCCNot(t_b2c, _range_g(a, b, out_c), _range_g(b, c, out_c));
                        }
                        if (b < n) {
                            ...
                            Adjoint ForwardOnlyCCNot(t_a2b, out_c![a], _range_g(a, b, out_c));
                        }
                    }
                }
            } apply {
            }

The idea here is that reversible code often runs forwards and then backwards almost identically. The variations between the backward and forward pass are not always directly in the center of the execution. When that happens, being able to make a few tweaks between the forward and backward pass can allow significantly more code reuse.

It seems silly to define a custom operation for this. It feels like it should be a language feature. Perhaps I should be able to say something like:

if [runningforward] {
    ...
} else {
    ...
}

This could also be used to make some manual adjoint implementations more compact, since they could be a slight if runningbackward tweak within the body.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • [?] I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • [no] I or my organization would be willing to help implement and/or test this

QIR: There is no way to inspect/view/pring %String type

The RT specifies quite a few ways to create an instance of %String type but the only method that might conceivably expose the content of the string to the user is __quantum__rt__fail(%String). Shouldn't there be a standard way for the user to inspect/print a %String?

File-scoped namespace statements

Suggestion

Allow namespace statements to end with a semicolon instead of a block, indicating that the namespace declaration applies to the entire file.

Considerations

Most Q# source files declare exactly one namespace, such that the extra level of indenting is unnecessary in most common cases. Allowing namespace declarations to be file-scoped would cut down on the amount of boilerplate and nesting, making Q# source files easier to read (esp. for newer users).

A similar discussion has led to the proposal and likely adoption of a similar feature into C# 10 (https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/file-scoped-namespaces.md).

Context

This proposal would not change any Q# semantics, but would provide a useful shorthand for existing features and semantics.

Examples

Example 1:
Using file-scoped namespaces to apply a namespace to all declarations in a file.

namespace Microsoft.Quantum.Foo;

// This function is declared with the fully
// qualified name Microsoft.Quantum.Foo.A.
function A() : String {
    return "aaaaaa";
}

Example 2:
Mixing file- and block-scoped namespaces is forbidden.

namespace Microsoft.Quantum.Foo;

namespace Bar { // ๐Ÿ’ฃ: can't mix file- and block-scope namespace declarations.
    
}

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

QIR: returning %Range type by value is problematic for native integration

While returning composite types by value (i.e. ret %Range %r) seems to be valid LLVM's IR, Clang doesn't generate correct assembly code from it and returned value isn't placed on the stack where the caller expects to find it. For C++ functions that return custom types by value, Clang generates IR with an output parameter:

struct Foo { long long a; long long b; long long c; };
Foo ReturnByValue(long long x) { return {x, 3, 7}; }
define void @ReturnByValue(%struct.Foo* noalias sret %0, i64 %1) {
  ...
  ret void
} 

However, the spec requires to always pass and return %Range by value.

QIR: Consider avoiding variadic functions in the runtime

As far as I can tell, there is no platform-agnostic way to implement variadic functions at the IR level and it's also error-prone, which creates a burden on the runtime implementers.

Instead of requiring:

%Array* __quantum__rt__array_create(i32, i32, ...)
i8* __quantum__rt__array_get_element_ptr(%Array*, ...)

consider similar to 1D specialization for 2D arrays (the arguments will be still passed via registers) and a generic buffer-based signature for larger arrays:

%Array* __quantum__rt__array_create_2d(i32, i64, i64)
i8* __quantum__rt__array_get_element_ptr_2d(%Array*, i64, i64)
%Array* __quantum__rt__array_create(i32, i32 %buffer_length, i64* %buffer)
i8* __quantum__rt__array_get_element_ptr(%Array*, i32 %buffer_length, i64* %buffer)

Notes:

  • even though the buffer size should match dimensionality of the array, it makes the signatures more consistent/safe to provide it explicitly in array_get_element_ptr;
  • for the size-specific functions a 3d one will also still fit into registers (in the common C calling conventions)

Can you add Topics such as "qsharp" and "quantum computing"?

Suggestion

TODO:
Insert a short outline for what the proposed modification is.

Considerations

TODO:
Explain why this modification to the Q# language is desirable.
Briefly summarize the benefits and drawbacks of the chosen mechanism opposed to other ways of achieving a similar functionality.

Context

TODO:
Describe briefly the most important points to consider in more detail going forward.
Link to related suggestions, proposals, specs, documentation or other related topics.

Examples

TODO:
Give concrete examples for syntax and behavior to illustrate the suggestions above.

Example 1:
TODO: insert title and caption

// TODO: 
// Insert code example that illustrates what is described above.
// Comment your code to further elaborate and clarify the example.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

QIR: consider specifying the expected behaviour in error cases such as out of bounds access to arrays

Currently, few of the quantum__rt* methods prescribe what should happen in case of an error. For example:

  • using a range with zero step,
  • getting an out of bounds element from an array,
  • projecting a 1D array,
  • concatenating non-1D arrays,
    etc.

Leaving those cases unspecified means undefined behaviour, specific to each vendor implementation of the runtime. While it's a possible choice, it might be worth to consider being more prescriptive. And if we choose to go with UB, the spec should probably explicitly state so in the overview section.

Allocatable types and generalization of initializers

Suggestion

The suggestion is to generalize the mechanism for [qubit allocations] in Q# to cover the following scenarios by introducing a more general notion of initializers for qubit allocations:

  • Creating instances of user defined types containing qubits
  • Initializing the allocated qubits with a certain operation upon allocation
  • Automatic allocation and deallocation of temporary qubits used during initialization

The suggestion applies to both using- and borrowing statements, and allows to instantiate values of a broad range of allocatable types instead of just values of type Qubit, Qubit[], and tuples thereof. The suggestion involves introducing a new keyword init that allows to elevate adjointable operations to initializers for the sake of supporting the second bullet point above.

Considerations

All of the above is possible to express with the current version of Q#, but the syntax for expressing that is somewhat verbose. Extending the concept of initializers and adding the necessary compiler support for automation of these scenarios allows to express them in a much more natural and readable manner. Any quantum transformations executed upon initializations are automatically reverted upon deallocation.

I believe the concept of initializers can be extended to cover a broader range of types in a consistent manner, without sacrificing any of the guarantees that Q# currently gives and in a way that can be executed on quantum hardware; it should always be possible to bound the number of required qubits by imposing a size limit on the call stack, much like it is the case for the current version of Q#.

The suggested generalizations greatly enhance the expressiveness of Q#. At the same time, it should be possible to translate the additional capabilities and syntax into the current version of the Q# language, such that the suggestion merely involves introducing additional syntax and automation capabilities. The examples below illustrate the suggested mechanism in more detail.

Context

Even though Q# does not currently support defining custom constructors for types, this would be a good addition to the language in the future. As part of a proposal for generalizing initializers, it is hence important to consider how the introduced concepts would work if custom constructors are supported. As part of a potential proposal, it also makes sense to discuss the implementation for supporting the nesting of initializers as part of initializer expressions in detail.

Furthermore, the restrictions that need to be imposed on operations to ensure that they can be elevated to initializers need to be clarified. Besides the requirement that such operations need to be adjointable, it may make sense to impose restrictions on whether or not the operations in question may capture (certain) qubits. If it is possible for such operations to capture qubits, then it giving guarantees regarding whether the un-computation of the allocated qubits is successful would be challenging. Alternatively, it may make sense to distinguish when an operation is (only) intended to be applied upon initialization, versus when its adjoint should also be applied before deallocation.

For the sake of consistency in syntax, it is reasonable to make one breaking change at part of this proposal. Concretely, the suggested breaking change is to replace the current syntax Qubit[n] that is used to initialize an array of qubits with Qubits(n). The exact syntax is to be determined as part of the proposal, and it needs to be possible to potentially extend it to the multi-dimensional case in a consistent manner in the future.

Related suggestions/proposals: #14, #39.

Examples

I will adopt the simplifications for qubit allocations that are currently under review in the given examples for the sake of convenience, though the examples remain valid also if only the current syntax is available.

Example 1: Initializing values of various types to various states
The following statements are valid within operations if this suggestion is adopted, where init is a newly introduced keyword used to elevate (certain) adjointable operations to initializers:

use q = Qubit();    // q contains a single qubit in a state |0>
use qs = Qubits(3);    // qs contains a 1D array of 3 qubits in a state |000>
use qInt = BigEndian(3);    // qInt contains a BigEndian value containing an array of 3 qubits in a state |000>
use xq = init H;    // xq contains a single qubit  in a state |+>
use xqs = init(3) ApplyToEachA(H, _);    // xqs contains a 1D array of 3 qubits in a state |+++>
use register = init(4) ApproximateQFT(4, _);  

For an operation ApproximateQFT as defined here, register will be bound to a value of type LittleEndian containing an array of 4 qubits initialized to QFT |0000> (i.e. to a uniform superposition). The last line in the example above can be translated into the following upon compilation:

use __var1__ = Qubits(4) {
    let register = LittleEndian(__var1__);
    within { ApproximateQFT(4, register); }
    apply {
        // code within the allocation scope
    }
}

Example 2: Nesting of initializer expressions and potential future syntax enhancements
If we were to allow for elevated operations to capture qubits, then introducing additional syntax sugar for particular operations, such as e.g. a coherent version of a logical AND would allow to express oracles constructed from such operators in the following way, if this suggestion is adopted:

use res = q1 <and> q2 <and> q3; 

where the right hand side is syntactic sugar init And(init And(q1, q2, _), q3, _), and q1, q2, q3 are existing qubits that are in scope. The statement above could be translated into the following upon compilation:

use res = Qubit() {
    within {
        use __var1__ = Qubit() {
            within { And (q1, q2, __var1__); } 
            apply { And (__var1__, q3, res); }
        }
    }
    apply {
        // code within the allocation scope
    }
}

Introducing additional syntax for coherent versions of logical operators is outside the scope of this proposal, but should be considered as part of the discussion on interactions with future modifications.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

QIR spec: `Range` type should not be passed by value

The QIR spec specifically calls out the Range struct as passed by value, as seen here:
https://github.com/microsoft/qsharp-language/blob/9cb9b91a49341d691c4fdc7d3f464453cc8eae36/Specifications/QIR/Data-Types.md#simple-types
This makes implementation difficult as other LLVM languages, notable C, C++, and Rust, do not have a mechanism to achieve that, requiring a forwarding function to written in raw LLVM that can allocate memory, copy the struct, and then pass the struct as a pointer to the underlying implementation. If this line is change to instead indicate that the struct is passed by pointer, then it will line up with the way struct passing is translated in C, C++, and Rust and make direct implementation of the QIR functions that use Range possible where today they are not.

@alan-geller @bettinaheim @kuzminrobin FYI

Do we need the IR wrappers?

Do we need the IR wrappers?

The known reason for the IR wrappers (in *.ll files) to exist is that the IR wrappers names start with the double underscore __.
The identifiers starting with double underscore are reserved in the standards of C and C++.
Are there any other reasons?
Can Q# compiler generate the QIR such that instead of the IR wrappers the corresponding wrapped functions are called?
(without __ in the beginning)

Stefan:
I would prefer finding a solution that does not require keeping the IR wrappers, whether that be switching the wrappers to a language that doesn't reserve __ as starting characters (like Rust) or updating the QIR spec to remove those. While they do provide some benefit for avoiding name collisions, that pattern has a cost overhead for implementation both for us and any external folks who want to implement their own runtime. Perhaps we could remove this question from here in favor of an issue filed against the spec in the language repo?

Bettina:
Yes, I am open to that conversation and filing it on the language repo makes sense

Add concept of a quantum-or-classical value

Suggestion

When writing arithmetic code, there is a tendency to repeat the same method multiple times based on whether certain inputs are quantum or classical. For example, you would have one method for xoring a LittleEndian into another LittleEndian, and a separate method for xoring a BigInt into a LittleEndian. It would be convenient if there was instead a xor method that accepted a BigIntOrLittleEndian and handled both cases.

This doesn't just apply at the level of designing methods. It also occurs when writing methods. For example, suppose you are creating a binary tree of qubits representing the AND of leaf input qubits as follows:

let n = Length(input_qubits);
using (work_qubits = Qubit[n-1]) {
    let qs = input_qubits + work_qubits;
    for (i in 0..n-2) {
        CCX(qs[2*i], qs[2*i+1], qs[n+i]);
    }

One of the problems you will run into is that if n is not guaranteed to be a power of 2, then the qubit at position n+n/2 is talking about a mix of leaf qubits at the end of the input and intermediate node qubits representing leaf qubits at the start of the input. To fix this, you can pad out to a power of 2:

let n = CeilPow2(Length(input_qubits));
using (work_qubits = Qubit[2*n-Length(input_qubits)]) {
    let qs = input_qubits + work_qubits;
    for (i in 0..n-2) {
        CCX(qs[2*i], qs[2*i+1], qs[n+i]);
    }

The problem now is that you are allocating unnecessary qubits to simplify your code. But if you were able to make an array of qubits-or-bits, and CCX understood that when one of its controls was a bit it should become a CX or no-op as appropriate, then you could do this:

let n = CeilPow2(Length(input_qubits));
let padding = RepeatArray(n - Length(input_qubits), False);
using (work_qubits = Qubit[n-1) {
    let qs = input_qubits + padding + work_qubits;
    for (i in 0..n-2) {
        CCX(qs[2*i], qs[2*i+1], qs[n+i]);
    }

And everything would just work. Although to be fair this is still not perfect; the unnecessary allocation is half as large but still there.

Considerations

This is a bit tricky to add to the language because at the moment the language is very picky about types. There's no way to say "this array contains a mix of qubits and bits", or "the control of this method can be a qubit or a bit".

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • [?] I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • [no] I or my organization would be willing to help implement and/or test this

Description lacks the limitations: __quantum__rt__array_concatenate

(I'm relatively new to this code base and I may be comparing unrelated things)

The Data Type Representation page does not mention that the __quantum__rt__array_concatenate function is only applicable for
1-dimenstional arrays,
in both arrays the elements must have the same size in bytes;

whereas the implementation of
QirArray* quantum__rt__array_concatenate(QirArray* head, QirArray* tail) in src/QirRuntime/lib/QIR/arrays.cpp requires 1 dimension:
assert(head->dimensions == 1 && tail->dimensions == 1).
It also calls concatenated->Append(tail) which contains:
assert(this->itemSizeInBytes == other->itemSizeInBytes).

The documentation for __quantum__rt__array_concatenate (in Data Type Representation) likely needs to mention those limitations.

Specification update to enable runtime-implementation initialization and finalization routines

Suggestion

In our QIR implementation, we have the need to run one-time initialization before any QIR API functions are called. At program exit, it is also useful for us to explicitly run any finalization activities (clean-up, print out, etc.). One specific use case that drives this initialization functionality is the need to indicate specifics about the backend being targeted. For example, our runtime implementation ultimately delegates to the qcor/xacc infrastructure, and therefore allows one to specify the quantum backend to target (IBM, Rigetti, simulators, etc), and input information for those specific backends (actual backend name, shots, simulation input, etc.). We package all of this type of input into a __quantum__rt_initialize(int, char**) function. The suggestion here is to indicate this in the specification, and ultimately let it be something that is optional, i.e. rt implementations can leverage it or not.

Considerations

This actually may be part of some larger specification update that details target QPU information provided at runtime. We may actually be able to package this as some sort of configuration file that is provided at some specification-noted location on the local host. If that is the case, then one may not need defined init/finalize type function calls, but can provide all input information as part of the configure file.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Specification should say that allocated qubits can be released early if possible

The page on Quantum Memory Management says this about using statements:

The using-statement allocates the qubits from the quantum processor's free qubit heap, and returns them to the heap after the statement terminates.

This should be updated to say that the qubits could be released before the end of the statement if the qubits are not used after a certain point in the block. The same could apply to the borrowing statement as well:

The borrowed qubits are in an unknown state and go out of scope at the end of the statement block.

BigInt versions of library functions & operations

Suggestion

TODO:
publish BigInt versions of all relevant library functions & operations

Context

say, you want to run Shor's algorithm, in the incarnation of the QDK sample "Quantum/samples/algorithms/integer-factorization" against the Resource-Estimator, to get a feeling about the resources you would need..

all library functions that are used in the sample, like "DrawRandomInt", "IsCoPrime" and operations like "EstimatePeriod" all use Int64 types.

that restricts you to factor only 'small' integers with max 19 decimal digits....

what if you want to check for an RSA 2048 bit key?

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Multidimensional arrays

Suggestion

Currently, for any Q# type 'T, the array type 'T[] represents an immutable collection of values of type 'T indexed by a single integer. It would be really helpful to add a new collection type that is indexed by tuples of integers instead, so as to allow for a more natural representation of concepts like matrices and tensors.

Considerations

While nested arrays can be used to do so (e.g.: Complex[][]), this can presents a number of disadvantages compared to a multidimensional array:

  • Nested arrays can in general be jagged; having multidimensional arrays as a type can help enforce that arrays are rectangular at the type level.
  • Performance: because nested arrays can be jagged, element lookup cannot in general be done as efficiently as with linear indexing.
  • Copy-and-update expressions: Using the w/ operator to modify elements of nested arrays can be a bit awkward.
    mutable matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
    set matrix w/ 2 <- (matrix[2] w/ 1 <- 20);
    // matrix is now [[1, 0, 0], [0, 1, 0], [0, 20, 1]]

Building on the utility of 1-D array notation, this suggestion proposes modifying Q# to include new multidimensional array types 'T[,], 'T[,,], and so forth. Like values of type 'T[], these new multidimensional would also be immutable, and could be manipulated by using the subscript ([]) and copy-and-update (w/) operators.

As a possible alternative, we could instead consider providing syntactic sugar for manipulating nested (ragged) arrays; for example, copy-and-update expressions could be extended to include tuple indices:

mutable matrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
set matrix w/ 2 <- (matrix[2] w/ 1 <- 20);
// syntactic sugar for the above:
set matrix w/ (2, 1) <- 20;

While this would address the ergonomics of using values of type 'T[][], 'T[][][], and so forth, this syntactic sugar does not represent the compile-time guarantee that nested arrays are rectangular, necessitating potentially expensive shape checks (e.g. Microsoft.Quantum.Arrays.RectangularArrayFact). Similarly, providing syntactic sugar for nested arrays would not fundamentally change performance concerns with using nested arrays to represent matrices and tensors. For instance, by modifying the formula used to map multidimensional indices to linear indices, matrix and tensor transposition can be implemented in time that scales with the number of dimensions rather than the number of elements.

Context

This suggestion would require adding new functions and operations to the Microsoft.Quantum.Arrays namespace in the Q# standard library to support the feature; as Q# does not currently allow for a function or operation to act on multiple input types except as unbounded type parameters (see #149), these functions or operations would require distinct names from their 1D counterparts (e.g.: ConstantArray2, ConstantArray3, and so forth). Adding these functions and operations may require breaking changes to Microsoft.Quantum.Arrays in order to accommodate a naming scheme that includes suffixes for dimensionality.

Currently, array types in Q# can be indexed by values of type Int or Range, but current array indexing could be extended to allow "fancy" slicing by values of type Int[] (similar to the functionality offered by Microsoft.Quantum.Arrays.Subarray). In the same way, this suggestion could be extended to allow indexing 2D arrays by values of type (Int[], Int[]) as well as (Int, Range), (Range, Int), (Int, Int), and so forth. For example:

let data = [
    (0, 0), (0, 1), (0, 2);
    (1, 0), (1, 1), (1, 2);
    (2, 0), (2, 1), (2, 2)
];
let corners = data[[0, 2], [0, 2]];     // โ† [(0, 0), (0, 2); (2, 0), (2, 2)]
let rightHandCorners = data[[0, 2], 2]; // โ† [(2, 0), (2, 2)]
let swapMatrix = (IdentityMatrix(4))[[0, 2, 1, 3], [0, 2, 1, 3]];

Examples

Example 1:
Declaring a literal array representing a matrix over ๐นโ‚‚.

// Bool[,] literals differentiated from Bool[][] by having only one level of [ ], corresponding
// to only one pair of [ ] used to subscript and in the type signature.
let bellTableau = [
    true, true, false, false; // โ† "rows" are separated by ; rather than comma.
    false, false, true, true
];

Example 2:
Declaring and updating a 3ร—3 array of Double values.

namespace Microsoft.Quantum.Arrays {
    function IdentityMatrix(nDimensions : Int) : Double[,] { ... }
}

mutable matrix = IdentityMatrix(3);
set matrix w/ (2, 1) <- 20;
// matrix is now [1, 0, 0; 0, 1, 0; 0, 20, 1]

Example 3:
Indexing into a stabilizer tableau using ranges and integers.

let perfect = [
    PauliI, PauliX, PauliZ, PauliZ, PauliX;
    PauliX, PauliZ, PauliZ, PauliX, PauliI;
    PauliZ, PauliZ, PauliX, PauliI, PauliX;
    PauliZ, PauliX, PauliI, PauliX, PauliZ
];
Message($"{perfect[0, 2]}"); // โ† Prints PauliZ.
let firstQubit = perfect[(..., 0)]; // โ† [PauliI, PauliX, PauliZ, PauliZ]
let firstRow = perfect[(0, ...)]; // โ† [PauliI, PauliX, PauliZ, PauliZ, PauliX]
let subblock = perfect[(1...2, 0...3)]; //  โ† [PauliX, PauliZ, PauliZ; PauliZ, PauliZ, PauliX]

Example 4:
Compatibility with nested array types.

let identityMatrices = MappedOverRange(IdentityMatrix, 1..5); // type: Double[,][] (array of 2D arrays)
let contrived = [
    [0], [0, 1];
    [0, 1, 2], [0, 1, 2, 3]
]; // type: Int[][,] (2D array of arrays)

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

QIR spec: `Pauli` data type values should be listed as signed or adjusted to `i8`

Right now the Pauli data type is explicitly listed as i2 and having the values 0, 1, 2, and 3 (see https://github.com/microsoft/qsharp-language/blob/9cb9b91a49341d691c4fdc7d3f464453cc8eae36/Specifications/QIR/Data-Types.md#simple-types). This is not technically true, as LLVM only supports signed integers, so an i2 can hold the values 0, 1, -1, -2. When attempting interop with other languages such as C, C++, or Rust, if the Pauli type is listed as the smallest addressable type in that language, either 8 bit signed integer or char, the compiler will automatically represent the binary value 11 stored in an i2 as -1 because it is "smart" enough to preserve the sign bit when extending into the larger type. To make it possible to implement functions that accept a Pauli QIR type without having to do extra handling, we should either update the list of integer values to 0, 1, -1, -2 (and update the corresponding generation code in the qsharp-compiler and implementation in qsharp-runtime) or consider updating the type to i8 so it matches the commonly available minimum addressable type in other languages.

@alan-geller, @bettinaheim, @kuzminrobin FYI.

Bounded polymorphism

Is your feature request related to a problem? Please describe.
When writing a polymorphic function in Q#, the type variables are fully general. They are opaque types with no properties that the function can rely on.

But there are many cases where a function needs a type to have certain properties. For example, a function may need its type variables to be equatable or comparable to store/retrieve them from a list or map. Arithmetic functions may also require that the types are numeric.

Describe the solution you'd like
Q# needs some form of bounded polymorphism to be able to write functions that are valid for only certain types. I believe type classes are the best solution for this: they are very flexible and fit with Q#'s functional style.

Describe alternatives you've considered
Overloading functions based on type (#29) would solve the naming problem (e.g. PlusI, PlusD, PlusL could be overloaded to Plus), but this solution doesn't scale. Because the numeric types are not formally grouped, there is no way to write a single polymorphic function that calls the right Plus automatically - it similarly needs to be overloaded for each numeric type, creating unnecessary code duplication.

Additional context
This would solve #147. Equatable types would have an instance of the equatable type class.

Treat Range as a built-in UDT

Is your feature request related to a problem? Please describe.
The Range type effectively acts as a UDT, in that it consists of three Int values. Unlike UDTs, however, Range values cannot be unpacked in destructuring assignments, and its items cannot be accessed using ::. This motivates the need for the RangeStart, RangeStep, and RangeEnd functions.

Describe the solution you'd like
It would be nice if the syntax a..b..c was syntactic sugar for Microsoft.Quantum.Core.Range(a, b, c), with a..b being sugar for Microsoft.Quantum.Core.Range(a, 1, b). Since Microsoft.Quantum.Core is auto-opened, these can then be shortened to Range(a, b, c) and Range(a, 1, b), respectively.
This would then allow things such as let (a, b, c) = (a..b..c)!, or let step = (a..b..c)::Step. In turn, that removes the need for the Range* functions, and allows for a more uniform design between the Range type and UDTs.

Allow function overloading

When writing arithmetic functions, it seems common to have variants specialized to int, BigInt, and LittleEndian. It's confusing to have to remember different names for these functions, instead of just having one standard name with disambiguation via the type system.

Discriminated Unions

Suggestion

This suggestion outlines how to add discriminated unions to Q#.
The overall philosophy/objective with this design is to use it to extend/generalize the current design of user defined types (UDTs).
It may not break current UDT syntax, and current UDTs should be thought of as single-case DUs.

NB: This was originally proposed on the compiler repo, and there is a lot of existing discussion there, most of which has been incorporated here.
Thanks to @samarsha, @cgranade, @msoeken and @guenp for input to this proposal ๐Ÿ’–

Considerations

Firstly, discriminated unions (DU), or tagged unions, are a fairly common language feature for function languages and will allow users of Q# to leverage coding patterns they are already familiar with.
Scala calls DUs case classes, Rust has DUs called enums, Haskell has sum types, and F# calls them DUs.
This gives us a lot of examples to draw from and evaluate to create the right feature for Q#.

Secondly, DUs can make code more robust as they allow you to use the type system to reduce bugs or unhandled edge cases.
In Examples there are some samples of how DUs can improve common Q# patterns and usage.
The main patterns I have run into where they might be useful fall into the following categories:

  • Making enum-like types (think like Pauli operators)
  • Handling formatting like endianess (LittleEndian or BigEndian cases of Qubit[])
  • Two case DU return types (Maybe<Int>), which requires type parameterized UDTs proposed here

If this is implemented in a way that allows all current UDT syntax to be valid, then this shouldn't be a breaking change and should just be strictly a feature add.

It is not clear to me there is another way to achieve this functionality other than creating a UDT where each field is a valid case/tag and you would use if statements to filter on the populated field manually.
This is similar to how I have had to implement what here would be a Maybe DU where I used a tuple (Bool, (Int, Int)) as a return type signature (see this issue).
There are certainly other designs for how to add DUs to Q#, but this proposal focuses on making it a non-breaking change by extending current UDTs.

For the formatting applications, @samarsha proposed the following as an alternate solution for the example of endianess that can be used right now in Q#:

// A quantum integer, represented internally using little-endian (but
// this is an implementation detail - it would be better if the
// internal representation could be hidden from users).
newtype QInt = Qubit[];

// Creating QInts
function LittleEndian(qs : Qubit[]) : QInt { return QInt(qs); }

function BigEndian(qs : Qubit[]) : QInt { return QInt(Reversed(qs)); }

// Consuming QInts
function QIntAsLittleEndian(n : QInt) : Qubit[] { return n!; }

function QIntAsBigEndian(n : QInt) : Qubit[] { return Reversed(n!); }

It's supported by the current Q# language, but IMO it would be better if Q# allowed opaque UDTs to be defined (i.e., UDTs where the underlying type is only visible from within a module/project, rather than public), so that users wouldn't be able to tell if QInt used little-endian or big-endian internally.

This would definitely work, but I think as Sarah notes, that it doesn't really achieve the conceptual abstraction and the user will still have to track some conversions.

Context

The most important feature of this design approach is that it feels natural with the current implementation for UDTs, and allows for all current UDT syntax to be valid.

DUs have the following components:

  1. The name of the DU type itself/the declaration syntax*.
  2. Cases (sometime implemented with tags) that identify which part of the union the value is in.
  3. Constructor for values of each case.
  4. Destructor syntax that allows you to match on which case a value is in.

* DUs could also include anonymous DUs as detailed by @cgranade, @guenp and @msoken here and at end of examples.

The following section goes into much more detail on what each of these parts could look like for Q#.
Much of the suggested syntax here comes from the examples that I looked in this reference on F#.
As noted above though, there are many languages that have similar features, but the F# syntax seemed to match most closely with established patterns in Q#.

Examples

The three main syntactic things that need prototyped to extend UDTs to DUs:

  1. how to declare a new DU type,
  2. what case constructors look like,
  3. and how to destruct the different types in the union.

To that end, the newtype keyword would be used to declare new DUs as well.

@samarsha notes that:

"In Haskell, newtype is used to declare strongly-typed type aliases that are limited to a single constructor. As a result, they can be erased at runtime. I am not sure if that was also part of the design of newtype in Q#. If Q# is making a distinction between types that can be erased at runtime and types that can't, then we can't use newtype for multi-constructor types. If newtype is meant to be a general data type, then I would still suggest using a different keyword like type for simplicity and to avoid confusing Haskell people. :)"

The following sections give example suggested designs for the three syntaxes listed above.

DU declarations

Since the goal is to make this feel natural with the current implementation for UDTs, here is a quick review of that syntax.

newtype Complex = (Re : Double, Im : Double);

You can see it uses the statement newtype to declare a new UDT, and there are named items in the defininition itself. For more details and examples see the docs.

The following examples would all be valid syntax for this design.

Example 1:
Standard UDT syntax where the constructor function implicitly will have the same name as the type.

newtype Foo = (Bar : Baz);

Example 2:
Standard UDT syntax where the constructor function is explicit.

newtype Foo = Foo(Bar : Baz);

Example 3:
A two case DU where there is no leading bar.

newtype MaybeBaz =
    | Some(Bar : Baz)
    | None();

Example 4:
A two case DU where a complex number can have one of two different coordinate systems.

newtype Complex = 
    Cartesian(Re : Double, Im : Double)
    | Polar(Double, Double)

Example 5:
Standard UDT syntax where the constructor function is explicit, and has a leading bar.

Note: probably should be valid for consistency, but not approved by the style guide

newtype Foo = | Foo(Bar : Baz);

Example 6:
Standard UDT syntax where the constructor function is implicit, and has a leading bar.

Note: probably should be valid for consistency, but not approved by the style guide

newtype Foo = | (Bar : Baz);

Example 7:
A two case DU where there is no leading bar.

newtype QInt =
    LittleEndian(Qubit[]) |
    BigEndian(Qubit[]);

Example 8:
A multi-case DU where all the case types are Unit, a.k.a. an enum. Could also potentially omit parens.

newtype Weekday =
    | Monday()
    | Tuesday()
    | Wednesday()
    | Thursday()
    | Friday();

Example 9:
A multi case DU that generalizes implementation options for an adder.

newtype AdderMethod = 
    | CDKM()
    | Draper(LowDepth : Bool)
    | TTK();

The design here also allows for type parameterization (see this proposal for parameterized UDTs). This would significantly expand what whe can do with DUs, in particular create a Maybe DU as shown in this example. The type parameter 'T can be any other valid type in Q#, see the docs for more info.

Example 10:
A parameterized two case DU where there is a leading bar.

newtype Maybe<'T> =
    | Some('T)
    | None();

Case constructors

You have seen some of the syntax for the case constructors already, they are the bits similar to function declaration syntax (i.e. Some(Int)).
One way to think about the deign here is that you are defining a collection of UDT constructors, one for each case.
You are declaring cases by declaring their constructors.
That means that the case constructor functions will be declared as globals in the same namespace that the DU is declared in.
As a consequence of that, then you will not be able to have DUs with case constructors that have the same fully qualified name.
I don't think at the moment that is a problem as it follows kind of the same pattern as you cannot have a UDT and a function share the same name in a namespace.

Examples of valid case constructor syntax:

Example 1:
Given the Maybe<'T> declaration, declaring a case that returns a type of Maybe<Int>.

newtype Maybe<'T> =
    | Some('T)
    | None();

// Has type Maybe<Int>
let x = Some(42);

// Has type String -> Maybe<String>
let maybeString = Some<String>;

Example 2:
Constructs a register that represents an integer, that can be interpreted as either little or big endian.

newtype QInt =
    | LittleEndian (Register : Qubit[])
    | BigEndian (Register : Qubit[]);

using (qubits = Qubit[3]) {
   let target = LittleEndian(qubits);
   // ...
}

Example 3:
Case constructor that has type Unit.

newtype Maybe<'T> =
    | Some('T)
    | None();

// Could be used to indicate that some calculation did not succeed. 
let return = None<String>();

// For constructors isomorphic to `Unit`, parens could be omitted.
let return = None<String>;

Case deconstructors

For the deconstructor here, an expression design could make a lot of sense.
That allows you to set a variable equal to the result of a deconstruction statement.
See the following examples of how this could work.

Example 1:
Example 3 from the previous section, and we use match to deconstruct the cases possible for Maybe<'T>.

newtype Maybe<'T> =
    | Some('T)
    | None();

function Foo(bar : Int) : Maybe<Int> {
    if (Int % 2 == 0) {
        return Some(bar/2);
    } else {
        // Would be nice to not have to specify <Int> here but that's not necessary for the DU design.
        return None<Int>;
    }
}

// Could be used to indicate that some calculation did not succeed. H/t to @samarsha for the suggestion [here](https://github.com/microsoft/qsharp-compiler/issues/406#issuecomment-626110376).
let x = match (Foo(8)) {
    // It's OK to omit <Int> here since that's
    // known exactly from the type of Foo(8).
    Some(_) -> "Even",
    None -> "Odd"
}

Use case: Library simplification

Via @cgranade in previous thread.

...we have a fairly common pattern where different algorithms and methods for doing the same thing may offer different trade-offs, such that a user may want to select an implementation based on those trade-offs. That's more than an enum of Unit cases, however, since some methods may have different options than others, such that I think this would be a really great place to have DUs.

From @msoeken in previous thread, I can maybe stub an example for an enum of Unit cases:

newtype PermuteMethod = 
    | Transformation()
    | Decomposition()
    | SomeOtherMethod()

operation ApplyPermutation(method : PermuteMethod, ...) Unit is Adj + Ctl {
    ...
}

Use case: Anonymous DUs

Detailed by @cgranade, @guenp and @msoken here:

One possible modification that I could suggest to this proposal is one that's come up in a few recent API discussions with @guenp and @msoeken, namely of anonymous discriminated unions. In TypeScript, for instance, the type string | number can be either a string or a number (Python has a similar feature using the notation Union[str, float]). That can be useful for cases that look like "overloading" in C-style languages, such as allowing PrepareAribtraryState to take either an array of Double values representing positive coefficients, or an array of complex coefficients:

operation PrepareArbitraryState(coefficients : (Double[] | Complex[] | ComplexPolar[]), target : Qubit[]) : Unit is Adj + Ctl {
    // ...
}

To use a value of such an anonymous union type, I'd suggest requiring that a developer first resolves the exact type using a match expression:

let complexCoefficients = coefficients match {
    realCoefficients : Double[] ->
         // In this arm, realCoefficients is a resolution of the anonymous DU
         // to have the concrete type Double[] instead.
         Mapped(Complex(_, 0.0), positiveCoefficients),
     alreadyComplexCoefficients : Complex[] -> alreadyComplexCoefficients,
     complexPolarCoefficients : ComplexPolar[] ->
         Mapped(ComplexPolarAsComplex, complexPolarCoefficients)
      // The match expression is exhaustive here, such that no blank pattern
      // is needed in order to guarantee that complexCoefficients has
      // a value of type Complex[] in all cases.
};

Adding a feature of this form wouldn't address all the usecases for DUs (e.g.: since file names and display names for DumpMachine locations are both of type String, labeled cases are still really helpful), but could possibly make DUs lighter-weight to use when the interpretation of each branch of a DU is obvious from context.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Allow `Qubit[n]` and `Qubit()` expressions in `within` blocks.

Currently, when you want to allocate several registers, it is quite cumbersome. You get a lot of indentation, you have to assign temporary names to the raw register before wrapping it into the type you actually want, etc.

One way to work around this issue would be if within blocks allowed arbitrary allocation expressions. That way there's no need for the temporary unwrapped names, and much less increase in indentation.

This feels like a natural thing to just allow inside the within block.

Examples

within {
    let a = LittleEndian(Qubit[10]);
    let b = LittleEndian(Qubit[10]);
    let c = LittleEndian(Qubit[10]);
} apply {
    ... code using a, b, c ...
}

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
    There's some similar stuff in #40
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • [not really] I or my organization would be willing to help implement and/or test this

Qubit allocation without new block scope

Suggestion

We propose creating an analogue of let for qubits: a statement that allocates qubits and binds them to a name for the remainder of the current scope, after which the name is no longer accessible and the qubits are released. The statement does not require a new block scope, unlike the current using and borrowing statements.

Considerations

Allocating qubits in Q# with using and borrowing statements requires creating a new block scope. The block makes the lifetime of the qubits explicit, and discourages holding on to qubits for longer than needed, but at the cost of increased block nesting and indentation. Additionally, it is very common for a using statement block to be the last statement in the enclosing scope, in which case the explicit scope did not actually help to shorten the lifetime of the allocated qubits.

The explicit block also makes it cumbersome to create more than one qubit variable at a time. If one using statement per variable is used, a new indentation level is required for each variable. More commonly, tuple destructuring is used to create more than one variable in a single assignment, but this becomes difficult to read as the number of variables increases or as the complexity of the qubit initializer expression increases.

A let-like qubit allocation statement is needed to simplify these use cases. Because using and borrowing statements are the only way to allocate qubits, there is no workaround currently in Q#. An alternative proposal that would only address the many variable case could be to allow multiple using statements in a row followed by only one scope:

using (q1 = Qubit())
using (q2 = Qubit()) {
    // ... use both q1 and q2 ...
}

But this still requires at least one new block scope in all cases, unlike the original suggestion.

Context

We can consider two possible naming conventions for the new statements:

  1. Reuse the same keywords as the block statements: using and borrowing.
  2. By analogy to let, create the new keywords use and borrow.

Reusing the keywords avoids a breaking change, but creates a slight inconsistency:

using q1 = Qubit();
borrowing q2 = Qubit();
letting q3 = q1; // wrong
let q3 = q1;     // right

If we use new keywords, we need to decide if we keep the original keywords for the block statements (supporting two parallel sets of keywords indefinitely), or if we rename those too:

use (q = Qubit()) {
    // ...
}
borrow (q = Qubit()) {
    // ...
}

Examples

Example 1: Current syntax.

// One variable.
using (q = Qubit()) {
    // ...
}

// Two variables with separate statements.
using (q1 = Qubit()) {
    using (q2 = Qubit()) {
        // ...
    }
}

// Two variables with tuple destructuring.
using ((q1, q2) = (Qubit(), Qubit())) {
    // ...
}

Example 2: Proposed syntax with new keywords.

// One variable.
use q = Qubit();
// ...

// Two variables with separate statements.
use q1 = Qubit();
use q2 = Qubit();
// ...

// Two variables with tuple destructuring.
use (q1, q2) = (Qubit(), Qubit());
// ...

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Support equality checking on arbitrary types

Is your feature request related to a problem? Please describe.
At the moment, the == operator is not supported for values whose type is given by a type parameter (see screenshot from an IQ# session below). Since all types in Q# are value types, however, we should be able to support == between arbitrary types, including type parameters.

Additional context
image

Make custom inline arithmetic operators possible

A major source of boilerplate and typos and readability issues in my Q# programs is having to write things like this:

PlusEqual(target, offset)

Instead of this:

target += offset

Or this:

PlusEqualQuantumClassicalProductLE(a, b, 53)

instead of this:

a += b * 53

I would find it very useful if there were some mechanism to fix that. There are many possible ways to do so. For example, a pattern matching system:

match (a: LittleEndian, b: LittleEndian, c: BigInt)
    in (a += b * c)
    to PlusEqualQuantumClassicalProductLE(a, b, c);
match (a: LittleEndian, b: LittleEndian, c: BigInt)
    in (a += c * b)
    to PlusEqualQuantumClassicalProductLE(a, b, c);
match (a: LittleEndian, b: LittleEndian, c: BigInt)
    in (a -= b * c)
    to Adjoint PlusEqualQuantumClassicalProductLE(a, b, c);
match (a: LittleEndian, b: LittleEndian, c: BigInt)
    in (a -= c * b)
    to Adjoint PlusEqualQuantumClassicalProductLE(a, b, c);

Allow operations to declare a budget

One of the values I often find myself adding to the documentation of an operation is how many resources it can consume (e.g. workspace qubits, T gates). It would be nice if there was a formal way to do this, and that the claimed budget was actually verified when executing.

For example:

operation op(qs: Qubit[]) : Unit {
    budget {
        set_max_workspace(Length(qs) * 2);
        set_max_operation(CCNOT, 20);
    }
    ...
}

That being said...

  • This could affect the execution time of simulators.
  • Most programming languages don't have this sort of thing, so it'll be kind of intrinsically unfamiliar.
  • Not all methods have simple resource counts. E.g. probabilistic methods might have an expected value that is much lower than the worst case value.
  • Different simulators may see different resource counts, if operations are decomposed in different ways based on the available gate set.

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • [?] I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

`body auto` should infer body block if any other block is specified

I've noticed myself often implementing body blocks exactly as follows:

body (...) {
    Controlled this_operation(new Qubit[0], (arguments))
}

which significantly reduces code duplication in cases where the controlled version of an operation only needs to control a few sub-operations, and you want both the controlled and uncontrolled versions of the operation.

I think the language should allow me to replace this boilerplate with just body auto, assuming the controlled block has been specified.

Support for covariant arrays

Is your feature request related to a problem? Please describe.
Consider the following snippet of code:

function fooA( a : (Unit => Unit is Adj) ) : Unit {}
function foo( a : (Unit => Unit) ) : Unit {}
function Bar() : Unit {
    let x = [foo, fooA];
    let z = [(1, foo), (2, fooA)];
    let y = [(foo, foo), (fooA, fooA)];
    // (QS5002) Array items must have a common base type.
    let w = [[foo], [fooA]]; // QS5002 error
}

Expressions let x ..., let y ..., let z ... compile without an error, and let w=... causes an error.
It seems like (Unit => Unit) is a supertype of (Unit => Unit is Adj), however
(Unit => Unit)[] and (Unit => Unit is Adj)[] have no common base type.

Describe the solution you'd like
I would like the code above to compile without an error.

Describe alternatives you've considered
Update the documentation at https://docs.microsoft.com/en-us/quantum/user-guide/language/types#operation-and-function-types to clarify that above is intended behavior

Add a list of reserved operation names to QIR

While QIR doesn't and shouldn't specify a required quantum instruction set, it is helpful to specify that certain operation names are reserved; that is, if a target supports an operation called CNOT, that operation has to implement a two-qubit controlled X.

Where possible, actual matrices should be provided for precision.

Build in support for integer limits on Repeat-statements

Suggestion

Q#'s repeat-statement should have built-in support for integer limits. This would allow a developer to specify the maximum number of loops that will be executed without needing to use a mutable count variable. This upper limit could be very between different loops, and could be determined at runtime by inputs, classical computation, or even quantum results. Having an integer limit would assist in the transformation of repeat-statements into forms that are compatible with execution capabilities of current hardware. Even beyond current capabilities, having a convenient construct for limiting the number repeats is handy syntax to capture a pattern where such a limit is needed but the current loop number is not needed in the repeat body.

This suggestion lines up with the possibility mentioned in the "Target-Specific Restrictions" section of the Repeat-Statement specification page: "Execution on quantum hardware can potentially be supported in the future by imposing a maximum number of iterations."

Considerations

In the current repeat-statement syntax, supporting an integer limit is possible via a mutable variable. This can be done either by counting down:

mutable limit = 10;
repeat {
    DoWork(q0);
}
until (M(q0) == Zero or limit <= 1)
fixup {
    Reset(q0);
    set limit = limit - 1;
}

or by counting up:

let limit = 10;
mutable count = 0;
repeat {
    DoWork(q0);
}
until (M(q0) == Zero or count >= limit - 1)
fixup {
    Reset(q0);
    set count = count + 1;
}

Note the subtleties in constructing the limit part of the conditional: in order to ensure that the loop stops after the 10th run the user must remember that the condition is a success/termination condition and a continuation condition (making it different from both while-loop and for-loop integer conditions), and be careful to remember whether they are zero indexing their limit or one indexing (these examples choose one indexing so that limit is a clear value that corresponds to the number of loops performed, with the trade-off being the need to use limit <= 1 and count >= limit - 1 for the conditions respectively). The developer may also decide to move the update of the mutable variable into the body of the repeat block instead of the fixup block, which has the subtle distinction of affecting what the variable value will be after termination, especially if the repeat terminated due to a different clause of the condition and/or the mutable variable is used after the execution of the repeat-statement.

For cases where the current loop count is not needed and a simple limit would suffice, these examples could use new syntax that provides the limit as a parameter to the repeat keyword. This allows the developer to skip the decision points of defining a mutable variable, updating that variable, and correctly crafting the right terminating condition. This syntax could look like this:

repeat (10) {
    DoWork(q0);
}
until (M(q0) == Zero)
fixup {
    Reset(q0);
}

This would ensure that loop is executed at most 10 times, and is clear syntax without the overhead of either author or readers of the code needing to consider the increment or termination pattern chosen.

It's also worth considering allowing a repeat loop that does not have an until condition if a limit is specified. This would essentially be an anonymous form of a for loop:

repeat (10) {
    DoWork(q0);
};

One way to think of this capability is that is akin to using a break in a for-loop for a language that supports that. Using repeat (<Int>) establishes the fixed loop while the until (<Condition>) is the early termination condition. Thus, an alternative consideration could be a break keyword that would allow such an early termination of a for-loop, though that has been called out as not part of the original intent of the for-loop in Q# (see here), and could confuse the role of for-loops as the predictable length loop construct.

Context

As part of exploring this option, I have been working on a prototype compiler rewrite step that transforms a repeat-statements into a recursive callable. This recursion can be unlimited or incorporate a fixed limit. With the fixed limit, recursive versions of repeat-statements can be interpreted as a fixed depth nested conditional structure that has a better chance of successfully translating into existing hardware instructions.

Examples

See above in the Considerations section.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Make it easier to implement circuits described using mutable arrays

I was trying to implement the carry lookahead adder from https://arxiv.org/pdf/2004.01826.pdf . it has a step described like this:

image

As you can see, their description involves storing a reference to an initialized qubit into an array, which will be used during later steps.

Currently, it is difficult to implement this in Q# because:

  1. The syntax for mutating an array-of-arrays is extremely verbose.
  2. set lines prevent the creation of an automatic adjoint.
  3. Qubits need to be allocated outside the nested loop, requiring you to separately calculate the total number you need to allocate instead of just relying on the loop to do it.

I'm not exactly sure how to fix this, but it's something that I've run into more than once.

Here are some ideas:

  1. Have a concept of a "qubit pool", which you can iteratively ask for more qubits from and where all the qubits are deallocated when the pool goes away. Using a pool would prevent the automatic creation of an adjoint:
using (pool = QubitPool()) {
    for (...complicated...) {
        let q = pool.allocate_another();
    }
}
  1. Add support for mutable arrays, and/or multi-dimensional arrays, and/or hash maps. Support syntax like set map[(i, j)] = x.

  2. Allow the use of set statements inside of auto-adjoint operations as long as they invoke no operation that returns a value (except perhaps for some whitelisted special case operations like allocation). Implement these methods by executing them forwards, with all operations replaced by no-ops, recording the historical state of mutable variables. Then rewind while applying the inverses of the operations.

Resources calculation for quantum arithmetic functions

Hello!

I have set up and ran some basic resource estimations from the tutorial for the Apply Majority algorithm on Q# Jupyter notebooks using e.g.

// |x1 x2 x3>|y> -> |x1 x2 x3>|y + MAJ(x1, x2, x3)>
operation ApplyMajorityUsingCNOTTransformation(x1: Qubit, x2: Qubit, x3: Qubit, y: Qubit): Unit is Adj + Ctl {
    within {
        CNOT(x2, x1);
        CNOT(x2, x3);
    } apply {
        CCNOT(x1, x3, y);
        CNOT(x2, y);
    }
}
operation UsingCNOTTTransformation() : Unit {
    use (x1, x2, x3, y) = (Qubit(), Qubit(), Qubit(), Qubit()) {
        ApplyMajorityUsingCNOTTransformation(x1, x2, x3, y);
    }
}

%estimate UsingCNOTTTransformation

I was hoping to try out the same for some quantum arithmetic functions (e.g. AddFxp, some multiplication functions, etc.) but Iโ€™m having some trouble understanding how to implement them. Are there any good resources describing how to implement a simple fixed point addition function (and count the resources?)

Thank you!

Reduction loops

Suggestion

Currently, Q# provides several looping structures (for, repeat/until, and while) that specify specific control flow ordering, but does not provide any looping structures allow developers to specify data flow dependencies instead. This proposal introduces a new kind of loop, reduce, in which iterations do not have a strict execution order, but that each contribute values to a larger calculation.

By analogy to existing classical approaches such as OpenMP, such reduction loops can be used to help take advantage of parallelism with respect to classical processors, or even with respect to quantum devices.

Considerations

This proposal extends control-flow loops to include data-flow, allowing Q# to include concepts such as list comprehensions and the flatmap monad, as well as concepts unique to quantum computing such as "shots" of circuit-like subroutines.

There are a number of alternatives one could consider as well:

  • Conventional for loops, decorated with attributes or #pragma-like metadata
  • Loops with hard-coded reduction functions (e.g.: sum, mean, etc.)

This proposal advances a more general version of the above based on ideas from OpenMP- and MapReduce-style parallelism. The suggestion made in this proposal can be made either as a statement that includes a new form of binding (adding to let, mutable, use, and borrow), or as an expression that can be used with existing let and mutable bindings as well as with return. Using expression-based notation would be more difficult to implement given Q#'s current statement-driven focus, but would be more parsimonious with other existing syntax such as let and return.

Context

The syntax used in these examples assumes QEP #4 as well as a way of indicating device qubit literals (i.e.: #0).

This proposal recommends that custom reduction functions must be denoted either @AssociativeReduction or @BatchReduction, in either case allowing for parallelism to be split across multiple devices. The examples below present a few custom reduction functions, with more details to be written in a formal proposal.

Examples

Example 1:
Summing over the first ๐‘› squares (statement-based notation).

function SumOfSquares(n : Int) : Int {
    reduce sum = PlusI over _ in 1..n {
        yield idx * idx;
    }
    return sum;
}

Example 2:
Summing over the first ๐‘› squares (expression-based notation).

function SumOfSquares(n : Int) : Int {
    return reduce PlusI over _ in 1..n {
        yield idx * idx;
    };
}

Example 3:
Using reduction expressions as list comprehensions.

// Assuming the following library code, and using QEP 5 syntax:
@BatchReduction()
function Collected<'TElement>(prevStates : [['TElement]], newBatch : ['TElement]) : 'TElement {
    return newBatch + reduce PlusA over state in prevStates { yield state; };
}

// # Basic list comprehension
let ys = reduce Collected over x in 1..10 { yield x * x; };
// Equivalent to Python:
// ys = [x ** 2 for x in range(10)]

// # List comprehension with filtering
let ys = reduce Collected over x in 1..10 { if x % 2 == 0 { yield x * x; } };
// Equivalent to Python:
// ys = [x ** 2 for x in range(10) if x % 2 == 0]

// # Flatmap list comprehensions
let ys = reduce Collected over x in 1..10 {
    yield x * x;
    yield x * x * x;
};
// Equivalent to Python:
// ys = sum([[x ** 2, x ** 3] for x in range(10)], [])

Example 4:
Using statement notation as circuit-like expectation value estimation.

reduce estZExpectation = ExpectationReduction(true) over _ in 1..nShots {
    use qs = Qubit[nQubits];
    Prepare(qs);
    yield Measure(basis, qs);
    ResetAll(qs);
}

// Assuming library code:
function CountReduction<'TYield>(prevBatches : [Int], newBatch : ['TYield]) : Int {
    return SumI(prevBatches) + Length(newBatch);
}

internal function ExpectationReductionStateUpdate(prevBatches : [(Int, Int)], newBatch : [Result]) : (Int, Int) {
    reduce prevNZeros = PlusI over (n, _) in prevBatches { yield n; }
    reduce prevNShots = PlusI over (_, n) in prevBatches { yield n; }

    reduce nZeros = CountReduction over result in newBatch {
        if result == Zero {
            yield; // โ† Yields ().
        }
    }
    let nShots = Length(newBatch);
    return (nZeros + prevNZeros, nShots + prevNShots);
}

internal function ExpectationReductionResult(normalizeAsEigenvalue : Bool, finalState : (Int, Int)) : Double {
    let estPr = (IntAsDouble(Fst(finalState)) / IntAsDouble(Snd(finalState)));
    return normalizeAsEigenvalue
           ? 2.0 * estPr - 1.0
           | estPr;
}

@BatchReduction()
function ExpectationReduction(normalizeAsEigenvalue : Bool) : (
    (([(Int, Int)], (Int, Int)) -> (Int, Int)),
    ((Int, Int), Double)
) {
    return (
        ExpectationReductionStateUpdate,
        ExpectationReductionResult(normalizeAsEigenvalue, _)
    );
}

Example 5:
Using statement notation to express QCVV-style applications

operation EstimateSurvivalProbabilityAtLength(nQubits : Int, nShots : Int, nOperations : Int) : Double {
    reduce estPr = ExpectationReduction(false) over _ in 1..nShots {
        use qs = Qubit[nQubits];
        let sequence = DrawRandomSequence(nQubits, nOperations);
        for gate in sequence {
            ApplyGate(gate, qs);
        }
        yield Measure([PauliZ, size=nQubits], qs);
        ResetAll(qs);
    }
    return estPr;
}

Example 6:
Using device qubit literals from within reduction bodies

reduce estZExpectation = ExpectationReduction(true) over _ in 1..nShots {
    Prepare([#0, #1]);
    yield Measure(basis, [#0, #1]);
    ResetAll([#0, #1]);
}

Open questions

  • Can blocks be elided in expression-based notation when a single expression is always yielded?
  • Can commutativity of associative functions be opted-out from?

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Code sample in Iterations throws exceptions

  1. mutable results = new (Int, Results)[Length(qubits)]; should have type Result as the second tuple element.
  2. The second loop (the one that measures the qubits) should either iterate from 0 to Length - 1 or use index - 1 as qubit index in the array, otherwise it throws ArgumentOutOfRangeException.
  3. Not a bug per se, but it looks weird to allocate an empty array of the right size and then append new elements to it; it seems more common either to allocate an array of size 0 and append to it or to allocate an empty array of the right size and then overwrite its elements with measurement results.

Lambda expressions

Suggestion

The suggestion is to introduce a new kind of expression that constructs an anonymous callable. The expression would consist of an argument tuple or a symbol tuple, and a single expression that defines the value that is returned when the constructed callable is invoked. The constructed callable can be either an operation or a function, but cannot be type parametrized.

Considerations

The current mechanism of constructing anonymous callables locally is partial application. While rather powerful, it requires a declaration at a global scope based on which new callables can be constructed locally. The suggested lambda expressions would allow to avoid having to declare a callable specifically to construct a suitable anonymous callable via partial applications. Example 1 given below illustrates this based on an example from the standard libraries.

Alternatively, one could consider extending the support for declaring anonymous local callables to include callables with a body that consists of several statements. A dedicated syntax when the body of the anonymous callable merely consists of a single expression allows to define a more concise syntax for this case.

Context

Supporting to construct anonymous local callables with a body that rather than consisting of a single expression consists of a block of statements is to be considered as part of considering related features. A dedicated syntax for the suggested special case potentially introduces a redundant way to express the same functionality in the future. I believe the benefit of having a less verbose way of expressing what I expect to be a very common use case nonetheless merits that in this case.

The return type of the constructed callable can always be inferred and so can operation characteristics, if applicable. It may be possible to always infer the argument type as well, but one could also limit support for argument type inference and require that the argument type is explicitly specified (in certain cases). If the argument type is inferred, it is sufficient to define the name(s) of the argument (items) as a symbol tuple. The consequence of potentially introducing some form of global type inference in the future also need to be considered when discussing inference for lambda expressions.

Whether there are any restrictions on what the returned expression may contain (e.g. mutable variables) as well as when subexpressions are evaluated (i.e. whether they are captured) needs to be clarified.

Examples

Example 1:
The function WithFirstInputApplied is declared purely to construct a suitable function to return in CurriedOp via partial application. This additional global declaration becomes unnecessary if lambda expressions are available.

    internal function WithFirstInputApplied<'T, 'U> (op : (('T, 'U) => Unit), arg1 : 'T) : ('U => Unit) {
        return op(arg1, _);
    }

    function CurriedOp<'T, 'U> (op : (('T, 'U) => Unit)) : ('T -> ('U => Unit)) {
        return WithFirstInputApplied(op, _);
    }

Example 2:
Tentatively suggested syntax for lambda expressions:

/// Returns a function that adds i to a given value.
function Increment (i : Int) : (Int -> Int) {

    // unused statement to illustrate syntax
    let square = (a : Int) -> a*a; // the argument type is explicitly specified

    return a -> a + i; // the argument type can be inferred in this case
}

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Remove "new T[n]" syntax for creating arrays

Suggestion

Q# supports two distinct syntaxes for creating arrays: square brackets like [1, 2, 3], and the new keyword like new Int[3]. When an array is created using the new keyword, all elements are initialized to a default value based on the type. The syntax to create arrays using new should be removed from the language.

Considerations

The existence of new T[n] has negative effects on the language.

The most important negative effect is the assumption that every type has a default value. This is not a reasonable assumption, because it is not possible to define useful default values of types like Qubit and T1 -> T2. Their current default values are invalid and will trigger an error if they are used. Yet the syntax is the same both for types with valid default values, and those without, which is misleading: while new Int[3] is a perfectly well-defined value whose elements can be used immediately, new Qubit[3] creates a potential trap that will crash the program if any value in the array is used.

This assumption is also invalid in the presence of uninhabited types such as Void. If Q# requires that every type has a default value, then uninhabited types are impossible to express properly. This can even cause bugs in the soundness of a type system, as demonstrated by this bug in Java's generics that happened because of the existence of a value (specifically null) for a type that should have been uninhabited.

Q#'s default values also caused a subtle bug in the Q# runtime: because the default values required by Q# do not always match the default values of the corresponding C# types (according to C#'s default keyword), it is possible to create null values of types like Result, String, and even Unit. See microsoft/qsharp-runtime#359.

Finally, the use of the new keyword is inconsistent with other ways to create values in Q#. new is required only when creating a default-initialized array, not when using a square bracket array literal or when creating values of other types.

Context

See microsoft/qsharp-compiler#589 for a more detailed discussion about default values and alternatives to the new T[n] syntax.

Examples

There are functions in the standard library that can create arrays instead of using the new T[n] syntax.

// Instead of:
let empty = new Int[0];
let results = new Result[5];

// Use:
let empty = EmptyArray<Int>();
let results = ConstantArray(5, Zero);

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

Pattern matching and match expressions

Suggestion

This suggestion proposes to add patterns to Q#, and to allow patterns to be used in deconstructing and matching values.

Considerations

The Q# language does not currently have a way of denoting that an action should be taken or a value should be computed from a list of closely related conditional expressions, similar to the switch of case statements common to many imperative languages. In absentia such a feature, operations that make use of techniques such as lookup tables can become unwieldy, such as seen in microsoft/QuantumLibraries#409.

To resolve this in a way that also builds a path towards adopting more functional-inspired features in the future, this suggestion would introduce match expressions that allow breaking down the various cases of a given value by deconstructing that value into patterns. Where side effects are necessary and/or desirable, such that an expression form is not appropriate, this suggestion would also introduce a new if let statement (similar to existing constructs in Rust and C#) that allows conditioning the execution of a block of statements on whether or not a given value matches a pattern.

The design of this suggestion is intended to present patterns as a generalization of existing Q# concepts, such as deconstructing assignments, and to allow for the future growth of Q# in a consistent and coherent fashion.

Context

This suggestion generalizes the existing deconstruction feature, allowing for deconstructing more complicated patterns.
In addition, this change would make it easier to develop additional Q# language features such as discriminated unions (see #51).

Related features in other languages:

Related Q# proposals and suggestions:

Kinds of patterns

We say that a pattern pattern is irrefutable for a type 'T if all possible values of type 'T match pattern. A pattern that is not irrefutable is refutable. A list of patterns pattern1, pattern2, ... is exhaustive for a value of type 'T if pattern1 | pattern2 | ... is irrefutable for 'T.

Kinds of patterns and pattern operators that may be worth considering:

  • Wildcard pattern (_), matches any value.
  • Named pattern (pattern as name), matches pattern, but captures the value as a new immutable binding. As a shorthand, name on its own acts as _ as name; this can always be disambiguated, since arbitrary expressions cannot be used as patterns.
  • Tuple pattern ((pattern1, pattern2, ...)), matches values of a given arity, where each part of a value matches the corresponding pattern.
  • Alternatives (pattern1 | pattern2 | ...), matches any value that matches pattern1, pattern2, or so forth.
  • Literal pattern (e.g.: (), 100L, 42, 3.14, PauliX, Zero, or "foo"): matches only the exact value of the given literal.
  • Named item pattern (e.g.: (Real -> pattern, Imag -> _)): matches named items of a UDT.
  • UDT pattern (Complex(0.0, _)): similar to the tuple pattern, but matches on the unwrapped value of a UDT constructor. Possibly redundant with named item patterns, but is less explicit.
  • Empty array pattern ([]): matches only the empty array of a given type.
  • Array-by-subscript pattern (e.g.: [0 -> pattern1, 1... -> pattern2]): matches when an index or range is valid, and when subscripting the value by the given index or range matches the pattern on the right of ->.
  • Array deconstructor pattern (e.g.: [pattern1, pattern2]): matches an array of the exact given length when each pattern is individually matched.
  • Guarded pattern (e.g: _ as foo when foo > 40): matches only when a Boolean condition is met.

Note that this list explicitly excludes anything that would cause reification of type information at runtime. For example, a type pattern cannot be used to refine an unconstrained generic:

function Bad<'T>(input : 'T) : String {
    if let q : Qubit = input { // ๐Ÿ’ฃ, would refine 'T based on type information only available at runtime.
        return "Was a qubit!";
    } else {
        return "Was not a qubit!";
    }
}

This suggestion also excludes any consideration of F#-style active patterns,

Using patterns

This suggestion would extend Q# to allow using patterns in three distinct contexts:

  • Deconstructing irrefutable patterns (let, for, use, or borrow statements).
  • Conditionally deconstructing refutable patterns (if let, similar to Rust's if let statement and C#'s if (... is pattern) statement).
  • Deconstructing values into one of an exhaustive list of patterns (match expression, similar to F#'s and Rust's match, and to C#'s switch expressions).

Examples

Example 1:
Do something for each different Pauli operator.

operation ApplyP(pauli : Pauli, target : Qubit) : Unit is Adj + Ctl {
    let op = match pauli {
        PauliI -> I,
        PauliX -> X,
        PauliY -> Y,
        PauliZ -> Z
    };
    op(target);
}

Example 2:
Define multiplication between Klein group elements (similar to group product and inverse definitions in microsoft/QuantumLibraries#409).

newtype K4 = (Bool, Bool);
function TimesK4(left : K4, right : K4) : K4 {
    return match (left, right) {
        // Match identity elements.
        (K4(false, false), _) -> right,
        (_, K4(false, false)) -> left,
        // All elements square to themselves.
        _ when left == right -> K4(false, false),
        // Remaining 6 elements:
        (K4(false, true), K4(true, false)) -> K4(true, true),
        (K4(false, true), K4(true, true)) -> K4(true, false),
        (K4(true, false), K4(false, true)) -> K4(true, true),
        (K4(true, false), K4(true, true)) -> K4(false, true),
        (K4(true, true), K4(false, true)) -> K4(true, false),
        (K4(true, true), K4(true, false)) -> K4(false, true)
    };
}

Example 3:
Apply different decompositions based on the number of controls.

// Adapted from:
// https://github.com/microsoft/qsharp-runtime/blob/974a385cc57c2b663e8134c1f3170f9cb8ae5fb1/src/Simulation/TargetDefinitions/Decompositions/XFromSinglyControlled.qs#L23
operation X(qubit : Qubit) : Unit is Adj + Ctl {
    body (...) {
        Controlled X([], qubit);
    }

    controlled (controls, ...) {
        match controls {
            [] -> ApplyUncontrolledX(qubit),
            [control] -> ApplyControlledX(control, qubit),
            [control0, control1] -> CCNOT(control0, control1, qubit),
            _ -> ApplyWithLessControlsA(Controlled X, (controls, qubit))
        }
    }
}

// Adapted from:
// https://github.com/microsoft/qsharp-runtime/blob/974a385cc57c2b663e8134c1f3170f9cb8ae5fb1/src/Simulation/TargetDefinitions/Decompositions/Measure.qs#L43-L69
operation Measure(bases : Pauli[], qubits : Qubit[]) : Result {
    if (Length(bases) != Length(qubits)) { fail "Arrays 'bases' and 'qubits' must be of the same length."; }
    if let ([basis], [qubit]) = (bases, qubits) {
        within {
            MapPauli(qubit, PauliZ, basis);
        } apply {
            let res = M(qubit);
            PreparePostM(res, qubit);
            return res;
        }
    } else {
        use q = Qubit();
        within {
            H(q);
        } apply {
            for (basis, qubit) in Zipped(bases, qubits) {
                Controlled (match basis {
                    PauliX -> X,
                    PauliY -> Y,
                    PauliZ -> Z,
                    _ -> I
                })(basis, qubit);
            }
        }
        let res =  M(q);
        Reset(q);
        return res;
    }
}

Examples using other proposed and suggested Q# enhancements

Example 4:
Using patterns with type-parameterized UDT and discriminated union features.

// As described at https://github.com/microsoft/qsharp-language/issues/51,
// we can use type-parameterized UDTs and discriminated unions together to
// define a "maybe" DU that represents the possibility that a value may not
// exist.
newtype Maybe<'T> =
    | Some('T)
    | None();
// This DU is similar to the Option DU from Rust and F#, the Maybe DU from
// Haskell, the Optional DU from Swift, the Nullable struct from C#, the
// Optional type hint from Python, and so forth.

// Once defined, we can then use Maybe<'T> to represent in a type-safe manner
// the idea that a function may or may not return a value:

/// # Summary
/// Returns a big integer represented as a 64-bit integer if it is within
/// the range of valid values for 64-bit integers, and `None` otherwise.
function MaybeBigIntAsInt(value : BigInt) : Maybe<Int> { ... }

// This suggestion would extend the feature suggested in
// https://github.com/microsoft/qsharp-language/issues/51
// by allowing deconstructing discriminated unions into their case constructors.
// Since case deconstructors would then be refutable patterns, we could use
// the `if let` suggestion above to both check whether a pattern matches,
// and to capture the expression matched by the pattern.

if let Some(smallInt) = MaybeBigIntAsInt(value) {
    // In this block, we are guaranteed that MaybeBigIntAsInt returned
    // a concrete value, and have bound that value to the immutable variable
    // `smallInt`.
} else {
    // In this block, the output of `MaybeBigIntAsInt(value)` did not match
    // the refutable pattern `Some(smallInt)` such that the `if let` condition
    // fails. Thus, we know that the BigInt โ†’ Int conversion failed, and can
    // proceed to handle that failure accordingly without needing exceptional
    // control flow.
}

Example 5:
Deconstructing anonymous DUs (copied from #51).

// As described at https://github.com/microsoft/qsharp-language/issues/51,
// we could consider anonymous DUs by using the case constructor delimiter `|`
// to combine various types together.
// This does not represent runtime reification of type information, however,
// as such anonymous DUs can always be converted to explicit and named DUs.
// That is, this suggestion from https://github.com/microsoft/qsharp-language/issues/51
// is effectively an example of syntactic sugar.

operation PrepareArbitraryState(coefficients : (Double[] | Complex[] | ComplexPolar[]), target : Qubit[]) : Unit is Adj + Ctl {
    let complexCoefficients = coefficients match {
        Double[](realCoefficients) ->
             // In this arm, realCoefficients is a resolution of the anonymous DU
             // to have the concrete type Double[] instead.
             Mapped(Complex(_, 0.0), realCoefficients),
         Complex[](alreadyComplexCoefficients) -> alreadyComplexCoefficients,
         ComplexPolar[](complexPolarCoefficients) ->
             Mapped(ComplexPolarAsComplex, complexPolarCoefficients)
          // The match expression is exhaustive here, such that no blank pattern
          // is needed in order to guarantee that complexCoefficients has
          // a value of type Complex[] in all cases.
    };

    // Use complexCoefficients in the rest of the implementation...
}

Note that this does not represent reifying arbitrary runtime type information as excluded above.
Rather, the type (Double[] | Complex[] | ComplexPolar[]) represents an anonymous DU with case constructors Double[], Complex[], and ComplexPolar[], such that all possible cases are known exactly at compile time.

Affidavit (please fill out)

Please add ticks by placing a cross in the box:

  • I have searched both open and closed suggestions and proposals on this site and believe this is not a duplicate.
  • I believe that the spirit of this suggestion is aligned with the design principles and general vision for Q#.

Please tick all that apply:

  • This is not a breaking change to the Q# language design
  • I or my organization would be willing to help implement and/or test this

QIR Array Slicing does not match spec

Array slicing is supposed to work like copy, where it includes a force parameter and will behave differently based on that and how many alias counts the target array has. In the QIR spec it has signature %Array*(%Array*, %Range, i1) and describes it as:

Creates and returns an array that is a slice of an existing 1-dimensional array. The slice may be accessing the same memory as the given array unless its alias count is larger than 0 or the last argument is true. The %Range specifies the indices that should be the elements of the returned array. The reference count of the elements remains unchanged.

Meanwhile the runtime currently defines __quantum__rt__array_slice_1d as:

https://github.com/microsoft/qsharp-runtime/blob/c84d56d98ae58bbf99aa980d1a063a81005611f9/src/Qir/Runtime/lib/QIR/bridge-rt.ll#L294
And the implementation always copies.

__quantum__rt__array_slice has the same problem.

@bettinaheim, @kuzminrobin, @alan-geller FYI

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.