/// <summary> /// Given the argument tuple of a specialization, returns the argument tuple for its controlled version. /// Returns null if the given argument tuple is null. /// </summary> private static QsTuple <LocalVariableDeclaration <QsLocalSymbol> > ControlledArg(QsTuple <LocalVariableDeclaration <QsLocalSymbol> > arg) => arg != null ? SyntaxGenerator.WithControlQubits(arg, QsNullable <Tuple <int, int> > .Null, QsLocalSymbol.NewValidName(NonNullable <string> .New(InternalUse.ControlQubitsName)), QsNullable <Tuple <QsPositionInfo, QsPositionInfo> > .Null) : null;
static ArgDeclType BuildArgument(string name, ResolvedType t) { var validName = QsLocalSymbol.NewValidName(NonNullable <string> .New(name)); var info = new InferredExpressionInformation(false, false); return(new ArgDeclType(validName, t, info, QsNullable <Tuple <int, int> > .Null, EmptyRange)); }
static ArgDeclType BuildArgument(string name, ResolvedType t) { var validName = QsLocalSymbol.NewValidName(name); var info = new InferredExpressionInformation(false, false); return(new ArgDeclType(validName, t, info, QsNullable <Position> .Null, Range.Zero)); }
/// <summary> /// Given the argument tuple of a specialization, returns the argument tuple for its controlled version. /// Returns null if the given argument tuple is null. /// </summary> private static QsTuple <LocalVariableDeclaration <QsLocalSymbol> >?ControlledArg(QsTuple <LocalVariableDeclaration <QsLocalSymbol> > arg) => arg != null ? SyntaxGenerator.WithControlQubits( arg, QsNullable <Position> .Null, QsLocalSymbol.NewValidName(InternalUse.ControlQubitsName), QsNullable <Range> .Null) : null;
// private methods private static ArgumentTuple BuildSpecArgTuple(ArgumentTuple callableArg, QsSpecializationKind specKind) => specKind.IsQsControlled || specKind.IsQsControlledAdjoint ? SyntaxGenerator.WithControlQubits( callableArg, QsNullable <Position> .Null, QsLocalSymbol.NewValidName(InternalUse.ControlQubitsName), QsNullable <DataTypes.Range> .Null) : callableArg;
private (QsCallable, ResolvedType) GenerateOperation(CallableDetails callable, QsScope contents) { var newName = UniqueVariableNames.PrependGuid(callable.Callable.FullName); var knownVariables = contents.KnownSymbols.Variables; var parameters = QsTuple<LocalVariableDeclaration<QsLocalSymbol>>.NewQsTuple(knownVariables .Select(var => QsTuple<LocalVariableDeclaration<QsLocalSymbol>>.NewQsTupleItem(new LocalVariableDeclaration<QsLocalSymbol>( QsLocalSymbol.NewValidName(var.VariableName), var.Type, new InferredExpressionInformation(false, false), var.Position, var.Range))) .ToImmutableArray()); var paramTypes = ResolvedType.New(ResolvedTypeKind.UnitType); if (knownVariables.Length == 1) { paramTypes = knownVariables.First().Type; } else if (knownVariables.Length > 1) { paramTypes = ResolvedType.New(ResolvedTypeKind.NewTupleType(knownVariables .Select(var => var.Type) .ToImmutableArray())); } var (signature, specializations) = this.MakeSpecializations(callable, newName, paramTypes, SpecializationImplementation.NewProvided(parameters, contents)); var generatedCallable = new QsCallable( QsCallableKind.Operation, newName, ImmutableArray<QsDeclarationAttribute>.Empty, new Modifiers(AccessModifier.Internal), callable.Callable.SourceFile, QsNullable<QsLocation>.Null, signature, parameters, specializations.ToImmutableArray(), ImmutableArray<string>.Empty, QsComments.Empty); // Change the origin of all type parameter references to use the new name and make all variables immutable generatedCallable = UpdateGeneratedOp.Apply(generatedCallable, knownVariables, callable.Callable.FullName, newName); return (generatedCallable, signature.ArgumentType); }
public void ParseOp() { ArgDeclType BuildArgument(string name, ResolvedType t) { var validName = QsLocalSymbol.NewValidName(NonNullable <string> .New(name)); var info = new InferredExpressionInformation(false, false); return(new ArgDeclType(validName, t, info, QsNullable <Tuple <int, int> > .Null, EmptyRange)); } string[] comments = { "# Summary", "Convenience function that performs state preparation by applying a ", "`statePrepUnitary` on the input state, followed by adiabatic state ", "preparation using a `adiabaticUnitary`, and finally phase estimation ", "with respect to `qpeUnitary`on the resulting state using a ", "`phaseEstAlgorithm`.", "", "# Input", "## statePrepUnitary", "An oracle representing state preparation for the initial dynamical", "generator.", "## adiabaticUnitary", "An oracle representing the adiabatic evolution algorithm to be used", "to implement the sweeps to the final state of the algorithm.", "## qpeUnitary", "An oracle representing a unitary operator $U$ representing evolution", "for time $\\delta t$ under a dynamical generator with ground state", "$\\ket{\\phi}$ and ground state energy $E = \\phi\\\\,\\delta t$.", "## phaseEstAlgorithm", "An operation that performs phase estimation on a given unitary operation.", "See [iterative phase estimation](/quantum/libraries/characterization#iterative-phase-estimation)", "for more details.", "## qubits", "A register of qubits to be used to perform the simulation.", "", "# Output", "An estimate $\\hat{\\phi}$ of the ground state energy $\\phi$", "of the generator represented by $U$." }; string expected = @"### YamlMime:QSharpType uid: microsoft.quantum.canon.adiabaticstateenergyunitary name: AdiabaticStateEnergyUnitary type: operation namespace: Microsoft.Quantum.Canon summary: |- Convenience function that performs state preparation by applying a `statePrepUnitary` on the input state, followed by adiabatic state preparation using a `adiabaticUnitary`, and finally phase estimation with respect to `qpeUnitary`on the resulting state using a `phaseEstAlgorithm`. syntax: 'operation AdiabaticStateEnergyUnitary (statePrepUnitary : (Qubit[] => Unit), adiabaticUnitary : (Qubit[] => Unit), qpeUnitary : (Qubit[] => Unit is Adj + Ctl), phaseEstAlgorithm : ((Microsoft.Quantum.Canon.DiscreteOracle, Qubit[]) => Double), qubits : Qubit[]) : Double' input: content: '(statePrepUnitary : (Qubit[] => Unit), adiabaticUnitary : (Qubit[] => Unit), qpeUnitary : (Qubit[] => Unit is Adj + Ctl), phaseEstAlgorithm : ((Microsoft.Quantum.Canon.DiscreteOracle, Qubit[]) => Double), qubits : Qubit[])' types: - name: statePrepUnitary summary: |- An oracle representing state preparation for the initial dynamical generator. isOperation: true input: types: - isArray: true isPrimitive: true uid: Qubit output: types: - isPrimitive: true uid: Unit - name: adiabaticUnitary summary: |- An oracle representing the adiabatic evolution algorithm to be used to implement the sweeps to the final state of the algorithm. isOperation: true input: types: - isArray: true isPrimitive: true uid: Qubit output: types: - isPrimitive: true uid: Unit - name: qpeUnitary summary: |- An oracle representing a unitary operator $U$ representing evolution for time $\delta t$ under a dynamical generator with ground state $\ket{\phi}$ and ground state energy $E = \phi\\,\delta t$. isOperation: true input: types: - isArray: true isPrimitive: true uid: Qubit output: types: - isPrimitive: true uid: Unit functors: - Adjoint - Controlled - name: phaseEstAlgorithm summary: |- An operation that performs phase estimation on a given unitary operation. See [iterative phase estimation](/quantum/libraries/characterization#iterative-phase-estimation) for more details. isOperation: true input: types: - uid: microsoft.quantum.canon.discreteoracle - isArray: true isPrimitive: true uid: Qubit output: types: - isPrimitive: true uid: Double - name: qubits summary: A register of qubits to be used to perform the simulation. isArray: true isPrimitive: true uid: Qubit output: content: Double types: - summary: |- An estimate $\hat{\phi}$ of the ground state energy $\phi$ of the generator represented by $U$. isPrimitive: true uid: Double ... "; var qubitArrayType = ResolvedType.New(QsType.NewArrayType(ResolvedType.New(QsType.Qubit))); var unitType = ResolvedType.New(QsType.UnitType); var doubleType = ResolvedType.New(QsType.Double); var oracleType = ResolvedType.New(QsType.NewUserDefinedType(new UserDefinedType(CanonName, NonNullable <string> .New("DiscreteOracle"), QsNullable <Tuple <QsPositionInfo, QsPositionInfo> > .Null))); var noInfo = CallableInformation.NoInformation; var acFunctors = ResolvedCharacteristics.FromProperties(new[] { OpProperty.Adjointable, OpProperty.Controllable }); var acInfo = new CallableInformation(acFunctors, InferredCallableInformation.NoInformation); var qubitToUnitOp = ResolvedType.New(QsType.NewOperation(new SigTypeTuple(qubitArrayType, unitType), noInfo)); var qubitToUnitOpAC = ResolvedType.New(QsType.NewOperation(new SigTypeTuple(qubitArrayType, unitType), acInfo)); var phaseEstArgs = new ResolvedType[] { oracleType, qubitArrayType }.ToImmutableArray(); var phaseEstArgTuple = ResolvedType.New(QsType.NewTupleType(phaseEstArgs)); var phaseEstOp = ResolvedType.New(QsType.NewOperation(new SigTypeTuple(phaseEstArgTuple, doubleType), noInfo)); var typeParams = new QsLocalSymbol[] { }.ToImmutableArray(); var argTypes = new ResolvedType[] { qubitToUnitOp, qubitToUnitOp, qubitToUnitOpAC, phaseEstOp, qubitArrayType }.ToImmutableArray(); var argTupleType = ResolvedType.New(QsType.NewTupleType(argTypes)); var signature = new ResolvedSignature(typeParams, argTupleType, doubleType, noInfo); var args = new List <ArgDeclType> { BuildArgument("statePrepUnitary", qubitToUnitOp), BuildArgument("adiabaticUnitary", qubitToUnitOp), BuildArgument("qpeUnitary", qubitToUnitOpAC), BuildArgument("phaseEstAlgorithm", phaseEstOp), BuildArgument("qubits", qubitArrayType) } .ConvertAll(arg => QsTuple <ArgDeclType> .NewQsTupleItem(arg)) .ToImmutableArray(); var argTuple = QsTuple <ArgDeclType> .NewQsTuple(args); var specs = new QsSpecialization[] { }.ToImmutableArray(); var qsCallable = new QsCallable(QsCallableKind.Operation, MakeFullName("AdiabaticStateEnergyUnitary"), ImmutableArray <QsDeclarationAttribute> .Empty, NonNullable <string> .New("Techniques.qs"), ZeroLocation, signature, argTuple, specs, comments.ToImmutableArray(), QsComments.Empty); var callable = new DocCallable("Microsoft.Quantum.Canon", qsCallable); var stream = new StringWriter(); callable.WriteToFile(stream); var s = stream.ToString(); Assert.Equal(expected, s); }
/// <summary> /// Returns the signature help information for a call expression if there is such an expression at the specified position. /// Returns null if some parameters are unspecified (null), /// or if the specified position is not a valid position within the currently processed file content, /// or if no call expression exists at the specified position at this time, /// or if no signature help information can be provided for the call expression at the specified position. /// </summary> public static SignatureHelp?SignatureHelp( this FileContentManager file, CompilationUnit compilation, Position?position, MarkupKind format = MarkupKind.PlainText) { // getting the relevant token (if any) var fragment = file?.TryGetFragmentAt(position, out var _, includeEnd: true); if (file is null || position is null || fragment?.Kind == null || compilation == null) { return(null); } var fragmentStart = fragment.Range.Start; // getting the overlapping call expressions (if any), and determine the header of the called callable bool OverlapsWithPosition(Range symRange) => (fragmentStart + symRange).ContainsEnd(position); var overlappingEx = fragment.Kind.CallExpressions().Where(ex => ex.Range.IsValue && OverlapsWithPosition(ex.Range.Item)).ToList(); if (!overlappingEx.Any()) { return(null); } overlappingEx.Sort((ex1, ex2) => // for nested call expressions, the last expressions (by range) is always the closest one { var(x, y) = (ex1.Range.Item, ex2.Range.Item); int result = x.Start.CompareTo(y.Start); return(result == 0 ? x.End.CompareTo(y.End) : result); }); var nsName = file.TryGetNamespaceAt(position); var(method, args) = overlappingEx.Last().Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .CallLikeExpression c ? (c.Item1, c.Item2) : (null, null); if (nsName == null || method == null || args == null) { return(null); } // getting the called identifier as well as what functors have been applied to it List <QsFunctor> FunctorApplications(ref QsExpression ex) { var(next, inner) = ex.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .AdjointApplication adj ? (QsFunctor.Adjoint, adj.Item) : ex.Expression is QsExpressionKind <QsExpression, QsSymbol, QsType> .ControlledApplication ctl ? (QsFunctor.Controlled, ctl.Item) : (null, null); var fs = inner == null ? new List <QsFunctor>() : FunctorApplications(ref inner); if (next != null) { fs.Add(next); } ex = inner ?? ex; return(fs); } var functors = FunctorApplications(ref method); var id = method.Expression as QsExpressionKind <QsExpression, QsSymbol, QsType> .Identifier; if (id == null) { return(null); } // extracting and adapting the relevant information for the called callable ResolutionResult <CallableDeclarationHeader> .Found?methodDecl = null; if (id.Item1.Symbol is QsSymbolKind <QsSymbol> .Symbol sym) { methodDecl = compilation.GlobalSymbols.TryResolveAndGetCallable( sym.Item, nsName, file.FileName) as ResolutionResult <CallableDeclarationHeader> .Found; } else if (id.Item1.Symbol is QsSymbolKind <QsSymbol> .QualifiedSymbol qualSym) { methodDecl = compilation.GlobalSymbols.TryGetCallable( new QsQualifiedName(qualSym.Item1, qualSym.Item2), nsName, file.FileName) as ResolutionResult <CallableDeclarationHeader> .Found; } if (methodDecl == null) { return(null); } var(documentation, argTuple) = (methodDecl.Item.Documentation, methodDecl.Item.ArgumentTuple); var nrCtlApplications = functors.Where(f => f.Equals(QsFunctor.Controlled)).Count(); while (nrCtlApplications-- > 0) { var ctlQsName = QsLocalSymbol.NewValidName(nrCtlApplications == 0 ? "cs" : $"cs{nrCtlApplications}"); argTuple = SyntaxGenerator.WithControlQubits(argTuple, QsNullable <Position> .Null, ctlQsName, QsNullable <Range> .Null); } // now that we now what callable is called we need to check which argument should come next bool BeforePosition(Range symRange) => fragmentStart + symRange.End < position; IEnumerable <(Range?, string?)> ExtractParameterRanges( QsExpression?ex, QsTuple <LocalVariableDeclaration <QsLocalSymbol> > decl) { var @null = ((Range?)null, (string?)null); IEnumerable <(Range?, string?)> SingleItem(string paramName) { var arg = ex?.Range == null ? ((Range?)null, paramName) : ex.Range.IsValue ? (ex.Range.Item, paramName) : @null; // no signature help if there are invalid expressions return(new[] { arg }); } if (decl is QsTuple <LocalVariableDeclaration <QsLocalSymbol> > .QsTupleItem dItem) { return(SingleItem(dItem.Item.VariableName is QsLocalSymbol.ValidName n ? n.Item : "__argName__")); } var declItems = decl as QsTuple <LocalVariableDeclaration <QsLocalSymbol> > .QsTuple; var exItems = ex?.Expression as QsExpressionKind <QsExpression, QsSymbol, QsType> .ValueTuple; if (declItems == null) { return(new[] { @null }); } if (exItems == null && declItems.Item.Length > 1) { return(SingleItem(decl.PrintArgumentTuple())); } var argItems = exItems != null ? exItems.Item.ToImmutableArray <QsExpression?>() : ex == null ? ImmutableArray <QsExpression?> .Empty : ImmutableArray.Create <QsExpression?>(ex); return(argItems.AddRange(Enumerable.Repeat <QsExpression?>(null, declItems.Item.Length - argItems.Length)) .Zip(declItems.Item, (e, d) => (e, d)) .SelectMany(arg => ExtractParameterRanges(arg.Item1, arg.Item2))); } var callArgs = ExtractParameterRanges(args, argTuple).ToArray(); if (id == null || callArgs == null || callArgs.Any(item => item.Item2 == null)) { return(null); // no signature help if there are invalid expressions } // finally we can build the signature help information MarkupContent AsMarkupContent(string str) => new MarkupContent { Kind = format, Value = str }; ParameterInformation AsParameterInfo(string?paramName) => new ParameterInformation { Label = paramName, Documentation = AsMarkupContent(documentation.ParameterDescription(paramName)) }; var signatureLabel = $"{methodDecl.Item.QualifiedName.Name} {argTuple.PrintArgumentTuple()}"; foreach (var f in functors) { if (f.IsAdjoint) { signatureLabel = $"{Keywords.qsAdjointFunctor.id} {signatureLabel}"; } if (f.IsControlled) { signatureLabel = $"{Keywords.qsControlledFunctor.id} {signatureLabel}"; } } var doc = documentation.PrintSummary(format == MarkupKind.Markdown).Trim(); var info = new SignatureInformation { Documentation = AsMarkupContent(doc), Label = signatureLabel, // Note: the label needs to be expressed in a way that the active parameter is detectable Parameters = callArgs.Select(d => d.Item2).Select(AsParameterInfo).ToArray() }; var precedingArgs = callArgs .TakeWhile(item => item.Item1 == null || BeforePosition(item.Item1)) // skip args that have already been typed or - in the case of inner items - are missing .Reverse().SkipWhile(item => item.Item1 == null); // don't count missing, i.e. not yet typed items, of the relevant inner argument tuple return(new SignatureHelp { Signatures = new[] { info }, // since we don't support overloading there is just one signature here ActiveSignature = 0, ActiveParameter = precedingArgs.Count() }); }