/// <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); }
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); } }
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())); }
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); } }
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); } }
/// <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]"); }
/// <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}"); }
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); }
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))))); }
/// <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); }
/// <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)); }