diff --git a/src/AzureClient/AzureClient.csproj b/src/AzureClient/AzureClient.csproj index 6aaa5956f9..07e7b79254 100644 --- a/src/AzureClient/AzureClient.csproj +++ b/src/AzureClient/AzureClient.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/AzureClient/AzureExecutionTarget.cs b/src/AzureClient/AzureExecutionTarget.cs index e11173aa90..5d75edc247 100644 --- a/src/AzureClient/AzureExecutionTarget.cs +++ b/src/AzureClient/AzureExecutionTarget.cs @@ -10,7 +10,16 @@ namespace Microsoft.Quantum.IQSharp.AzureClient { - internal enum AzureProvider { IonQ, Honeywell, QCI, Mock } + internal enum AzureProvider + { + IonQ, + Quantinuum, + // NB: This provider name is deprecated, but may exist in older + // workspaces and should still be supported. + Honeywell, + QCI, + Mock + } internal class AzureExecutionTarget { @@ -21,14 +30,23 @@ protected AzureExecutionTarget(string? targetId) public string? TargetId { get; } - public virtual string PackageName => $"Microsoft.Quantum.Providers.{GetProvider(TargetId)}"; + public virtual string PackageName => GetProvider(TargetId) switch + { + + AzureProvider.IonQ => "Microsoft.Quantum.Providers.IonQ", + AzureProvider.Quantinuum => "Microsoft.Quantum.Providers.Honeywell", + AzureProvider.Honeywell => "Microsoft.Quantum.Providers.Honeywell", + AzureProvider.QCI => "Microsoft.Quantum.Providers.QCI", + _ => $"Microsoft.Quantum.Providers.{GetProvider(TargetId)}" + }; public RuntimeCapability RuntimeCapability => GetProvider(TargetId) switch { - AzureProvider.IonQ => RuntimeCapability.BasicQuantumFunctionality, - AzureProvider.Honeywell => RuntimeCapability.BasicMeasurementFeedback, - AzureProvider.QCI => RuntimeCapability.BasicMeasurementFeedback, - _ => RuntimeCapability.FullComputation + AzureProvider.IonQ => RuntimeCapability.BasicQuantumFunctionality, + AzureProvider.Quantinuum => RuntimeCapability.BasicMeasurementFeedback, + AzureProvider.Honeywell => RuntimeCapability.BasicMeasurementFeedback, + AzureProvider.QCI => RuntimeCapability.BasicMeasurementFeedback, + _ => RuntimeCapability.FullComputation }; /// @@ -50,7 +68,13 @@ protected AzureExecutionTarget(string? targetId) /// /// It creates the AzureExecutionTarget instance for the given targetId. /// - public static AzureExecutionTarget? Create(string? targetId) => GetProvider(targetId) is null + /// + /// An instance of if + /// describes a target for a valid + /// provider, and null otherwise. + /// + public static AzureExecutionTarget? Create(string? targetId) => + GetProvider(targetId) is null ? null : new AzureExecutionTarget(targetId); @@ -59,10 +83,14 @@ protected AzureExecutionTarget(string? targetId) /// Gets the Azure Quantum provider corresponding to the given execution target. /// /// The Azure Quantum execution target ID. - /// The enum value representing the provider. + /// + /// The enum value representing the + /// provider, or null if does + /// not describe a valid provider. + /// /// /// Valid target IDs are structured as "provider.target". - /// For example, "ionq.simulator" or "honeywell.qpu". + /// For example, "ionq.simulator" or "quantinuum.qpu". /// protected static AzureProvider? GetProvider(string? targetId) { diff --git a/src/AzureClient/Mocks/MockTargetStatus.cs b/src/AzureClient/Mocks/MockTargetStatus.cs index 76e8e0f9fe..5b62a92227 100644 --- a/src/AzureClient/Mocks/MockTargetStatus.cs +++ b/src/AzureClient/Mocks/MockTargetStatus.cs @@ -15,5 +15,7 @@ public MockTargetStatus(string id) : base() } public override string TargetId { get; } + + public override string ToString() => $"MockTargetStatus {{ TargetId = \"{TargetId}\" }}"; } } \ No newline at end of file diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 66f28ae9ea..28701bbff9 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -38,10 +38,10 @@ - - - - + + + + diff --git a/src/ExecutionPathTracer/ExecutionPathTracer.csproj b/src/ExecutionPathTracer/ExecutionPathTracer.csproj index 64cd72a115..06bc540a4a 100644 --- a/src/ExecutionPathTracer/ExecutionPathTracer.csproj +++ b/src/ExecutionPathTracer/ExecutionPathTracer.csproj @@ -32,7 +32,7 @@ - + diff --git a/src/MockLibraries/Mock.Chemistry/Mock.Chemistry.csproj b/src/MockLibraries/Mock.Chemistry/Mock.Chemistry.csproj index a94edb5864..b168660bbd 100644 --- a/src/MockLibraries/Mock.Chemistry/Mock.Chemistry.csproj +++ b/src/MockLibraries/Mock.Chemistry/Mock.Chemistry.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -6,6 +6,6 @@ - + diff --git a/src/MockLibraries/Mock.Standard/Mock.Standard.csproj b/src/MockLibraries/Mock.Standard/Mock.Standard.csproj index a94edb5864..b168660bbd 100644 --- a/src/MockLibraries/Mock.Standard/Mock.Standard.csproj +++ b/src/MockLibraries/Mock.Standard/Mock.Standard.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -6,6 +6,6 @@ - + diff --git a/src/Tests/AzureClientTests.cs b/src/Tests/AzureClientTests.cs index 88ce846ae3..ed87b3e838 100644 --- a/src/Tests/AzureClientTests.cs +++ b/src/Tests/AzureClientTests.cs @@ -70,11 +70,17 @@ public void TestAzureExecutionTarget() Assert.AreEqual(targetId, executionTarget?.TargetId); Assert.AreEqual("Microsoft.Quantum.Providers.IonQ", executionTarget?.PackageName); + // Check that deprecated targets still work. targetId = "HonEYWEll.targetId"; executionTarget = AzureExecutionTarget.Create(targetId); Assert.AreEqual(targetId, executionTarget?.TargetId); Assert.AreEqual("Microsoft.Quantum.Providers.Honeywell", executionTarget?.PackageName); + targetId = "QuantiNUUm.targetId"; + executionTarget = AzureExecutionTarget.Create(targetId); + Assert.AreEqual(targetId, executionTarget?.TargetId); + Assert.AreEqual("Microsoft.Quantum.Providers.Honeywell", executionTarget?.PackageName); + targetId = "qci.target.name.qpu"; executionTarget = AzureExecutionTarget.Create(targetId); Assert.AreEqual(targetId, executionTarget?.TargetId); @@ -150,11 +156,13 @@ public void TestManualTargets() // set up the mock workspace var azureWorkspace = azureClient.ActiveWorkspace as MockAzureWorkspace; Assert.IsNotNull(azureWorkspace); - azureWorkspace?.AddProviders("ionq", "honeywell", "unrecognized"); + azureWorkspace?.AddProviders("ionq", "honeywell", "quantinuum", "unrecognized"); // get connection status to verify list of targets targets = ExpectSuccess>(azureClient.GetConnectionStatusAsync(new MockChannel())); - Assert.AreEqual(4, targets.Count()); // only 2 valid quantum execution targets + // Above, we added 3 valid quantum execution targets, each of which contributes two targets (simulator and mock), + // for a total of six targets. + Assert.That.Enumerable(targets).HasCount(6); // GetActiveTargetAsync, but no active target set yet ExpectError(AzureClientError.NoTarget, azureClient.GetActiveTargetAsync(new MockChannel())); @@ -306,7 +314,7 @@ public void TestRuntimeCapabilities() // Set up workspace with mock providers var azureWorkspace = azureClient.ActiveWorkspace as MockAzureWorkspace; Assert.IsNotNull(azureWorkspace); - azureWorkspace?.AddProviders("ionq", "honeywell"); + azureWorkspace?.AddProviders("ionq", "honeywell", "quantinuum"); // Verify that IonQ job fails to compile (QPRGen0) ExpectSuccess(azureClient.SetActiveTargetAsync(new MockChannel(), "ionq.mock")); @@ -316,6 +324,11 @@ public void TestRuntimeCapabilities() ExpectSuccess(azureClient.SetActiveTargetAsync(new MockChannel(), "honeywell.mock")); var job = ExpectSuccess(azureClient.SubmitJobAsync(new MockChannel(), submissionContext, CancellationToken.None)); Assert.IsNotNull(job); + + // Verify that Quantinuum job can be successfully submitted (QPRGen1) + ExpectSuccess(azureClient.SetActiveTargetAsync(new MockChannel(), "quantinuum.mock")); + job = ExpectSuccess(azureClient.SubmitJobAsync(new MockChannel(), submissionContext, CancellationToken.None)); + Assert.IsNotNull(job); } [TestMethod] diff --git a/src/Tests/IQsharpEngineTests.cs b/src/Tests/IQsharpEngineTests.cs index dc8843a809..0a66975de2 100644 --- a/src/Tests/IQsharpEngineTests.cs +++ b/src/Tests/IQsharpEngineTests.cs @@ -933,6 +933,8 @@ operation RunTeleport() : Unit { .WithMockAzure() .Input("%azure.target honeywell.mock") .ExecutesSuccessfully() + .Input("%azure.target quantinuum.mock") + .ExecutesSuccessfully() .Input("%azure.submit RunTeleport") .ExecutesSuccessfully() .Input("%azure.target ionq.mock") diff --git a/src/Tests/TestExtensions.cs b/src/Tests/TestExtensions.cs index 80040dd473..3197ad9f29 100644 --- a/src/Tests/TestExtensions.cs +++ b/src/Tests/TestExtensions.cs @@ -41,7 +41,7 @@ await engine var client = await (await engine).Engine.GetEngineService(); if (client is AzureClient azureClient && azureClient.ActiveWorkspace is MockAzureWorkspace workspace) { - workspace.AddProviders("ionq", "honeywell"); + workspace.AddProviders("ionq", "quantinuum", "honeywell"); } else { @@ -207,6 +207,31 @@ internal static T HasOperation(this T assert, string namespaceName, string na internal static string NormalizeLineEndings(this string s) => Regex.Replace(s, @"\r\n|\n\r|\n|\r", "\r\n"); + + + internal class EnumerableAssert + { + internal IEnumerable Enumerable; + } + + internal static EnumerableAssert Enumerable(this Assert assert, IEnumerable enumerable) => + new EnumerableAssert + { + Enumerable = enumerable + }; + + internal static EnumerableAssert HasCount(this EnumerableAssert enumerableAssert, int count) + { + // Collect in a list so that we can report on failure. + var elements = enumerableAssert.Enumerable.ToList(); + Assert.AreEqual( + count, + elements.Count, + $"Expected {count} elements, but got {elements.Count}. Enumerable yielded values:\n{string.Join("\n", elements.Select(e => $" - {e?.ToString() ?? ""}"))}" + ); + return enumerableAssert; + } + } } diff --git a/src/Tests/Workspace.ProjectReferences.ProjectA/ProjectA.csproj b/src/Tests/Workspace.ProjectReferences.ProjectA/ProjectA.csproj index b56d7e3f8c..d8d9bb03e9 100644 --- a/src/Tests/Workspace.ProjectReferences.ProjectA/ProjectA.csproj +++ b/src/Tests/Workspace.ProjectReferences.ProjectA/ProjectA.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 diff --git a/src/Tests/Workspace.ProjectReferences.ProjectB/ProjectB.csproj b/src/Tests/Workspace.ProjectReferences.ProjectB/ProjectB.csproj index f8c0a6c431..635c0a7b99 100644 --- a/src/Tests/Workspace.ProjectReferences.ProjectB/ProjectB.csproj +++ b/src/Tests/Workspace.ProjectReferences.ProjectB/ProjectB.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 diff --git a/src/Tests/Workspace.ProjectReferences/Workspace.ProjectReferences.csproj b/src/Tests/Workspace.ProjectReferences/Workspace.ProjectReferences.csproj index 0547d2fdab..12611f48bd 100644 --- a/src/Tests/Workspace.ProjectReferences/Workspace.ProjectReferences.csproj +++ b/src/Tests/Workspace.ProjectReferences/Workspace.ProjectReferences.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -7,7 +7,7 @@ - + diff --git a/src/Tool/appsettings.json b/src/Tool/appsettings.json index 4d65270816..f9867c504d 100644 --- a/src/Tool/appsettings.json +++ b/src/Tool/appsettings.json @@ -6,21 +6,21 @@ }, "AllowedHosts": "*", "DefaultPackageVersions": [ - "Microsoft.Quantum.Compiler::0.22.191200-beta", - "Microsoft.Quantum.CSharpGeneration::0.22.191200-beta", - "Microsoft.Quantum.Development.Kit::0.22.191200-beta", - "Microsoft.Quantum.Simulators::0.22.191200-beta", - "Microsoft.Quantum.Xunit::0.22.191200-beta", - "Microsoft.Quantum.Standard::0.22.191200-beta", - "Microsoft.Quantum.Standard.Visualization::0.22.191200-beta", - "Microsoft.Quantum.Chemistry::0.22.191200-beta", - "Microsoft.Quantum.Chemistry.Jupyter::0.22.191200-beta", - "Microsoft.Quantum.MachineLearning::0.22.191200-beta", - "Microsoft.Quantum.Numerics::0.22.191200-beta", - "Microsoft.Quantum.Katas::0.22.191200-beta", - "Microsoft.Quantum.Research::0.22.191200-beta", - "Microsoft.Quantum.Providers.IonQ::0.22.191200-beta", - "Microsoft.Quantum.Providers.Honeywell::0.22.191200-beta", - "Microsoft.Quantum.Providers.QCI::0.22.191200-beta" + "Microsoft.Quantum.Compiler::0.23.194209-beta", + "Microsoft.Quantum.CSharpGeneration::0.23.194209-beta", + "Microsoft.Quantum.Development.Kit::0.23.194209-beta", + "Microsoft.Quantum.Simulators::0.23.194209-beta", + "Microsoft.Quantum.Xunit::0.23.194209-beta", + "Microsoft.Quantum.Standard::0.23.194209-beta", + "Microsoft.Quantum.Standard.Visualization::0.23.194209-beta", + "Microsoft.Quantum.Chemistry::0.23.194209-beta", + "Microsoft.Quantum.Chemistry.Jupyter::0.23.194209-beta", + "Microsoft.Quantum.MachineLearning::0.23.194209-beta", + "Microsoft.Quantum.Numerics::0.23.194209-beta", + "Microsoft.Quantum.Katas::0.23.194209-beta", + "Microsoft.Quantum.Research::0.23.194209-beta", + "Microsoft.Quantum.Providers.IonQ::0.23.194209-beta", + "Microsoft.Quantum.Providers.Honeywell::0.23.194209-beta", + "Microsoft.Quantum.Providers.QCI::0.23.194209-beta" ] } \ No newline at end of file diff --git a/tests.live/All.Tests.ps1 b/tests.live/All.Tests.ps1 index 3dddc1388b..0768075c74 100644 --- a/tests.live/All.Tests.ps1 +++ b/tests.live/All.Tests.ps1 @@ -59,6 +59,11 @@ Describe "Test Python Integration" { python -m pytest -k honeywell --junitxml="junit/TestResults-Honeywell.xml" | Write-Verbose $LASTEXITCODE | Should -Be 0 } + + It "Runs pytest successfully for Quantinuum" -Tag "submit.quantinuum" { + python -m pytest -k quantinuum --junitxml="junit/Quantinuum.xml" | Write-Verbose + $LASTEXITCODE | Should -Be 0 + } AfterAll { Pop-Location } } diff --git a/tests.live/Python/test_live.py b/tests.live/Python/test_live.py index 7f119b836c..6fc730c81b 100644 --- a/tests.live/Python/test_live.py +++ b/tests.live/Python/test_live.py @@ -159,4 +159,52 @@ def test_honeywell_submit(): retrieved_histogram = qsharp.azure.output() assert isinstance(retrieved_histogram, dict) assert '0' in retrieved_histogram + +def test_quantinuum_targets(): + """ + Tests that we can fetch targets from the service, + and that the workspace includes the targets we need for submission + """ + targets = connect() + assert len(targets) > 2 + + target_ids = [t.id for t in targets] + assert 'quantinuum.hqs-lt-s1' in target_ids + assert 'quantinuum.hqs-lt-s1-apival' in target_ids + +def test_quantinuum_submit(): + """ + Test that the RunTeleport operation can be submitted successfully on the quantinuum apival target + """ + import qsharp + from Microsoft.Quantum.Tests import RunTeleport + + # Make sure we can simulate locally: + expected = True + result = RunTeleport.simulate(doPlus=expected) + assert result == 0 if expected else 1 + + import qsharp.azure + connect() + + t = qsharp.azure.target("quantinuum.hqs-lt-s1-apival") + assert isinstance(t, qsharp.azure.AzureTarget) + assert t.id == "quantinuum.hqs-lt-s1-apival" + + job = qsharp.azure.submit(RunTeleport, doPlus=expected) + assert isinstance(job, qsharp.azure.AzureJob) + assert not job.id == '' + print("Submitted job: ", job.id) + + try: + wait_until_completed(job) + except TimeoutError: + warnings.warn("Quantinuum execution exceeded timeout. Skipping fetching results.") + else: + job = qsharp.azure.status() + assert isinstance(job, qsharp.azure.AzureJob) + if job.status == "Succeeded": + retrieved_histogram = qsharp.azure.output() + assert isinstance(retrieved_histogram, dict) + assert '0' in retrieved_histogram \ No newline at end of file