private void AddEdge(QsQualifiedName identifier, QsSpecializationKind kind, TypeParameterResolutions typeRes, Range referenceRange) { if (this.CurrentNode is null) { throw new ArgumentException("AddEdge requires CurrentNode to be non-null."); } // Add an edge to the specific specialization kind referenced var called = new ConcreteCallGraphNode(identifier, kind, typeRes); var edge = new ConcreteCallGraphEdge(referenceRange); this.Graph.AddDependency(this.CurrentNode, called, edge); // Add all the specializations of the referenced callable to the graph var newNodes = this.GetSpecializationKinds(identifier) .Select(specKind => new ConcreteCallGraphNode(identifier, specKind, typeRes)); foreach (var node in newNodes) { if (!this.RequestStack.Contains(node) && !this.ResolvedNodeSet.Contains(node)) { this.Graph.AddNode(node); this.RequestStack.Push(node); } } }
internal override void AddDependency(QsQualifiedName identifier) { if (this.CurrentNode is null) { throw new ArgumentException("AddDependency requires CurrentNode to be non-null."); } var combination = new TypeResolutionCombination(this.ExprTypeParamResolutions); var typeRes = combination.CombinedResolutionDictionary.FilterByOrigin(identifier); this.ExprTypeParamResolutions.Clear(); var referenceRange = Range.Zero; if (this.CurrentStatementOffset.IsValue && this.CurrentExpressionRange.IsValue) { referenceRange = this.CurrentStatementOffset.Item + this.CurrentExpressionRange.Item; } var called = new CallGraphNode(identifier); var edge = new CallGraphEdge(typeRes, referenceRange); this.Graph.AddDependency(this.CurrentNode, called, edge); // If we are not processing all elements, then we need to keep track of what elements // have been processed, and which elements still need to be processed. if (this.WithTrimming && !this.RequestStack.Contains(called) && !this.ResolvedNodeSet.Contains(called)) { this.RequestStack.Push(called); } }
private static TypedExpression IdentifierForCallable(QsQualifiedName cName, TypeKind cType) => SyntaxGenerator.AutoGeneratedExpression( ExpressionKind.NewIdentifier( Identifier.NewGlobalCallable(cName), QsNullable <ImmutableArray <ResolvedType> > .Null), cType, false);
/// <summary> /// Constructor for ConcreteCallGraphNode objects. /// Strips position info from the given type parameter resolutions. /// </summary> public ConcreteCallGraphNode(QsQualifiedName callableName, QsSpecializationKind kind, TypeParameterResolutions paramResolutions) : base(callableName) { this.Kind = kind; // Remove position info from type parameter resolutions this.ParamResolutions = paramResolutions.ToImmutableDictionary( kvp => kvp.Key, kvp => StripPositionInfo.Apply(kvp.Value)); }
// static methods for convenience public static IEnumerable <Location> Find(QsQualifiedName idName, QsNamespace ns, QsLocation defaultOffset, out Tuple <NonNullable <string>, QsLocation> declarationLocation, IImmutableSet <NonNullable <string> > limitToSourceFiles = null) { var finder = new IdentifierReferences(idName, defaultOffset, limitToSourceFiles); finder.Transform(ns ?? throw new ArgumentNullException(nameof(ns))); declarationLocation = finder.DeclarationLocation; return(finder.Locations); }
internal override void AddDependency(QsQualifiedName identifier) { if (this.CurrentNode is null) { throw new ArgumentException("AddDependency requires CurrentNode to be non-null."); } var combination = new TypeResolutionCombination(this.ExprTypeParamResolutions.Append(this.CurrentNode.ParamResolutions)); var typeRes = combination.CombinedResolutionDictionary.FilterByOrigin(identifier); this.ExprTypeParamResolutions.Clear(); var referenceRange = Range.Zero; if (this.CurrentStatementOffset.IsValue && this.CurrentExpressionRange.IsValue) { referenceRange = this.CurrentStatementOffset.Item + this.CurrentExpressionRange.Item; } this.lastReferenceRange = referenceRange; void AddEdge(QsSpecializationKind kind) => this.AddEdge(identifier, kind, typeRes, referenceRange); if (this.IsInCall) { if (this.HasAdjointDependency && this.HasControlledDependency) { AddEdge(QsSpecializationKind.QsControlledAdjoint); } else if (this.HasAdjointDependency) { AddEdge(QsSpecializationKind.QsAdjoint); } else if (this.HasControlledDependency) { AddEdge(QsSpecializationKind.QsControlled); } else { AddEdge(QsSpecializationKind.QsBody); } } else { // The callable is being used in a non-call context, such as being // assigned to a variable or passed as an argument to another callable, // which means it could get a functor applied at some later time. // We're conservative and add all possible kinds defined for the callable. foreach (var kind in this.GetSpecializationKinds(identifier)) { AddEdge(kind); } } }
/// <summary> /// Handles adding the dependencies for specializations marked as self-inverse. /// </summary> internal void AddSelfInverseDependency(QsQualifiedName identifier, QsSpecializationKind targetSpec) { if (this.CurrentNode is null) { throw new ArgumentException("AddDependency requires CurrentNode to be non-null."); } var combination = new TypeResolutionCombination(new[] { this.CurrentNode.ParamResolutions }); var typeRes = combination.CombinedResolutionDictionary.FilterByOrigin(identifier); this.AddEdge(identifier, targetSpec, typeRes, this.lastReferenceRange); }
private void AddEdge(QsQualifiedName identifier, QsSpecializationKind kind, TypeParameterResolutions typeRes, Range referenceRange) { if (this.CurrentNode is null) { throw new ArgumentException("AddEdge requires CurrentNode to be non-null."); } var called = new ConcreteCallGraphNode(identifier, kind, typeRes); var edge = new ConcreteCallGraphEdge(referenceRange); this.Graph.AddDependency(this.CurrentNode, called, edge); if (!this.RequestStack.Contains(called) && !this.ResolvedNodeSet.Contains(called)) { this.RequestStack.Push(called); } }
private CheckForConstriction(QsQualifiedName origin) : base(new TransformationState(origin), TransformationOptions.NoRebuild) { }
public TransformationState(QsQualifiedName origin) { this.Origin = origin; }
private UpdateGeneratedOp(ImmutableArray <LocalVariableDeclaration <NonNullable <string> > > parameters, QsQualifiedName oldName, QsQualifiedName newName) : base(new TransformationState(parameters, oldName, newName)) { this.Expressions = new ExpressionTransformation(this); this.ExpressionKinds = new ExpressionKindTransformation(this); this.Types = new TypeTransformation(this); }
public static QsCallable Apply(QsCallable qsCallable, ImmutableArray <LocalVariableDeclaration <NonNullable <string> > > parameters, QsQualifiedName oldName, QsQualifiedName newName) { var filter = new UpdateGeneratedOp(parameters, oldName, newName); return(filter.Namespaces.OnCallableDeclaration(qsCallable)); }
/// <summary> /// Returns true if the declaration with the given qualified name would be accessible if it was referenced using /// its unqualified name, given the current namespace and a list of open namespaces. /// <para/> /// Note: Names that start with "_" are treated as "private;" they are only accessible from the namespace in /// which they are declared. /// </summary> private static bool IsAccessibleAsUnqualifiedName( QsQualifiedName qualifiedName, string?currentNamespace, IEnumerable <string> openNamespaces) => openNamespaces.Contains(qualifiedName.Namespace) && (!qualifiedName.Name.StartsWith("_") || qualifiedName.Namespace == currentNamespace);
public ImmutableHashSet <string> IdentifiersInCallable(QsQualifiedName name) => this.Identifiers.TryGetValue(name, out var ids) ? ids.ToImmutable() : null;
/// <summary> /// Generates an substitution class for a given callable with a given name /// </summary> /// /// <para> /// In the following we illustrate the syntax that is generated using /// as an example the `Microsoft.Quantum.Canon.ApplyAnd` operation with /// `Microsoft.Quantum.Intrinsic.CCNOT` as an alternative when using /// `ToffoliSimulator`. /// /// The generated code looks as follows: /// /// <code> /// namespace Microsoft.Quantum.Canon { /// public partial class ApplyAnd { /// public class Native : ApplyAnd { /// public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m) : base(m) { /// sim0 = m as ToffoliSimulator; /// } /// /// public override void __Init__() { /// base.Init(); /// if (sim0 != null) alternative0 = __Factory__.Get<Microsoft.Quantum.Intrinsic.CCNOT>(typeof(Microsoft.Quantum.Intrinsic.CCNOT)); /// } /// /// public override Func<(Qubit, Qubit, Qubit), QVoid> __Body__ => args => { /// if (sim0 != null) return alternative0.__Body__(args); /// else return base.__Body__(args); /// } /// /// // methods for other specializations ... /// /// private ToffoliSimulator sim0 = null; /// private Microsoft.Quantum.Intrinsic.CCNOT alternative0 = null; /// } /// } /// } /// </code> /// </para> /// /// <param name="name">Namespace and name of the callable</param> /// <param name="callable">Q# Callable</param> /// <param name="substitutionAttributes">All attribute values from @HasSubstitutionAttribute attributes of that callable</param> public void AddCallable(QsQualifiedName name, QsCallable callable, IEnumerable <(string AlternativeOperation, string InSimulator)> substitutionAttributes)
/// <summary> /// Formats a qualified name using dotted-name syntax. /// <summary> public static string ToFullName(this QsQualifiedName name) => name?.Namespace.Value + "." + name?.Name.Value;
/// <summary> /// Base constructor for call graph nodes. Initializes CallableName. /// </summary> protected CallGraphNodeBase(QsQualifiedName callableName) => this.CallableName = callableName;
private static IEnumerable <QsSpecializationKind> GetSpecializationKinds(ImmutableDictionary <QsQualifiedName, QsCallable> globals, QsQualifiedName callableName) { // If there is a call to an unknown callable, throw exception if (!globals.TryGetValue(callableName, out QsCallable currentCallable)) { throw new ArgumentException($"Couldn't find definition for callable: {callableName}"); } return(currentCallable.Specializations.Select(x => x.Kind).Distinct()); }
public IdentifierReferences(QsQualifiedName idName, QsLocation defaultOffset, IImmutableSet <NonNullable <string> > limitToSourceFiles = null) : base(new IdentifierLocation(idName, defaultOffset)) { this.IdentifierName = idName ?? throw new ArgumentNullException(nameof(idName)); this.RelevantSourseFiles = limitToSourceFiles; }
internal bool SetCurrentCallable(QsQualifiedName name) { this.CurrentCallable = name; return(name != null && this.Identifiers.TryAdd(name, ImmutableHashSet.CreateBuilder <string>())); }
// Rewrite Implementations private static Access GetAccessModifier(ImmutableDictionary <QsQualifiedName, QsCustomType> userDefinedTypes, QsQualifiedName typeName) { // If there is a reference to an unknown type, throw exception if (!userDefinedTypes.TryGetValue(typeName, out var type)) { throw new ArgumentException($"Couldn't find definition for user defined type: {typeName}"); } return(type.Access); }
/// <summary> /// Adds dependency to the graph from the current callable to the callable referenced by the given identifier. /// </summary> internal abstract void AddDependency(QsQualifiedName identifier);
/// <summary> /// Returns true if the qualified name is visible given the current namespace and a list of open namespaces. /// <para/> /// Names that start with "_" are treated as "private"; they are only visible from the namespace in which they /// are declared. /// </summary> private static bool IsVisible(QsQualifiedName qualifiedName, string currentNamespace, IEnumerable <string> openNamespaces) => openNamespaces.Contains(qualifiedName.Namespace.Value) && (!qualifiedName.Name.Value.StartsWith("_") || qualifiedName.Namespace.Value == currentNamespace);
// public static methods /// <summary> /// Returns a Q# attribute with the given name and argument that can be attached to a declaration. /// The attribute id is set to Null if the given name is null. /// The attribute argument is set to an invalid expression if the given argument is null. /// </summary> public static QsDeclarationAttribute BuildAttribute(QsQualifiedName name, TypedExpression arg) => new QsDeclarationAttribute(BuildId(name), QsNullable <Range> .Null, arg, Position.Zero, QsComments.Empty);
public TransformationState(ImmutableArray <LocalVariableDeclaration <NonNullable <string> > > parameters, QsQualifiedName oldName, QsQualifiedName newName) { this.Parameters = parameters; this.OldName = oldName; this.NewName = newName; }
// public static methods /// <summary> /// Returns a Q# attribute with the given name and argument that can be attached to a declaration. /// The attribute id is set to Null if the given name is null. /// The attribute argument is set to an invalid expression if the given argument is null. /// </summary> public static QsDeclarationAttribute BuildAttribute(QsQualifiedName name, TypedExpression arg) => new QsDeclarationAttribute(BuildId(name), arg ?? SyntaxGenerator.InvalidExpression, Position.Zero, QsComments.Empty);
private (ResolvedSignature, IEnumerable <QsSpecialization>) MakeSpecializations( QsQualifiedName callableName, ResolvedType paramsType, SpecializationImplementation bodyImplementation) { QsSpecialization MakeSpec(QsSpecializationKind kind, ResolvedSignature signature, SpecializationImplementation impl) => new QsSpecialization( kind, callableName, ImmutableArray <QsDeclarationAttribute> .Empty, this.CurrentCallable.Callable.SourceFile, QsNullable <QsLocation> .Null, QsNullable <ImmutableArray <ResolvedType> > .Null, signature, impl, ImmutableArray <string> .Empty, QsComments.Empty); var adj = this.CurrentCallable.Adjoint; var ctl = this.CurrentCallable.Controlled; var ctlAdj = this.CurrentCallable.ControlledAdjoint; bool addAdjoint = false; bool addControlled = false; bool isSelfAdjoint = false; if (this.InWithinBlock) { addAdjoint = true; addControlled = false; } else if (this.InBody) { if (adj != null && adj.Implementation is SpecializationImplementation.Generated adjGen) { addAdjoint = adjGen.Item.IsInvert; isSelfAdjoint = adjGen.Item.IsSelfInverse; } if (ctl != null && ctl.Implementation is SpecializationImplementation.Generated ctlGen) { addControlled = ctlGen.Item.IsDistribute; } if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated ctlAdjGen) { addAdjoint = addAdjoint || (ctlAdjGen.Item.IsInvert && ctl.Implementation.IsGenerated); addControlled = addControlled || (ctlAdjGen.Item.IsDistribute && adj.Implementation.IsGenerated); isSelfAdjoint = isSelfAdjoint || ctlAdjGen.Item.IsSelfInverse; } } else if (ctlAdj != null && ctlAdj.Implementation is SpecializationImplementation.Generated gen) { addControlled = this.InAdjoint && gen.Item.IsDistribute; addAdjoint = this.InControlled && gen.Item.IsInvert; isSelfAdjoint = gen.Item.IsSelfInverse; } var props = new List <OpProperty>(); if (addAdjoint) { props.Add(OpProperty.Adjointable); } if (addControlled) { props.Add(OpProperty.Controllable); } var newSig = new ResolvedSignature( this.CurrentCallable.Callable.Signature.TypeParameters, paramsType, ResolvedType.New(ResolvedTypeKind.UnitType), new CallableInformation(ResolvedCharacteristics.FromProperties(props), new InferredCallableInformation(isSelfAdjoint, false))); var controlledSig = new ResolvedSignature( newSig.TypeParameters, ResolvedType.New(ResolvedTypeKind.NewTupleType(ImmutableArray.Create( ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Qubit))), newSig.ArgumentType))), newSig.ReturnType, newSig.Information); var specializations = new List <QsSpecialization>() { MakeSpec(QsSpecializationKind.QsBody, newSig, bodyImplementation) }; if (addAdjoint) { specializations.Add(MakeSpec( QsSpecializationKind.QsAdjoint, newSig, SpecializationImplementation.NewGenerated(QsGeneratorDirective.Invert))); } if (addControlled) { specializations.Add(MakeSpec( QsSpecializationKind.QsControlled, controlledSig, SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); } if (addAdjoint && addControlled) { specializations.Add(MakeSpec( QsSpecializationKind.QsControlledAdjoint, controlledSig, SpecializationImplementation.NewGenerated(QsGeneratorDirective.Distribute))); } return(newSig, specializations); }
public bool Transformation(QsCompilation compilation, [NotNullWhen(true)] out QsCompilation?transformed) { // we do not change the Q# syntax tree transformed = compilation; // global callables var globalCallables = compilation.Namespaces.GlobalCallableResolutions(); // collect all callables that have an substitution attribute var globals = globalCallables.Where(p => p.Value.Source.CodeFile.EndsWith(".qs")) .Where(p => p.Value.Attributes.Any(HasSubstitutionAttribute)); if (!globals.Any()) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Info, Message = "AutoSubstitution: no operations have @SubstitutableOnTarget attribute", Stage = IRewriteStep.Stage.Transformation }); return(true); } // no need to generate any C# file, if there is no substitution attribute, or if we cannot retrieve the output path if (!AssemblyConstants.TryGetValue(Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants.OutputPath, out var outputPath)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = "AutoSubstitution: cannot determine output path for generated C# code", Stage = IRewriteStep.Stage.Transformation }); return(false); } diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Info, Message = $"AutoSubstitution: Generating file __AutoSubstitution__.g.cs in {outputPath}", Stage = IRewriteStep.Stage.Transformation }); using var writer = new StreamWriter(Path.Combine(outputPath, "__AutoSubstitution__.g.cs")); var context = CodegenContext.Create(compilation, AssemblyConstants); var generator = new CodeGenerator(context); foreach (var(key, callable) in globals) { var attributeArguments = callable.Attributes.Where(HasSubstitutionAttribute).Select(GetSubstitutionAttributeArguments); foreach (var(alternativeOperation, _) in attributeArguments) { var period = alternativeOperation.LastIndexOf('.'); if (period == -1) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: name of alternative operation in {key.Namespace}.{key.Name} must be completely specified (including namespace)", Stage = IRewriteStep.Stage.Transformation }); return(false); } var qualifiedName = new QsQualifiedName(alternativeOperation.Substring(0, period), alternativeOperation.Substring(period + 1)); if (!globalCallables.TryGetValue(qualifiedName, out var alternativeCallable)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: cannot find alternative operation `{alternativeOperation}`", Stage = IRewriteStep.Stage.Transformation }); return(false); } var callableSignature = callable.Signature; var alternativeSignature = alternativeCallable.Signature; if (!callableSignature.ArgumentType.Equals(alternativeSignature.ArgumentType) || !callableSignature.ReturnType.Equals(alternativeSignature.ReturnType)) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: signature of `{alternativeOperation}` does not match the one of {key.Namespace}.{key.Name}", Stage = IRewriteStep.Stage.Transformation }); return(false); } if (!GetSpecializationKinds(callable).IsSubsetOf(GetSpecializationKinds(alternativeCallable))) { diagnostics.Add(new IRewriteStep.Diagnostic { Severity = DiagnosticSeverity.Error, Message = $"AutoSubstitution: specializations of `{alternativeOperation}` must be a superset of specializations of {key.Namespace}.{key.Name}", Stage = IRewriteStep.Stage.Transformation }); return(false); } } generator.AddCallable(key, callable, attributeArguments); } generator.WriteTo(writer); return(true); }
private static Identifier GetConcreteIdentifier( Response currentResponse, Stack <Request> requests, List <Response> responses, Identifier.GlobalCallable globalCallable, ImmutableConcretion types) { QsQualifiedName concreteName = globalCallable.Item; var typesHashSet = ImmutableHashSet <KeyValuePair <Tuple <QsQualifiedName, NonNullable <string> >, ResolvedType> > .Empty; if (types != null && !types.IsEmpty) { typesHashSet = types.ToImmutableHashSet(); } string name = null; // Check for recursive call if (currentResponse.OriginalName.Equals(globalCallable.Item) && typesHashSet.SetEquals(currentResponse.TypeResolutions)) { name = currentResponse.ConcreteCallable.FullName.Name.Value; } // Search requests for identifier if (name == null) { name = requests .Where(req => req.OriginalName.Equals(globalCallable.Item) && typesHashSet.SetEquals(req.TypeResolutions)) .Select(req => req.ConcreteName.Name.Value) .FirstOrDefault(); } // Search responses for identifier if (name == null) { name = responses .Where(res => res.OriginalName.Equals(globalCallable.Item) && typesHashSet.SetEquals(res.TypeResolutions)) .Select(res => res.ConcreteCallable.FullName.Name.Value) .FirstOrDefault(); } // If identifier can't be found, make a new request if (name == null) { // If this is not a generic, do not change the name if (!typesHashSet.IsEmpty) { // Create new name concreteName = UniqueVariableNames.PrependGuid(globalCallable.Item); } requests.Push(new Request() { OriginalName = globalCallable.Item, TypeResolutions = types, ConcreteName = concreteName }); } else { // If the identifier was found, update with the name concreteName = new QsQualifiedName(globalCallable.Item.Namespace, NonNullable <string> .New(name)); } return(Identifier.NewGlobalCallable(concreteName)); }
private static AttributeId BuildId(QsQualifiedName name) => name != null ? AttributeId.NewValue(new UserDefinedType(name.Namespace, name.Name, QsNullable <Range> .Null)) : AttributeId.Null;