diff --git a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs index 9555e36865..7241583aa6 100644 --- a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs +++ b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs @@ -298,10 +298,46 @@ type CallGraphTests(output: ITestOutputHelper) = AssertNotInGraph graph "Bar" + [] + [] + member this.``Multiple Entry Points``() = + let graph = PopulateCallGraphWithExe 9 |> BuildTrimmedGraph + + [ "Main1", [ "Foo" ]; "Main2", [ "Bar" ]; "Foo", []; "Bar", [] ] + |> List.map (fun x -> AssertExpectedDirectDependencies (fst x) (snd x) graph) + |> ignore + + [] + [] + member this.``Unit Test as Starting Point``() = + let graph = PopulateCallGraphWithExe 10 |> BuildTrimmedGraph + + [ "Test", [ "Foo" ]; "Foo", [] ] + |> List.map (fun x -> AssertExpectedDirectDependencies (fst x) (snd x) graph) + |> ignore + + AssertNotInGraph graph "Bar" + + [] + [] + member this.``Entry Points and Unit Tests``() = + let graph = PopulateCallGraphWithExe 11 |> BuildTrimmedGraph + + [ + "Main1", [ "Foo" ] + "Main2", [ "Bar" ] + "Test1", [ "Zip" ] + "Test2", [ "Zap" ] + ] + |> List.map (fun x -> AssertExpectedDirectDependencies (fst x) (snd x) graph) + |> ignore + + AssertNotInGraph graph "Unused" + [] [] member this.``Concrete Graph has Concretizations``() = - let graph = PopulateCallGraphWithExe 9 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 12 |> ConcreteCallGraph let makeNode name resType = MakeNode name QsSpecializationKind.QsBody [ ("A", resType) ] @@ -325,7 +361,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Contains All Specializations``() = - let graph = PopulateCallGraphWithExe 10 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 13 |> ConcreteCallGraph let makeNode name spec = MakeNode name spec [] @@ -348,7 +384,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Double Reference Resolution``() = - let graph = PopulateCallGraphWithExe 11 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 14 |> ConcreteCallGraph let makeNode resType = MakeNode "Foo" QsSpecializationKind.QsBody [ ("A", resType) ] @@ -362,7 +398,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Non-Call Reference Only Body``() = - let graph = PopulateCallGraphWithExe 12 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 15 |> ConcreteCallGraph let makeNode spec = MakeNode "Foo" spec [] @@ -380,7 +416,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Non-Call Reference With Adjoint``() = - let graph = PopulateCallGraphWithExe 13 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 16 |> ConcreteCallGraph let makeNode spec = MakeNode "Foo" spec [] @@ -398,7 +434,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Non-Call Reference With All``() = - let graph = PopulateCallGraphWithExe 14 |> ConcreteCallGraph + let graph = PopulateCallGraphWithExe 17 |> ConcreteCallGraph let makeNode spec = MakeNode "Foo" spec [] @@ -415,7 +451,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Call Self-Adjoint Reference``() = - let compilation = PopulateCallGraphWithExe 15 + let compilation = PopulateCallGraphWithExe 18 let mutable transformed = { Namespaces = ImmutableArray.Empty; EntryPoints = ImmutableArray.Empty } Assert.True(CodeGeneration.GenerateFunctorSpecializations(compilation, &transformed)) let graph = transformed |> ConcreteCallGraph @@ -446,7 +482,7 @@ type CallGraphTests(output: ITestOutputHelper) = [] [] member this.``Concrete Graph Clears Type Param Resolutions After Statements``() = - let compilation = PopulateCallGraphWithExe 16 + let compilation = PopulateCallGraphWithExe 19 let mutable transformed = { Namespaces = ImmutableArray.Empty; EntryPoints = ImmutableArray.Empty } Assert.True(CodeGeneration.GenerateFunctorSpecializations(compilation, &transformed)) let graph = transformed |> ConcreteCallGraph @@ -461,6 +497,90 @@ type CallGraphTests(output: ITestOutputHelper) = Assert.Empty unresolvedTypeParameters + [] + [] + member this.``Concrete Graph Multiple Entry Points``() = + let graph = PopulateCallGraphWithExe 20 |> ConcreteCallGraph + + let makeNode name resType = + MakeNode name QsSpecializationKind.QsBody [ ("A", resType) ] + + let makeNodeNoRes name = + MakeNode name QsSpecializationKind.QsBody [] + + let Main1 = makeNodeNoRes "Main1" + let Main2 = makeNodeNoRes "Main2" + let FooDouble = makeNode "Foo" Double + let FooString = makeNode "Foo" String + let FooEmpty = makeNodeNoRes "Foo" + + AssertInConcreteGraph graph Main1 + AssertInConcreteGraph graph Main2 + AssertInConcreteGraph graph FooDouble + AssertInConcreteGraph graph FooString + + AssertNotInConcreteGraph graph FooEmpty + + [] + [] + member this.``Concrete Graph Unit Tests``() = + let graph = PopulateCallGraphWithExe 21 |> ConcreteCallGraph + + let makeNode name resType = + MakeNode name QsSpecializationKind.QsBody [ ("A", resType) ] + + let makeNodeNoRes name = + MakeNode name QsSpecializationKind.QsBody [] + + let Test1 = makeNodeNoRes "Test1" + let Test2 = makeNodeNoRes "Test2" + let FooDouble = makeNode "Foo" Double + let FooString = makeNode "Foo" String + let FooEmpty = makeNodeNoRes "Foo" + + AssertInConcreteGraph graph Test1 + AssertInConcreteGraph graph Test2 + AssertInConcreteGraph graph FooDouble + AssertInConcreteGraph graph FooString + + AssertNotInConcreteGraph graph FooEmpty + + [] + [] + member this.``Concrete Graph Entry Points and Unit Tests``() = + let graph = PopulateCallGraphWithExe 22 |> ConcreteCallGraph + + let makeNode name resType = + MakeNode name QsSpecializationKind.QsBody [ ("A", resType) ] + + let makeNodeNoRes name = + MakeNode name QsSpecializationKind.QsBody [] + + let Main1 = makeNodeNoRes "Main1" + let Main2 = makeNodeNoRes "Main2" + let Test1 = makeNodeNoRes "Test1" + let Test2 = makeNodeNoRes "Test2" + let FooDouble = makeNode "Foo" Double + let FooString = makeNode "Foo" String + let FooEmpty = makeNodeNoRes "Foo" + let BarDouble = makeNode "Bar" Double + let BarString = makeNode "Bar" String + let BarEmpty = makeNodeNoRes "Bar" + let Unused = makeNodeNoRes "Unused" + + AssertInConcreteGraph graph Main1 + AssertInConcreteGraph graph Main2 + AssertInConcreteGraph graph Test1 + AssertInConcreteGraph graph Test2 + AssertInConcreteGraph graph FooDouble + AssertInConcreteGraph graph FooString + AssertInConcreteGraph graph BarDouble + AssertInConcreteGraph graph BarString + + AssertNotInConcreteGraph graph FooEmpty + AssertNotInConcreteGraph graph BarEmpty + AssertNotInConcreteGraph graph Unused + [] [] member this.``No Cycles``() = diff --git a/src/QsCompiler/Tests.Compiler/TestCases/PopulateCallGraph.qs b/src/QsCompiler/Tests.Compiler/TestCases/PopulateCallGraph.qs index ae377b0258..bd7a0c9529 100644 --- a/src/QsCompiler/Tests.Compiler/TestCases/PopulateCallGraph.qs +++ b/src/QsCompiler/Tests.Compiler/TestCases/PopulateCallGraph.qs @@ -134,6 +134,81 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // ================================= +// Multiple Entry Points +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + @EntryPoint() + operation Main1() : Unit { + Foo(); + } + + @EntryPoint() + operation Main2() : Unit { + Bar(); + } + + operation Foo() : Unit { } + + operation Bar() : Unit { } +} + +// ================================= + +// Unit Test as Starting Point +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + open Microsoft.Quantum.Diagnostics; + + @Test("QuantumSimulator") + operation Test() : Unit { + Foo(); + } + + operation Foo() : Unit { } + + operation Bar() : Unit { } +} + +// ================================= + +// Entry Points and Unit Tests +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + open Microsoft.Quantum.Diagnostics; + + @EntryPoint() + operation Main1() : Unit { + Foo(); + } + + @EntryPoint() + operation Main2() : Unit { + Bar(); + } + + @Test("QuantumSimulator") + operation Test1() : Unit { + Zip(); + } + + @Test("QuantumSimulator") + operation Test2() : Unit { + Zap(); + } + + operation Foo() : Unit { } + + operation Bar() : Unit { } + + operation Zip() : Unit { } + + operation Zap() : Unit { } + + operation Unused() : Unit { } +} + +// ================================= + // Concrete Graph has Concretizations namespace Microsoft.Quantum.Testing.PopulateCallGraph { @@ -212,6 +287,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Double Reference Resolution namespace Microsoft.Quantum.Testing.PopulateCallGraph { + function Foo<'A>(x : 'A) : 'A { return x; } @@ -226,6 +302,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Non-Call Reference Only Body namespace Microsoft.Quantum.Testing.PopulateCallGraph { + operation Foo() : Unit { } @EntryPoint() @@ -238,6 +315,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Non-Call Reference With Adjoint namespace Microsoft.Quantum.Testing.PopulateCallGraph { + operation Foo() : Unit is Adj { body(...) { } adjoint(...) { } @@ -253,6 +331,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Non-Call Reference With All namespace Microsoft.Quantum.Testing.PopulateCallGraph { + operation Foo() : Unit is Ctl+Adj { body(...) { } controlled(ctl, ...) { } @@ -270,6 +349,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Call Self-Adjoint Reference namespace Microsoft.Quantum.Testing.PopulateCallGraph { + operation Foo() : Unit is Ctl+Adj { body(...) { } controlled distribute; @@ -290,6 +370,7 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { // Concrete Graph Clears Type Param Resolutions After Statements namespace Microsoft.Quantum.Testing.PopulateCallGraph { + @EntryPoint() operation Main () : Unit { using (qs = Qubit[1]) { @@ -307,3 +388,75 @@ namespace Microsoft.Quantum.Testing.PopulateCallGraph { Bar(Foo, x); } } + +// ================================= + +// Concrete Graph Multiple Entry Points +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + @EntryPoint() + operation Main1() : Unit { + Foo(); + } + + @EntryPoint() + operation Main2() : Unit { + Foo(); + } + + operation Foo<'A>() : Unit { } +} + +// ================================= + +// Concrete Graph Unit Tests +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + open Microsoft.Quantum.Diagnostics; + + @Test("QuantumSimulator") + operation Test1() : Unit { + Foo(); + } + + @Test("QuantumSimulator") + operation Test2() : Unit { + Foo(); + } + + operation Foo<'A>() : Unit { } +} + +// ================================= + +// Concrete Graph Entry Points and Unit Tests +namespace Microsoft.Quantum.Testing.PopulateCallGraph { + + open Microsoft.Quantum.Diagnostics; + + @EntryPoint() + operation Main1() : Unit { + Foo(); + } + + @EntryPoint() + operation Main2() : Unit { + Foo(); + } + + @Test("QuantumSimulator") + operation Test1() : Unit { + Bar(); + } + + @Test("QuantumSimulator") + operation Test2() : Unit { + Bar(); + } + + operation Foo<'A>() : Unit { } + + operation Bar<'A>() : Unit { } + + operation Unused() : Unit { } +} diff --git a/src/QsCompiler/Transformations/CallGraph/CallGraphWalker.cs b/src/QsCompiler/Transformations/CallGraph/CallGraphWalker.cs index ca59f0efd1..fcb8ad2112 100644 --- a/src/QsCompiler/Transformations/CallGraph/CallGraphWalker.cs +++ b/src/QsCompiler/Transformations/CallGraph/CallGraphWalker.cs @@ -66,9 +66,11 @@ public static void PopulateGraph(GraphBuilder graph, QsCompilation compilation) /// public static void PopulateTrimmedGraph(GraphBuilder graph, QsCompilation compilation) { + var globals = compilation.Namespaces.GlobalCallableResolutions(); var walker = new BuildGraph(graph); - var entryPointNodes = compilation.EntryPoints.Select(name => new CallGraphNode(name)); walker.SharedState.WithTrimming = true; + + var entryPointNodes = compilation.EntryPoints.Select(name => new CallGraphNode(name)); foreach (var entryPoint in entryPointNodes) { // Make sure all the entry points are added to the graph @@ -76,7 +78,16 @@ public static void PopulateTrimmedGraph(GraphBuilder graph, QsCompilation compil walker.SharedState.RequestStack.Push(entryPoint); } - var globals = compilation.Namespaces.GlobalCallableResolutions(); + var unitTests = globals + .Where(kvp => kvp.Value.Attributes.Any(BuiltIn.MarksTestOperation)) + .Select(kvp => new CallGraphNode(kvp.Key)); + foreach (var unitTest in unitTests) + { + // Make sure all the unit tests are added to the graph + walker.SharedState.Graph.AddNode(unitTest); + walker.SharedState.RequestStack.Push(unitTest); + } + while (walker.SharedState.RequestStack.TryPop(out var currentRequest)) { // If there is a call to an unknown callable, throw exception diff --git a/src/QsCompiler/Transformations/CallGraph/ConcreteCallGraphWalker.cs b/src/QsCompiler/Transformations/CallGraph/ConcreteCallGraphWalker.cs index eba02247ab..7b7d854d05 100644 --- a/src/QsCompiler/Transformations/CallGraph/ConcreteCallGraphWalker.cs +++ b/src/QsCompiler/Transformations/CallGraph/ConcreteCallGraphWalker.cs @@ -41,6 +41,7 @@ public static void PopulateConcreteGraph(ConcreteGraphBuilder graph, QsCompilati { var globals = compilation.Namespaces.GlobalCallableResolutions(); var walker = new BuildGraph(graph); + var entryPointNodes = compilation.EntryPoints.SelectMany(name => GetSpecializationKinds(globals, name).Select(kind => new ConcreteCallGraphNode(name, kind, TypeParameterResolutions.Empty))); @@ -51,6 +52,16 @@ public static void PopulateConcreteGraph(ConcreteGraphBuilder graph, QsCompilati walker.SharedState.RequestStack.Push(entryPoint); } + var unitTests = globals + .Where(kvp => kvp.Value.Attributes.Any(BuiltIn.MarksTestOperation)) + .SelectMany(kvp => kvp.Value.Specializations.Select(spec => new ConcreteCallGraphNode(kvp.Key, spec.Kind, TypeParameterResolutions.Empty))); + foreach (var unitTest in unitTests) + { + // Make sure all the unit tests are added to the graph + walker.SharedState.Graph.AddNode(unitTest); + walker.SharedState.RequestStack.Push(unitTest); + } + while (walker.SharedState.RequestStack.TryPop(out var currentRequest)) { // If there is a call to an unknown callable, throw exception