Exemple #1
0
        /// <summary>
        /// Adds a TargetInstruction attribute to each intrinsic callable that doesn't have one,
        /// unless the automatically determined target instruction name conflicts with another target instruction name.
        /// The automatically determined name of the target instruction is the lower case version of the unqualified callable name.
        /// Type constructors and generic callables are left unchanged.
        /// </summary>
        /// <returns>Returns true if all missing attributes have been successfully added and false if some attributes could not be added.</returns>
        /// <exception cref="InvalidOperationException">All intrinsic callables need to have exactly one body specialization.</exception>
        public static bool TryAddMissingTargetInstructionAttributes(QsCompilation compilation, out QsCompilation transformed)
        {
            var           success          = true;
            List <string> instructionNames = new List <string>();

            // populate the intruction names with all manually specified ones first
            foreach (var callable in compilation.Namespaces.Callables())
            {
                var manuallySpecified = SymbolResolution.TryGetTargetInstructionName(callable.Attributes);
                if (manuallySpecified.IsValue)
                {
                    instructionNames.Add(manuallySpecified.Item);
                }
            }

            QsCallable AddAttribute(QsCallable callable)
            {
                if (callable.Specializations.Length != 1)
                {
                    throw new InvalidOperationException("intrinsic callable needs to have exactly one body specialization");
                }

                var instructionName = callable.FullName.Name.ToLowerInvariant();

                if (instructionNames.Contains(instructionName))
                {
                    success = false;
                    return(callable);
                }
                return(callable.AddAttribute(
                           AttributeUtils.BuildAttribute(
                               BuiltIn.TargetInstruction.FullName,
                               AttributeUtils.StringArgument(instructionName))));
            }

            QsNamespace AddAttributes(QsNamespace ns)
            {
                var elements = ns.Elements.Select(element =>
                                                  element is QsNamespaceElement.QsCallable callable &&
                                                  callable.Item.IsIntrinsic &&
                                                  callable.Item.Signature.TypeParameters.Length == 0 &&
                                                  !NameDecorator.IsAutoGeneratedName(callable.Item.FullName) &&
                                                  !callable.Item.Kind.IsTypeConstructor &&
                                                  !callable.Item.Attributes.Any(BuiltIn.DefinesTargetInstruction)
                    ? QsNamespaceElement.NewQsCallable(AddAttribute(callable.Item))
                    : element);

                return(new QsNamespace(ns.Name, elements.ToImmutableArray(), ns.Documentation));
            }

            var namespaces = compilation.Namespaces.Select(AddAttributes).ToImmutableArray();

            transformed = new QsCompilation(namespaces, compilation.EntryPoints);
            return(success);
        }
Exemple #2
0
 private bool NamespaceElementFilter(QsNamespaceElement elem)
 {
     if (elem is QsNamespaceElement.QsCallable call)
     {
         return(BuiltIn.RewriteStepDependencies.Contains(call.Item.FullName) || this.SharedState.IntrinsicCallableSet.Contains(call.Item.FullName));
     }
     else
     {
         return(true);
     }
 }
Exemple #3
0
                public override QsNamespace OnNamespace(QsNamespace ns)
                {
                    SharedState.NamespaceCallables.TryGetValue(ns.Name, out IEnumerable <QsCallable> concretesInNs);

                    // Removes unused or generic callables from the namespace
                    // Adds in the used concrete callables
                    return(ns.WithElements(elems => elems
                                           .Where(elem => !(elem is QsNamespaceElement.QsCallable))
                                           .Concat(concretesInNs?.Select(call => QsNamespaceElement.NewQsCallable(call)) ?? Enumerable.Empty <QsNamespaceElement>())
                                           .ToImmutableArray()));
                }
Exemple #4
0
 private static bool Filter(QsNamespaceElement elem, ImmutableHashSet <QsQualifiedName> graphNodes)
 {
     if (elem is QsNamespaceElement.QsCallable call)
     {
         return(graphNodes.Contains(call.Item.FullName));
     }
     else
     {
         return(true);
     }
 }
 private bool NamespaceElementFilter(QsNamespaceElement elem)
 {
     if (elem is QsNamespaceElement.QsCallable call)
     {
         return(SharedState.ExemptCallableSet.Contains(call.Item.FullName));
     }
     else
     {
         return(true);
     }
 }
Exemple #6
0
 private static bool FilterWithIntrinsics(QsNamespaceElement elem, ImmutableHashSet <QsQualifiedName> graphNodes)
 {
     if (elem is QsNamespaceElement.QsCallable call)
     {
         return(call.Item.Specializations.Any(spec => spec.Implementation.IsIntrinsic) ||
                graphNodes.Contains(call.Item.FullName));
     }
     else
     {
         return(true);
     }
 }
Exemple #7
0
        /// <summary>
        /// Returns the source of the given QsNamespaceElement (either QsCallable or QsCustomTypes)
        /// </summary>
        public static string SourceFile(this QsNamespaceElement e)
        {
            if (e is QsNamespaceElement.QsCallable c)
            {
                return(c.Item.SourceFile.Value);
            }
            else if (e is QsNamespaceElement.QsCustomType t)
            {
                return(t.Item.SourceFile.Value);
            }

            return("[Unknown]");
        }
Exemple #8
0
        /// <summary>
        /// Returns the name of the given QsNamespaceElement (either QsCallable or QsCustomTypes)
        /// </summary>
        public static string ToFullName(this QsNamespaceElement e)
        {
            var name = UNKNOWN_OPERATION;

            if (e is QsNamespaceElement.QsCallable c)
            {
                name = c.Item.FullName;
            }
            else if (e is QsNamespaceElement.QsCustomType t)
            {
                name = t.Item.FullName;
            }

            return($"{name.Namespace.Value}.{name.Name.Value}");
        }
Exemple #9
0
        public void ExcludeInaccessible()
        {
            var elements = new[] { Access.Public, Access.Internal }
            .SelectMany(access =>
            {
                var source = new Source("Tests.qs", QsNullable <string> .Null);
                var unit   = ResolvedType.New(QsType.UnitType);

                var signature = new ResolvedSignature(
                    Array.Empty <QsLocalSymbol>().ToImmutableArray(),
                    unit,
                    unit,
                    CallableInformation.NoInformation);
                var argumentTuple = QsTuple <ArgDeclType> .NewQsTuple(ImmutableArray.Create <QsTuple <ArgDeclType> >());
                var callable      = new QsCallable(
                    kind: QsCallableKind.Operation,
                    fullName: MakeFullName(access + "Operation"),
                    attributes: ImmutableArray <QsDeclarationAttribute> .Empty,
                    access,
                    source: source,
                    location: ZeroLocation,
                    signature: signature,
                    argumentTuple: argumentTuple,
                    specializations: ImmutableArray.Create <QsSpecialization>(),
                    documentation: ImmutableArray.Create <string>(),
                    comments: QsComments.Empty);

                var typeItems = QsTuple <QsTypeItem> .NewQsTuple(
                    ImmutableArray.Create(QsTuple <QsTypeItem> .NewQsTupleItem(QsTypeItem.NewAnonymous(unit))));
                var type = new QsCustomType(
                    fullName: MakeFullName(access + "Type"),
                    attributes: ImmutableArray <QsDeclarationAttribute> .Empty,
                    access,
                    source: source,
                    location: ZeroLocation,
                    type: unit,
                    typeItems: typeItems,
                    documentation: ImmutableArray.Create <string>(),
                    comments: QsComments.Empty);
                return(new[]
                {
                    QsNamespaceElement.NewQsCallable(callable),
                    QsNamespaceElement.NewQsCustomType(type)
                });
            });
            var emptyLookup = Array.Empty <ImmutableArray <string> >().ToLookup(x => "");
            var ns          = new QsNamespace(CanonName, elements.ToImmutableArray(), emptyLookup);
            var docNs       = new DocNamespace(ns);
            var stream      = new MemoryStream();

#pragma warning disable 618 // WriteToStream is obsolete.
            docNs.WriteToStream(stream, null);
#pragma warning restore 618

            var expected = @"### YamlMime:QSharpNamespace
# This file is automatically generated.
# Please do not modify this file manually, or your changes may be lost when
# documentation is rebuilt.

uid: microsoft.quantum.canon
name: Microsoft.Quantum.Canon
operations:
- uid: microsoft.quantum.canon.publicoperation
  summary: ''
newtypes:
- uid: microsoft.quantum.canon.publictype
  summary: ''
...
";
            var actual   = Encoding.UTF8.GetString(stream.ToArray());
            Assert.Equal(expected, actual);
        }
Exemple #10
0
            public override QsCallable OnFunction(QsCallable c) => c; // Prevent anything in functions from being lifted

            /// <inheritdoc/>
            public override QsNamespace OnNamespace(QsNamespace ns)
            {
                // Generated operations list will be populated in the transform
                this.SharedState.GeneratedOperations = new List <QsCallable>();
                return(base.OnNamespace(ns)
                       .WithElements(elems => elems.AddRange(this.SharedState.GeneratedOperations.Select(op => QsNamespaceElement.NewQsCallable(op)))));
            }
Exemple #11
0
 /// <summary>
 /// Returns the source of the given QsNamespaceElement (either QsCallable or QsCustomTypes)
 /// </summary>
 public static string SourceFile(this QsNamespaceElement e) =>
 (e switch
        /// <summary>
        /// Creates a separate callable for each intrinsic specialization,
        /// and replaces the specialization implementations of the original callable with a call to these.
        /// Self adjoint generation directives in intrinsic callables are replaced by a provided implementation.
        /// Type constructors and generic callables or callables that already define a target instruction name are left unchanged.
        /// </summary>
        /// <exception cref="ArgumentException">
        /// An intrinsic callable contains non-intrinsic specializations
        /// or a non-intrinsic callable contains intrinsic specializations,
        /// or the a callable doesn't have a body specialization.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// A specialization has explicit type arguments;
        /// Monomorphization needs to run before separating target instructions.
        /// </exception>
        private static QsNamespace LiftIntrinsicSpecializations(QsNamespace ns)
        {
            var elements = ImmutableArray.CreateBuilder <QsNamespaceElement>();

            foreach (var element in ns.Elements)
            {
                if (element is QsNamespaceElement.QsCallable c &&
                    c.Item.Signature.TypeParameters.Length == 0 &&
                    !c.Item.Kind.IsTypeConstructor)
                {
                    if (c.Item.IsIntrinsic)
                    {
                        QsCallable callable = c.Item;
                        if (!callable.Specializations.Any(spec => spec.Kind.IsQsBody))
                        {
                            throw new ArgumentException("missing body specialization");
                        }
                        else if (callable.Specializations.Any(spec => spec.TypeArguments.IsValue))
                        {
                            throw new InvalidOperationException("specialization with type arguments");
                        }
                        else if (callable.Specializations.Length == 1 && callable.Attributes.Any(BuiltIn.DefinesTargetInstruction))
                        {
                            elements.Add(element);
                        }
                        else
                        {
                            QsQualifiedName GeneratedName(QsSpecializationKind kind) =>
                            new QsQualifiedName(callable.FullName.Namespace, $"{callable.FullName.Name}{SpecializationSuffix(kind)}");

                            var specializations = ImmutableArray.CreateRange(callable.Specializations.Select(spec =>
                            {
                                var inferredInfo = spec.Signature.Information.InferredInformation;
                                if (!inferredInfo.IsIntrinsic && !inferredInfo.IsSelfAdjoint)
                                {
                                    throw new ArgumentException("non-intrinsic specialization for intrinsic callable");
                                }

                                // Get the correct argument tuple both for the added intrinsic callable
                                // and the generated provided specialization that replaces the intrinsic one.
                                var argTuple = BuildSpecArgTuple(callable.ArgumentTuple, spec.Kind);

                                // Create a separate callable for that specialization,
                                // unless the specialization is not needed for a self-adjoint callable.

                                var genCallableSignature = new ResolvedSignature(
                                    ImmutableArray <QsLocalSymbol> .Empty,
                                    spec.Signature.ArgumentType,
                                    spec.Signature.ReturnType,
                                    new CallableInformation(
                                        ResolvedCharacteristics.Empty,
                                        new InferredCallableInformation(isIntrinsic: true, isSelfAdjoint: false)));
                                var genCallableName = GeneratedName(
                                    inferredInfo.IsSelfAdjoint && spec.Kind.IsQsAdjoint ? QsSpecializationKind.QsBody :
                                    inferredInfo.IsSelfAdjoint && spec.Kind.IsQsControlledAdjoint ? QsSpecializationKind.QsControlled :
                                    spec.Kind);

                                if (!inferredInfo.IsSelfAdjoint || spec.Kind.IsQsBody || spec.Kind.IsQsControlled)
                                {
                                    var genCallableBody = new QsSpecialization(
                                        QsSpecializationKind.QsBody,
                                        genCallableName,
                                        spec.Attributes,
                                        spec.Source,
                                        QsNullable <QsLocation> .Null,
                                        spec.TypeArguments,
                                        genCallableSignature,
                                        SpecializationImplementation.Intrinsic,
                                        spec.Documentation,
                                        spec.Comments);
                                    var genCallable = new QsCallable(
                                        callable.Kind,
                                        genCallableName,
                                        callable.Attributes,
                                        callable.Access,
                                        spec.Source,
                                        spec.Location,
                                        genCallableSignature,
                                        argTuple,
                                        ImmutableArray.Create(genCallableBody),
                                        ImmutableArray <string> .Empty,
                                        QsComments.Empty);
                                    elements.Add(QsNamespaceElement.NewQsCallable(genCallable));
                                }

                                // Create a specialization that calls into the generated callable,
                                // or the corresponding callable no callable for the specialization
                                // has been added due to hte operation being self-adjoint.

                                var genCallableType =
                                    callable.Kind == QsCallableKind.Operation
                                    ? OperationTypeFromSignature(genCallableSignature)
                                    : TypeKind.NewFunction(genCallableSignature.ArgumentType, genCallableSignature.ReturnType);
                                var call = SyntaxGenerator.CallNonGeneric(
                                    IdentifierForCallable(genCallableName, genCallableType),
                                    SyntaxGenerator.ArgumentTupleAsExpression(argTuple));
                                var statement = new QsStatement(
                                    QsStatementKind.NewQsReturnStatement(call),
                                    LocalDeclarations.Empty,
                                    QsNullable <QsLocation> .Null,
                                    QsComments.Empty);
                                var localDeclarations = new LocalDeclarations(
                                    SyntaxGenerator.ValidDeclarations(SyntaxGenerator.ExtractItems(argTuple)));

                                return(spec.WithImplementation(SpecializationImplementation.NewProvided(
                                                                   argTuple,
                                                                   new QsScope(ImmutableArray.Create(statement), localDeclarations))));
                            }));

                            // Create a callable that contains all specializations that
                            // call into the generated callables for each specialization.

                            var inlineAttribute = AttributeUtils.BuildAttribute(BuiltIn.Inline.FullName, SyntaxGenerator.UnitValue);
                            var signature       = new ResolvedSignature(
                                ImmutableArray <QsLocalSymbol> .Empty,
                                callable.Signature.ArgumentType,
                                callable.Signature.ReturnType,
                                new CallableInformation(
                                    callable.Signature.Information.Characteristics,
                                    new InferredCallableInformation(isSelfAdjoint: callable.IsSelfAdjoint, isIntrinsic: false)));
                            var redirect = new QsCallable(
                                callable.Kind,
                                callable.FullName,
                                ImmutableArray.Create(inlineAttribute),
                                callable.Access,
                                callable.Source,
                                callable.Location,
                                signature,
                                callable.ArgumentTuple,
                                specializations,
                                callable.Documentation,
                                callable.Comments);
                            elements.Add(QsNamespaceElement.NewQsCallable(redirect));
                        }
                    }
                    else if (c.Item.Specializations.Any(spec => spec.Implementation.IsIntrinsic))
                    {
                        throw new ArgumentException("intrinsic specialization for non-intrinsic callable");
                    }
                    else
                    {
                        elements.Add(element);
                    }
                }
        /// <summary>
        /// Replaces self adjoint generation directives in non-intrinsic callables with a provided implementation
        /// that calls the appropriate specialization of the callable.
        /// Intrinsic callables are left unchanged.
        /// </summary>
        private static QsNamespace ReplaceSelfAdjointSpecializations(QsNamespace ns)
        {
            var elements = ImmutableArray.CreateBuilder <QsNamespaceElement>();

            foreach (var element in ns.Elements)
            {
                if (element is QsNamespaceElement.QsCallable c)
                {
                    if (c.Item.IsSelfAdjoint && !c.Item.IsIntrinsic)
                    {
                        var callableId = IdentifierForCallable(
                            c.Item.FullName,
                            OperationTypeFromSignature(c.Item.Signature));

                        var callable = c.Item.WithSpecializations(specs =>
                                                                  ImmutableArray.CreateRange(specs.Select(spec =>
                        {
                            if (spec.Kind.IsQsBody || spec.Kind.IsQsControlled)
                            {
                                return(spec);
                            }
                            else
                            {
                                var argTuple = BuildSpecArgTuple(c.Item.ArgumentTuple, spec.Kind);
                                var callee   = spec.Kind.IsQsControlledAdjoint
                                        ? SyntaxGenerator.AutoGeneratedExpression(
                                    ExpressionKind.NewControlledApplication(callableId),
                                    OperationTypeFromSignature(spec.Signature),
                                    false)
                                        : callableId;

                                var call = SyntaxGenerator.CallNonGeneric(
                                    callee,
                                    SyntaxGenerator.ArgumentTupleAsExpression(argTuple));
                                var statement = new QsStatement(
                                    QsStatementKind.NewQsReturnStatement(call),
                                    LocalDeclarations.Empty,
                                    QsNullable <QsLocation> .Null,
                                    QsComments.Empty);
                                var localDeclarations = new LocalDeclarations(
                                    SyntaxGenerator.ValidDeclarations(SyntaxGenerator.ExtractItems(argTuple)));

                                return(spec.WithImplementation(SpecializationImplementation.NewProvided(
                                                                   argTuple,
                                                                   new QsScope(ImmutableArray.Create(statement), localDeclarations))));
                            }
                        })));
                        elements.Add(QsNamespaceElement.NewQsCallable(callable));
                    }
                    else
                    {
                        elements.Add(element);
                    }
                }
                else
                {
                    elements.Add(element);
                }
            }
            return(new QsNamespace(ns.Name, elements.ToImmutable(), ns.Documentation));
        }
        public void ExcludeInaccessible()
        {
            var elements =
                new[] { AccessModifier.DefaultAccess, AccessModifier.Internal }
            .SelectMany(access =>
            {
                var source = NonNullable <string> .New("Tests.qs");
                var unit   = ResolvedType.New(QsType.UnitType);

                var signature = new ResolvedSignature(Array.Empty <QsLocalSymbol>().ToImmutableArray(),
                                                      unit,
                                                      unit,
                                                      CallableInformation.NoInformation);
                var argumentTuple = QsTuple <ArgDeclType> .NewQsTuple(ImmutableArray.Create <QsTuple <ArgDeclType> >());
                var callable      = new QsCallable(kind: QsCallableKind.Operation,
                                                   fullName: MakeFullName(access + "Operation"),
                                                   attributes: ImmutableArray <QsDeclarationAttribute> .Empty,
                                                   modifiers: new Modifiers(access),
                                                   sourceFile: source,
                                                   location: ZeroLocation,
                                                   signature: signature,
                                                   argumentTuple: argumentTuple,
                                                   specializations: ImmutableArray.Create <QsSpecialization>(),
                                                   documentation: ImmutableArray.Create <string>(),
                                                   comments: QsComments.Empty);

                var typeItems = QsTuple <QsTypeItem> .NewQsTuple(
                    ImmutableArray.Create(QsTuple <QsTypeItem> .NewQsTupleItem(QsTypeItem.NewAnonymous(unit))));
                var type = new QsCustomType(fullName: MakeFullName(access + "Type"),
                                            attributes: ImmutableArray <QsDeclarationAttribute> .Empty,
                                            modifiers: new Modifiers(access),
                                            sourceFile: source,
                                            location: ZeroLocation,
                                            type: unit,
                                            typeItems: typeItems,
                                            documentation: ImmutableArray.Create <string>(),
                                            comments: QsComments.Empty);
                return(new[]
                {
                    QsNamespaceElement.NewQsCallable(callable),
                    QsNamespaceElement.NewQsCustomType(type)
                });
            });
            var emptyLookup = Array.Empty <ImmutableArray <string> >().ToLookup(x => NonNullable <string> .New(""));
            var ns          = new QsNamespace(CanonName, elements.ToImmutableArray(), emptyLookup);
            var docNs       = new DocNamespace(ns);
            var stream      = new MemoryStream();

            docNs.WriteToStream(stream, null);

            var expected = @"### YamlMime:QSharpNamespace
uid: microsoft.quantum.canon
name: Microsoft.Quantum.Canon
operations:
- uid: microsoft.quantum.canon.defaultaccessoperation
  summary: ''
newtypes:
- uid: microsoft.quantum.canon.defaultaccesstype
  summary: ''
...
";
            var actual   = Encoding.UTF8.GetString(stream.ToArray());

            Assert.Equal(expected, actual);
        }
Exemple #15
0
        /// <summary>
        /// Performs Monomorphization on the given compilation. If the monomorphizeIntrinsics parameter
        /// is set to false, then intrinsics will not be monomorphized.
        /// </summary>
        public static QsCompilation Apply(QsCompilation compilation, bool monomorphizeIntrinsics = false)
        {
            var globals          = compilation.Namespaces.GlobalCallableResolutions();
            var concretizations  = new List <QsCallable>();
            var concreteNamesMap = new Dictionary <ConcreteCallGraphNode, QsQualifiedName>();

            var nodesWithResolutions = new ConcreteCallGraph(compilation).Nodes
                                       // Remove specialization information so that we only deal with the full callables.
                                       // Note: this only works fine if for all nodes in the call graph,
                                       // all existing functor specializations and their dependencies are also in the call graph.
                                       .Select(n => new ConcreteCallGraphNode(n.CallableName, QsSpecializationKind.QsBody, n.ParamResolutions))
                                       .Where(n => n.ParamResolutions.Any())
                                       .ToImmutableHashSet();

            var getAccessModifiers = new GetAccessModifiers((typeName) => GetAccessModifier(compilation.Namespaces.GlobalTypeResolutions(), typeName));

            // Loop through the nodes, getting a list of concrete callables
            foreach (var node in nodesWithResolutions)
            {
                // If there is a call to an unknown callable, throw exception
                if (!globals.TryGetValue(node.CallableName, out var originalGlobal))
                {
                    throw new ArgumentException($"Couldn't find definition for callable: {node.CallableName}");
                }

                if (monomorphizeIntrinsics || !originalGlobal.IsIntrinsic)
                {
                    // Get concrete name
                    var concreteName = NameDecorator.PrependGuid(node.CallableName);

                    // Add to concrete name mapping
                    concreteNamesMap[node] = concreteName;

                    // Generate the concrete version of the callable
                    var concrete =
                        ReplaceTypeParamImplementations.Apply(originalGlobal, node.ParamResolutions, getAccessModifiers)
                        .WithFullName(oldName => concreteName)
                        .WithSpecializations(specs => specs.Select(spec => spec.WithParent(_ => concreteName)).ToImmutableArray());
                    concretizations.Add(concrete);
                }
            }

            var callablesByNamespace = concretizations.ToLookup(x => x.FullName.Namespace);
            var namespacesWithImpls  = compilation.Namespaces.Select(ns =>
            {
                var elemsToAdd = callablesByNamespace[ns.Name].Select(call => QsNamespaceElement.NewQsCallable(call));

                return(ns.WithElements(elems =>
                                       elems
                                       .Where(elem =>
                                              !(elem is QsNamespaceElement.QsCallable call) ||
                                              !IsGeneric(call.Item) ||
                                              (call.Item.IsIntrinsic && !monomorphizeIntrinsics) ||
                                              BuiltIn.RewriteStepDependencies.Contains(call.Item.FullName))
                                       .Concat(elemsToAdd)
                                       .ToImmutableArray()));
            }).ToImmutableArray();

            var compWithImpls = new QsCompilation(namespacesWithImpls, compilation.EntryPoints);

            GetConcreteIdentifierFunc getConcreteIdentifier = (globalCallable, types) =>
                                                              GetConcreteIdentifier(concreteNamesMap, globalCallable, types);

            var intrinsicsToKeep = monomorphizeIntrinsics
                ? ImmutableHashSet <QsQualifiedName> .Empty
                : globals
                                   .Where(kvp => kvp.Value.Specializations.Any(spec => spec.Implementation.IsIntrinsic))
                                   .Select(kvp => kvp.Key)
                                   .ToImmutableHashSet();

            return(ReplaceTypeParamCalls.Apply(compWithImpls, getConcreteIdentifier, intrinsicsToKeep));
        }