diff --git a/MachineLearning/src/Structure.qs b/MachineLearning/src/Structure.qs index 7b8f191a3c8..80f31432225 100644 --- a/MachineLearning/src/Structure.qs +++ b/MachineLearning/src/Structure.qs @@ -22,7 +22,7 @@ namespace Microsoft.Quantum.MachineLearning { : Int { mutable nQubitsRequired = 0; for (gate in model::Structure) { - set nQubitsRequired = Fold( + set nQubitsRequired = 1 + Fold( MaxI, 0, gate::ControlIndices + [ gate::TargetIndex, diff --git a/MachineLearning/tests/ClassificationTests.qs b/MachineLearning/tests/ClassificationTests.qs new file mode 100644 index 00000000000..96e9c4ce309 --- /dev/null +++ b/MachineLearning/tests/ClassificationTests.qs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.MachineLearning.Tests { + open Microsoft.Quantum.Logical; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.MachineLearning as ML; + + @Test("QuantumSimulator") + function InferredLabelFact() : Unit { + EqualityFactI(ML.InferredLabel(0.25, 0.26), 1, "InferredLabel returned wrong class."); + } + + @Test("QuantumSimulator") + function InferredLabelsFact() : Unit { + AllEqualityFactI( + ML.InferredLabels(0.25, [0.23, 0.26, 0.1]), + [0, 1, 0], + "InferredLabels returned at least one wrong class." + ); + } + +} diff --git a/MachineLearning/tests/StructureTests.qs b/MachineLearning/tests/StructureTests.qs new file mode 100644 index 00000000000..e9ee25a29ae --- /dev/null +++ b/MachineLearning/tests/StructureTests.qs @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.MachineLearning.Tests { + open Microsoft.Quantum.Logical; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.MachineLearning as ML; + + @Test("QuantumSimulator") + function NQubitsRequiredFact() : Unit { + let model = Default() + w/ Structure <- [ + ML.ControlledRotation((3, [7, 9]), PauliX, 0) + ]; + let actual = ML.NQubitsRequired(model); + EqualityFactI(actual, 10, "Wrong output from NQubitsRequired."); + } + + function ExampleModel() : ML.SequentialModel { + return Default() + w/ Structure <- [ + Default() + w/ TargetIndex <- 2 + w/ ControlIndices <- [0] + w/ Axis <- PauliX + w/ ParameterIndex <- 0, + Default() + w/ TargetIndex <- 0 + w/ ControlIndices <- [1, 2] + w/ Axis <- PauliZ + w/ ParameterIndex <- 1 + ] + w/ Parameters <- [ + 1.234, + 2.345 + ]; + } + + operation ApplyExampleModelManually(register : Qubit[]) : Unit is Adj + Ctl { + Controlled R([register[0]], (PauliX, 1.234, register[2])); + Controlled R([register[1], register[2]], (PauliZ, 2.345, register[0])); + } + + @Test("QuantumSimulator") + operation TestApplySequentialClassifier() : Unit { + AssertOperationsEqualReferenced(ML.NQubitsRequired(ExampleModel()), + ML.ApplySequentialClassifier(ExampleModel(), _), + ApplyExampleModelManually + ); + } + + function EqualCR(x : ML.ControlledRotation, y : ML.ControlledRotation) : Bool { + return x::Axis == y::Axis and + All(EqualI, Zip(x::ControlIndices, y::ControlIndices)) and + x::TargetIndex == y::TargetIndex and + x::ParameterIndex == y::ParameterIndex; + } + + @Test("QuantumSimulator") + function LocalRotationsLayerFact() : Unit { + Fact(All(EqualCR, Zip( + ML.LocalRotationsLayer(3, PauliY), + [ + Default() + w/ TargetIndex <- 0 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 0, + + Default() + w/ TargetIndex <- 1 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 1, + + Default() + w/ TargetIndex <- 2 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 2 + ] + )), "LocalRotationsLayer returned wrong output."); + } + + @Test("QuantumSimulator") + function PartialRotationsLayerFact() : Unit { + Fact(All(EqualCR, Zip( + ML.PartialRotationsLayer([4, 5, 6], PauliY), + [ + Default() + w/ TargetIndex <- 4 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 0, + + Default() + w/ TargetIndex <- 5 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 1, + + Default() + w/ TargetIndex <- 6 + w/ ControlIndices <- new Int[0] + w/ Axis <- PauliY + w/ ParameterIndex <- 2 + ] + )), "PartialRotationsLayer returned wrong output."); + } + + @Test("QuantumSimulator") + function CyclicEntanglingLayerFact() : Unit { + Fact(All(EqualCR, Zip( + ML.CyclicEntanglingLayer(3, PauliX, 2), + [ + Default() + w/ TargetIndex <- 0 + w/ ControlIndices <- [2] + w/ Axis <- PauliX + w/ ParameterIndex <- 0, + + Default() + w/ TargetIndex <- 1 + w/ ControlIndices <- [0] + w/ Axis <- PauliX + w/ ParameterIndex <- 1, + + Default() + w/ TargetIndex <- 2 + w/ ControlIndices <- [1] + w/ Axis <- PauliX + w/ ParameterIndex <- 2 + ] + )), "CyclicEntanglingLayer returned wrong output."); + } + + @Test("QuantumSimulator") + function CombinedStructureFact() : Unit { + let combined = ML.CombinedStructure([ + [ + Default() + w/ TargetIndex <- 0 + w/ ControlIndices <- [2] + w/ Axis <- PauliX + w/ ParameterIndex <- 0, + + Default() + w/ TargetIndex <- 1 + w/ ControlIndices <- [0] + w/ Axis <- PauliX + w/ ParameterIndex <- 1 + ], + [ + Default() + w/ TargetIndex <- 2 + w/ ControlIndices <- [1] + w/ Axis <- PauliZ + w/ ParameterIndex <- 0 + ] + ]); + Fact(All(EqualCR, Zip( + combined, + [ + Default() + w/ TargetIndex <- 0 + w/ ControlIndices <- [2] + w/ Axis <- PauliX + w/ ParameterIndex <- 0, + + Default() + w/ TargetIndex <- 1 + w/ ControlIndices <- [0] + w/ Axis <- PauliX + w/ ParameterIndex <- 1, + + Default() + w/ TargetIndex <- 2 + w/ ControlIndices <- [1] + w/ Axis <- PauliZ + w/ ParameterIndex <- 2 + ] + )), "CombinedStructure returned wrong output."); + } + +} diff --git a/MachineLearning/tests/TypesTests.qs b/MachineLearning/tests/TypesTests.qs new file mode 100644 index 00000000000..4a70e80f30e --- /dev/null +++ b/MachineLearning/tests/TypesTests.qs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.MachineLearning.Tests { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.MachineLearning as ML; + + @Test("QuantumSimulator") + function ScheduleLengthFact() : Unit { + let actualLength = ML.ScheduleLength(ML.SamplingSchedule([0..4, 1..2..5])); + EqualityFactI(actualLength, 5 + 3, "Wrong output from ScheduleLength."); + } + + @Test("QuantumSimulator") + function SampledFact() : Unit { + let actuallySampled = ML.Sampled(ML.SamplingSchedule([0..4, 1..2..5]), [0, 10, 20, 30, 40, 50, 60, 70]); + AllEqualityFactI(actuallySampled, [0, 10, 20, 30, 40, 10, 30, 50], "Wrong output from Sampled."); + } + +} diff --git a/MachineLearning/tests/ValidationTests.qs b/MachineLearning/tests/ValidationTests.qs new file mode 100644 index 00000000000..9fc54cf74aa --- /dev/null +++ b/MachineLearning/tests/ValidationTests.qs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.MachineLearning.Tests { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.MachineLearning as ML; + + @Test("QuantumSimulator") + function MisclassificationsFact() : Unit { + let misclassifications = ML.Misclassifications([0, 1, 0, 0], [0, 1, 1, 0]); + AllEqualityFactI(misclassifications, [2], "Wrong output from Misclassifications."); + } + + @Test("QuantumSimulator") + function NMisclassificationsFact() : Unit { + let nMisclassifications = ML.NMisclassifications([0, 1, 0, 0, 1], [0, 1, 1, 0, 0]); + EqualityFactI(nMisclassifications, 2, "Wrong output from NMisclassifications."); + } +} diff --git a/Standard/src/Arithmetic/Comparators.qs b/Standard/src/Arithmetic/Comparators.qs index c6898baa6f6..217ab59c479 100644 --- a/Standard/src/Arithmetic/Comparators.qs +++ b/Standard/src/Arithmetic/Comparators.qs @@ -54,19 +54,15 @@ namespace Microsoft.Quantum.Arithmetic { } // Implementation step of `ApplyRippleCarryComparatorLE`. - operation _ApplyRippleCarryComparatorLE(x: LittleEndian, y: LittleEndian, auxiliary: Qubit[], output: Qubit) : Unit { - body (...) { - let nQubitsX = Length(x!); + operation _ApplyRippleCarryComparatorLE(x: LittleEndian, y: LittleEndian, auxiliary: Qubit[], output: Qubit) + : Unit is Adj + Ctl { + let nQubitsX = Length(x!); - // Take 2's complement - ApplyToEachCA(X, x! + auxiliary); + // Take 2's complement + ApplyToEachCA(X, x! + auxiliary); - InPlaceMajority(x![0], [y![0], auxiliary[0]]); - ApplyToEachCA(MAJ, Zip3(Most(x!), Rest(y!), Rest(x!))); - } - adjoint auto; - controlled auto; - adjoint controlled auto; + ApplyMajorityInPlace(x![0], [y![0], auxiliary[0]]); + ApplyToEachCA(MAJ, Zip3(Most(x!), Rest(y!), Rest(x!))); } } diff --git a/Standard/src/Arithmetic/Integer.qs b/Standard/src/Arithmetic/Integer.qs index 07c9d9a306a..4c59819a1b2 100644 --- a/Standard/src/Arithmetic/Integer.qs +++ b/Standard/src/Arithmetic/Integer.qs @@ -404,7 +404,7 @@ namespace Microsoft.Quantum.Arithmetic { "Input registers must have the same number of qubits." ); ApplyToEachCA(CNOT, Zip(Rest(xs!), Rest(ys!))); - (Adjoint CascadeCNOT) (Rest(xs!)); + Adjoint ApplyCNOTChain(Rest(xs!)); } /// # Summary @@ -583,16 +583,18 @@ namespace Microsoft.Quantum.Arithmetic { X(ys![0]); } else { - ApplyToEachCA(X, ys!); - ApplyToEachCA(CNOT, Zip(Rest(xs!),Rest(ys!))); - (Adjoint CascadeCNOT) (Rest(xs!)); - CascadeCCNOT (Most(ys!), xs!); - (Controlled CCNOT) (controls, (xs![nQubits-1], ys![nQubits-1], result)); - (Adjoint CascadeCCNOT) (Most(ys!), xs!); - CascadeCNOT(Rest(xs!)); - (Controlled CNOT) (controls, (xs![nQubits-1], result)); - ApplyToEachCA(CNOT, Zip(Rest(xs!), Rest(ys!))); - ApplyToEachCA(X, ys!); + within { + ApplyToEachCA(X, ys!); + ApplyToEachCA(CNOT, Zip(Rest(xs!), Rest(ys!))); + } apply { + within { + (Adjoint ApplyCNOTChain) (Rest(xs!)); + CascadeCCNOT (Most(ys!), xs!); + } apply { + (Controlled CCNOT) (controls, (xs![nQubits-1], ys![nQubits-1], result)); + } + (Controlled CNOT) (controls, (xs![nQubits-1], result)); + } } } } diff --git a/Standard/src/Diagnostics/Facts.qs b/Standard/src/Diagnostics/Facts.qs index 33224a98d85..eaa4622c58a 100644 --- a/Standard/src/Diagnostics/Facts.qs +++ b/Standard/src/Diagnostics/Facts.qs @@ -200,6 +200,9 @@ namespace Microsoft.Quantum.Diagnostics { /// The array that is expected from a test case of interest. /// ## message /// A message to be printed if the arrays are not equal. + /// + /// # See Also + /// Microsoft.Quantum.Diagnostics.AllEqualityFactI function AllEqualityFactB(actual : Bool[], expected : Bool[], message : String) : Unit { let n = Length(actual); if (n != Length(expected)) { @@ -209,4 +212,26 @@ namespace Microsoft.Quantum.Diagnostics { Ignore(Mapped(EqualityFactB(_, _, message), Zip(actual, expected))); } + /// # Summary + /// Asserts that two arrays of integer values are equal. + /// + /// # Input + /// ## actual + /// The array that is produced by a test case of interest. + /// ## expected + /// The array that is expected from a test case of interest. + /// ## message + /// A message to be printed if the arrays are not equal. + /// + /// # See Also + /// Microsoft.Quantum.Diagnostics.AllEqualityFactB + function AllEqualityFactI(actual : Int[], expected : Int[], message : String) : Unit { + let n = Length(actual); + if (n != Length(expected)) { + fail message; + } + + Ignore(Mapped(EqualityFactI(_, _, message), Zip(actual, expected))); + } + } diff --git a/Standard/src/Simulation/Algorithms.qs b/Standard/src/Simulation/Algorithms.qs index 3ecc5d7f19f..ef33f64567b 100644 --- a/Standard/src/Simulation/Algorithms.qs +++ b/Standard/src/Simulation/Algorithms.qs @@ -9,7 +9,7 @@ namespace Microsoft.Quantum.Simulation { // A simulation technique converts an EvolutionGenerator to time evolution // by the encoded system for some time step // Here is an example of a simulation technique. - + /// # Summary /// Implements time-evolution by a term contained in a `GeneratorSystem`. /// @@ -28,8 +28,8 @@ namespace Microsoft.Quantum.Simulation { let generatorIndex = generatorSystemFunction(idx); (evolutionSet!(generatorIndex))!(stepsize, qubits); } - - + + /// # Summary /// Implements a single time-step of time-evolution by the system /// described in an `EvolutionGenerator` using a Trotter–Suzuki @@ -54,19 +54,19 @@ namespace Microsoft.Quantum.Simulation { { let (evolutionSet, generatorSystem) = evolutionGenerator!; let (nTerms, generatorSystemFunction) = generatorSystem!; - + // The input to DecomposeIntoTimeStepsCA has signature // (Int, ((Int, Double, Qubit[]) => () is Adj + Ctl)) let trotterForm = (nTerms, TrotterStepImpl(evolutionGenerator, _, _, _)); - return (DecomposeIntoTimeStepsCA(trotterForm, trotterOrder))(trotterStepSize, _); + return (DecomposedIntoTimeStepsCA(trotterForm, trotterOrder))(trotterStepSize, _); } - - + + // This simulation algorithm takes (timeMax, EvolutionGenerator, // register) and other algorithm-specific parameters (trotterStepSize, // trotterOrder), and performs evolution under the EvolutionGenerator // for time = timeMax. - + /// # Summary /// Makes repeated calls to `TrotterStep` to approximate the /// time-evolution operator exp(_-iHt_). @@ -88,19 +88,19 @@ namespace Microsoft.Quantum.Simulation { { let nTimeSlices = Ceiling(maxTime / trotterStepSize); let resizedTrotterStepSize = maxTime / IntAsDouble(nTimeSlices); - + for (idxTimeSlice in 0 .. nTimeSlices - 1) { (TrotterStep(evolutionGenerator, trotterOrder, resizedTrotterStepSize))(qubits); } } - + adjoint invert; controlled distribute; controlled adjoint distribute; } - - + + /// # Summary /// `SimulationAlgorithm` function that uses a Trotter–Suzuki /// decomposition to approximate the time-evolution operator _exp(-iHt)_. @@ -117,11 +117,11 @@ namespace Microsoft.Quantum.Simulation { { return SimulationAlgorithm(TrotterSimulationAlgorithmImpl(trotterStepSize, trotterOrder, _, _, _)); } - - + + // This simple time-dependent simulation algorithm implements a // sequence of uniformly-sized trotter steps - + /// # Summary /// Implementation of multiple Trotter steps to approximate a unitary /// operator that solves the time-dependent Schrödinger equation. @@ -143,7 +143,7 @@ namespace Microsoft.Quantum.Simulation { { let nTimeSlices = Ceiling(maxTime / trotterStepSize); let resizedTrotterStepSize = maxTime / IntAsDouble(nTimeSlices); - + for (idxTimeSlice in 0 .. nTimeSlices - 1) { let schedule = IntAsDouble(idxTimeSlice) / IntAsDouble(nTimeSlices); @@ -153,13 +153,13 @@ namespace Microsoft.Quantum.Simulation { (TrotterSimulationAlgorithm(resizedTrotterStepSize, trotterOrder))!(resizedTrotterStepSize, evolutionGenerator, qubits); } } - + adjoint invert; controlled distribute; controlled adjoint distribute; } - - + + /// # Summary /// `TimeDependentSimulationAlgorithm` function that uses a Trotter–Suzuki /// decomposition to approximate a unitary operator that solves the @@ -177,7 +177,7 @@ namespace Microsoft.Quantum.Simulation { { return TimeDependentSimulationAlgorithm(TimeDependentTrotterSimulationAlgorithmImpl(trotterStepSize, trotterOrder, _, _, _)); } - + }