From 22e61d74fe8475f18a5184f2d76cd952fc5c4fc5 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 21 Feb 2020 16:01:56 -0800 Subject: [PATCH 1/4] Added new verbosemessage training option. --- MachineLearning/src/Training.qs | 21 +++++++++++++-------- MachineLearning/src/Types.qs | 13 +++++++++++-- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/MachineLearning/src/Training.qs b/MachineLearning/src/Training.qs index a95e4b21ef1..c8fd204f258 100644 --- a/MachineLearning/src/Training.qs +++ b/MachineLearning/src/Training.qs @@ -91,7 +91,7 @@ namespace Microsoft.Quantum.MachineLearning { let labels = Mapped(_Label, samples); for ((idxModel, model) in Enumerated(models)) { - Message($"Beginning training at start point #{idxModel}..."); + options::VerboseMessage($" Beginning training at start point #{idxModel}..."); let proposedUpdate = TrainSequentialClassifierAtModel( model, samples, options, trainingSchedule, 1 @@ -151,15 +151,17 @@ namespace Microsoft.Quantum.MachineLearning { let nQubits = MaxI(FeatureRegisterSize(miniBatch[0]::Features), NQubitsRequired(model)); let effectiveTolerance = options::Tolerance / IntAsDouble(Length(model::Structure)); - for (sample in miniBatch) { + for ((idxSample, sample) in Enumerated(miniBatch)) { mutable err = IntAsDouble(sample::Label); if (err < 1.0) { set err = -1.0; //class 0 misclassified to class 1; strive to reduce the probability } + options::VerboseMessage($" Encoding sample {idxSample}..."); let stateGenerator = ApproximateInputEncoder(effectiveTolerance, sample::Features) // Force the number of qubits in case something else in the // minibatch requires a larger register. w/ NQubits <- nQubits; + options::VerboseMessage($" Estimating gradient at sample {idxSample}..."); let grad = EstimateGradient( model, stateGenerator, options::NMeasurements @@ -229,7 +231,7 @@ namespace Microsoft.Quantum.MachineLearning { ) ); - //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters + // An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters let minibatches = Mapped( Subarray(_, samples), Chunks( @@ -237,11 +239,13 @@ namespace Microsoft.Quantum.MachineLearning { Misclassifications(inferredLabels, actualLabels) ) ); - for (minibatch in minibatches) { + for ((idxMinibatch, minibatch) in Enumerated(minibatches)) { + options::VerboseMessage($" Beginning minibatch {idxMinibatch} of {Length(minibatches)}."); let (utility, updatedModel) = _RunSingleTrainingStep( minibatch, options, bestSoFar ); if (utility > 0.0000001) { + options::VerboseMessage($" Observed good parameter update... estimating and possibly commiting."); // There has been some good parameter update. // Check if it actually improves things, and if so, // commit it. @@ -343,11 +347,11 @@ namespace Microsoft.Quantum.MachineLearning { mutable nStalls = 0; for (ep in 1..options::MaxEpochs) { + options::VerboseMessage($" Beginning epoch {ep}."); let (nMisses, proposedUpdate) = _RunSingleTrainingEpoch( samples, schedule, periodScore, - options - w/ LearningRate <- lrate - w/ MinibatchSize <- batchSize, + options w/ LearningRate <- lrate + w/ MinibatchSize <- batchSize, current, nBestMisses ); @@ -363,7 +367,8 @@ namespace Microsoft.Quantum.MachineLearning { } if ( - NearlyEqualD(current::Bias, proposedUpdate::Bias) and _AllNearlyEqualD(current::Parameters, proposedUpdate::Parameters) + NearlyEqualD(current::Bias, proposedUpdate::Bias) and + _AllNearlyEqualD(current::Parameters, proposedUpdate::Parameters) ) { set nStalls += 1; // If we're more than halfway through our maximum allowed number of stalls, diff --git a/MachineLearning/src/Types.qs b/MachineLearning/src/Types.qs index c86d223df5c..ac3ec00bb79 100644 --- a/MachineLearning/src/Types.qs +++ b/MachineLearning/src/Types.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.MachineLearning { + open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Arithmetic; @@ -176,6 +177,8 @@ namespace Microsoft.Quantum.MachineLearning { /// (approximately zero gradient) before failing. /// ## StochasticRescaleFactor /// The amount to rescale stalled models by before retrying an update. + /// ## VerboseMessage + /// A function that can be used to provide verbose feedback. /// /// # Remarks /// This UDT should not be created directly, but rather should be specified @@ -196,9 +199,14 @@ namespace Microsoft.Quantum.MachineLearning { NMeasurements: Int, MaxEpochs: Int, MaxStalls: Int, - StochasticRescaleFactor: Double + StochasticRescaleFactor: Double, + VerboseMessage: (String -> Unit) ); + // Used only to populate default training options to ignore verbose + // messages. + function _Ignored(value : String) : Unit {} + /// # Summary /// Returns a default set of options for training classifiers. /// @@ -215,7 +223,8 @@ namespace Microsoft.Quantum.MachineLearning { /// ``` function DefaultTrainingOptions() : TrainingOptions { return TrainingOptions( - 0.1, 0.005, 15, 10000, 16, 8, 0.01 + 0.1, 0.005, 15, 10000, 16, 8, 0.01, + _Ignored ); } From 61d5e4f4da3986a04e80ca2a79945c40139b8dfb Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 21 Feb 2020 17:59:16 -0800 Subject: [PATCH 2/4] Cache state preparation using BoundCA. --- Chemistry/src/Runtime/Runtime.csproj | 4 +- .../tests/ChemistryTests/QSharpTests.csproj | 2 +- .../tests/SystemTests/SystemTests.csproj | 2 +- MachineLearning/src/InputEncoding.qs | 23 +++--- MachineLearning/src/MachineLearning.csproj | 2 +- MachineLearning/src/Training.qs | 36 ++++++--- .../tests/MachineLearningTests.csproj | 2 +- Numerics/src/Numerics.csproj | 4 +- Numerics/tests/NumericsTests.csproj | 2 +- Standard/src/Preparation/Arbitrary.qs | 80 +++++++++++++++---- Standard/src/Standard.csproj | 4 +- Standard/tests/Standard.Tests.csproj | 2 +- 12 files changed, 111 insertions(+), 52 deletions(-) diff --git a/Chemistry/src/Runtime/Runtime.csproj b/Chemistry/src/Runtime/Runtime.csproj index 329b463b03b..00894345ae6 100644 --- a/Chemistry/src/Runtime/Runtime.csproj +++ b/Chemistry/src/Runtime/Runtime.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -16,7 +16,7 @@ - + diff --git a/Chemistry/tests/ChemistryTests/QSharpTests.csproj b/Chemistry/tests/ChemistryTests/QSharpTests.csproj index 9196080d248..dd14fae3e1c 100644 --- a/Chemistry/tests/ChemistryTests/QSharpTests.csproj +++ b/Chemistry/tests/ChemistryTests/QSharpTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Chemistry/tests/SystemTests/SystemTests.csproj b/Chemistry/tests/SystemTests/SystemTests.csproj index 83477b28bb7..4173aa7eca5 100644 --- a/Chemistry/tests/SystemTests/SystemTests.csproj +++ b/Chemistry/tests/SystemTests/SystemTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/MachineLearning/src/InputEncoding.qs b/MachineLearning/src/InputEncoding.qs index 62adda62358..d0dca7dcc05 100644 --- a/MachineLearning/src/InputEncoding.qs +++ b/MachineLearning/src/InputEncoding.qs @@ -47,18 +47,12 @@ namespace Microsoft.Quantum.MachineLearning { } /// Do special processing on the first cNegative entries - operation _EncodeSparseNegativeInput( - cNegative: Int, - tolerance: Double, + operation _ReflectAboutNegativeCoefficients( + negLocs : Int[], coefficients : ComplexPolar[], reg: LittleEndian ) : Unit is Adj + Ctl { - let negLocs = _NegativeLocations(cNegative, coefficients); - // Prepare the state disregarding the sign of negative components. - ApproximatelyPrepareArbitraryState(tolerance, _Unnegate(negLocs, coefficients), reg); - // Reflect about the negative coefficients to apply the negative signs - // at the end. for (idxNegative in negLocs) { ReflectAboutInteger(idxNegative, reg); } @@ -126,16 +120,25 @@ namespace Microsoft.Quantum.MachineLearning { // Here, by a "few," we mean fewer than the number of qubits required // to encode features. if ((cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0)) { + let negLocs = _NegativeLocations(cNegative, complexCoefficients); return StateGenerator( nQubits, - _EncodeSparseNegativeInput(cNegative, tolerance, complexCoefficients, _) + BoundCA([ + // Prepare the state disregarding the sign of negative components. + _CompileApproximateArbitraryStatePreparation( + tolerance, _Unnegate(negLocs, complexCoefficients), nQubits + ), + // Reflect about the negative coefficients to apply the negative signs + // at the end. + _ReflectAboutNegativeCoefficients(negLocs, complexCoefficients, _) + ]) ); } // Finally, we fall back to arbitrary state preparation. return StateGenerator( nQubits, - ApproximatelyPrepareArbitraryState(tolerance, complexCoefficients, _) + _CompileApproximateArbitraryStatePreparation(tolerance, complexCoefficients, nQubits) ); } diff --git a/MachineLearning/src/MachineLearning.csproj b/MachineLearning/src/MachineLearning.csproj index d9204dafa29..3808778dd91 100644 --- a/MachineLearning/src/MachineLearning.csproj +++ b/MachineLearning/src/MachineLearning.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 x64 diff --git a/MachineLearning/src/Training.qs b/MachineLearning/src/Training.qs index c8fd204f258..f2ede702cea 100644 --- a/MachineLearning/src/Training.qs +++ b/MachineLearning/src/Training.qs @@ -142,25 +142,18 @@ namespace Microsoft.Quantum.MachineLearning { /// (utility, (new)parameters) pair /// operation _RunSingleTrainingStep( - miniBatch : LabeledSample[], + miniBatch : (LabeledSample, StateGenerator)[], options : TrainingOptions, model : SequentialModel ) : (Double, SequentialModel) { mutable batchGradient = ConstantArray(Length(model::Parameters), 0.0); - let nQubits = MaxI(FeatureRegisterSize(miniBatch[0]::Features), NQubitsRequired(model)); - let effectiveTolerance = options::Tolerance / IntAsDouble(Length(model::Structure)); - for ((idxSample, sample) in Enumerated(miniBatch)) { + for ((idxSample, (sample, stateGenerator)) in Enumerated(miniBatch)) { mutable err = IntAsDouble(sample::Label); if (err < 1.0) { set err = -1.0; //class 0 misclassified to class 1; strive to reduce the probability } - options::VerboseMessage($" Encoding sample {idxSample}..."); - let stateGenerator = ApproximateInputEncoder(effectiveTolerance, sample::Features) - // Force the number of qubits in case something else in the - // minibatch requires a larger register. - w/ NQubits <- nQubits; options::VerboseMessage($" Estimating gradient at sample {idxSample}..."); let grad = EstimateGradient( model, stateGenerator, @@ -211,7 +204,7 @@ namespace Microsoft.Quantum.MachineLearning { /// epoch. /// - The new best sequential model found. operation _RunSingleTrainingEpoch( - samples : LabeledSample[], + encodedSamples : (LabeledSample, StateGenerator)[], schedule : SamplingSchedule, periodScore: Int, options : TrainingOptions, model : SequentialModel, @@ -220,6 +213,8 @@ namespace Microsoft.Quantum.MachineLearning { : (Int, SequentialModel) { mutable nBestMisses = nPreviousBestMisses; mutable bestSoFar = model; + let samples = Mapped(Fst, encodedSamples); + let stateGenerators = Mapped(Snd, encodedSamples); let features = Mapped(_Features, samples); let actualLabels = Mapped(_Label, samples); @@ -233,7 +228,7 @@ namespace Microsoft.Quantum.MachineLearning { // An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters let minibatches = Mapped( - Subarray(_, samples), + Subarray(_, encodedSamples), Chunks( options::MinibatchSize, Misclassifications(inferredLabels, actualLabels) @@ -281,7 +276,16 @@ namespace Microsoft.Quantum.MachineLearning { ); } - + function _EncodeSample(effectiveTolerance : Double, nQubits : Int, sample : LabeledSample) + : (LabeledSample, StateGenerator) { + return ( + sample, + ApproximateInputEncoder(effectiveTolerance, sample::Features) + // Force the number of qubits in case something else in the + // minibatch requires a larger register. + w/ NQubits <- nQubits + ); + } /// # Summary /// Given the structure of a sequential classifier, trains the classifier @@ -339,6 +343,12 @@ namespace Microsoft.Quantum.MachineLearning { ); mutable current = bestSoFar; + // Encode samples first. + options::VerboseMessage(" Pre-encoding samples..."); + let effectiveTolerance = options::Tolerance / IntAsDouble(Length(model::Structure)); + let nQubits = MaxI(FeatureRegisterSize(samples[0]::Features), NQubitsRequired(model)); + let encodedSamples = Mapped(_EncodeSample(effectiveTolerance, nQubits, _), samples); + //reintroducing learning rate heuristics mutable lrate = options::LearningRate; mutable batchSize = options::MinibatchSize; @@ -349,7 +359,7 @@ namespace Microsoft.Quantum.MachineLearning { for (ep in 1..options::MaxEpochs) { options::VerboseMessage($" Beginning epoch {ep}."); let (nMisses, proposedUpdate) = _RunSingleTrainingEpoch( - samples, schedule, periodScore, + encodedSamples, schedule, periodScore, options w/ LearningRate <- lrate w/ MinibatchSize <- batchSize, current, diff --git a/MachineLearning/tests/MachineLearningTests.csproj b/MachineLearning/tests/MachineLearningTests.csproj index 5762a1a6054..19c6d85219c 100644 --- a/MachineLearning/tests/MachineLearningTests.csproj +++ b/MachineLearning/tests/MachineLearningTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Numerics/src/Numerics.csproj b/Numerics/src/Numerics.csproj index c26ff7edddf..a2ad6273e0b 100644 --- a/Numerics/src/Numerics.csproj +++ b/Numerics/src/Numerics.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -37,7 +37,7 @@ - + diff --git a/Numerics/tests/NumericsTests.csproj b/Numerics/tests/NumericsTests.csproj index a6ae4fb2aba..ae3f330d512 100644 --- a/Numerics/tests/NumericsTests.csproj +++ b/Numerics/tests/NumericsTests.csproj @@ -1,4 +1,4 @@ - + diff --git a/Standard/src/Preparation/Arbitrary.qs b/Standard/src/Preparation/Arbitrary.qs index 7115ca75c48..90fbd0b4a2b 100644 --- a/Standard/src/Preparation/Arbitrary.qs +++ b/Standard/src/Preparation/Arbitrary.qs @@ -218,16 +218,57 @@ namespace Microsoft.Quantum.Preparation { qubits : LittleEndian ) : Unit is Adj + Ctl { + (_CompileApproximateArbitraryStatePreparation(tolerance, coefficients, Length(qubits!)))(qubits); + } + + operation _ApplyToLittleEndian(bareOp : ((Qubit[]) => Unit is Adj + Ctl), register : LittleEndian) + : Unit is Adj + Ctl { + bareOp(register!); + } + + function _CompileApproximateArbitraryStatePreparation( + tolerance : Double, + coefficients : ComplexPolar[], + nQubits : Int + ) + : (LittleEndian => Unit is Adj + Ctl) { // pad coefficients at tail length to a power of 2. - let coefficientsPadded = Padded(-2 ^ Length(qubits!), ComplexPolar(0.0, 0.0), coefficients); - let target = (qubits!)[0]; - let op = (Adjoint _ApproximatelyPrepareArbitraryState(tolerance, coefficientsPadded, _, _))(_, target); - op( + let coefficientsPadded = Padded(-2 ^ nQubits, ComplexPolar(0.0, 0.0), coefficients); + let idxTarget = 0; + let rngControl = // Determine what controls to apply to `op`. - Length(qubits!) > 1 - ? LittleEndian((qubits!)[1 .. Length(qubits!) - 1]) - | LittleEndian(new Qubit[0]) + nQubits > 1 + ? (1 .. (nQubits - 1)) + | (1..0); + let plan = _ApproximatelyUnprepareArbitraryStatePlan( + tolerance, coefficientsPadded, (rngControl, idxTarget) ); + let unprepare = BoundCA(plan); + return _ApplyToLittleEndian(Adjoint unprepare, _); + } + + operation _ApplyMultiplexStep( + tolerance : Double, disentangling : Double[], axis : Pauli, + (rngControl : Range, idxTarget : Int), + register : Qubit[] + ) + : Unit is Adj + Ctl { + let actualControl = LittleEndian(register[rngControl]); + ApproximatelyMultiplexPauli(tolerance, disentangling, axis, actualControl, register[idxTarget]); + } + + function _RangeLength(rng : Range) : Int { + mutable len = 0; + for (idx in rng) { + set len += 1; + } + return len; + } + + operation _ApplyGlobalRotationStep( + angle : Double, idxTarget : Int, register : Qubit[] + ) : Unit is Adj + Ctl { + Exp([PauliI], angle, [register[idxTarget]]); } /// # Summary @@ -236,34 +277,39 @@ namespace Microsoft.Quantum.Preparation { /// # See Also /// - PrepareArbitraryState /// - Microsoft.Quantum.Canon.MultiplexPauli - operation _ApproximatelyPrepareArbitraryState( + function _ApproximatelyUnprepareArbitraryStatePlan( tolerance : Double, coefficients : ComplexPolar[], - control : LittleEndian, target : Qubit + (rngControl : Range, idxTarget : Int) ) - : Unit is Adj + Ctl { + : (Qubit[] => Unit is Adj + Ctl)[] { + mutable plan = new (Qubit[] => Unit is Adj + Ctl)[0]; + // For each 2D block, compute disentangling single-qubit rotation parameters let (disentanglingY, disentanglingZ, newCoefficients) = _StatePreparationSBMComputeCoefficients(coefficients); if (_AnyOutsideToleranceD(tolerance, disentanglingZ)) { - ApproximatelyMultiplexPauli(tolerance, disentanglingZ, PauliZ, control, target); + set plan += [_ApplyMultiplexStep(tolerance, disentanglingZ, PauliZ, (rngControl, idxTarget), _)]; } if (_AnyOutsideToleranceD(tolerance, disentanglingY)) { - ApproximatelyMultiplexPauli(tolerance, disentanglingY, PauliY, control, target); + set plan += [_ApplyMultiplexStep(tolerance, disentanglingY, PauliY, (rngControl, idxTarget), _)]; } + // target is now in |0> state up to the phase given by arg of newCoefficients. // Continue recursion while there are control qubits. - if (Length(control!) == 0) { + if (_RangeLength(rngControl) == 0) { let (abs, arg) = newCoefficients[0]!; if (AbsD(arg) > tolerance) { - Exp([PauliI], -1.0 * arg, [target]); + set plan += [_ApplyGlobalRotationStep(-1.0 * arg, idxTarget, _)]; } } else { if (_AnyOutsideToleranceCP(tolerance, newCoefficients)) { - let newControl = LittleEndian(Rest(control!)); - let newTarget = (control!)[0]; - _ApproximatelyPrepareArbitraryState(tolerance, newCoefficients, newControl, newTarget); + let newControl = (RangeStart(rngControl) + 1)..RangeStep(rngControl)..RangeEnd(rngControl); + let newTarget = RangeStart(rngControl); + set plan += _ApproximatelyUnprepareArbitraryStatePlan(tolerance, newCoefficients, (newControl, newTarget)); } } + + return plan; } diff --git a/Standard/src/Standard.csproj b/Standard/src/Standard.csproj index 9c4d3cffb3a..c92f4da3449 100644 --- a/Standard/src/Standard.csproj +++ b/Standard/src/Standard.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 @@ -30,7 +30,7 @@ - + diff --git a/Standard/tests/Standard.Tests.csproj b/Standard/tests/Standard.Tests.csproj index 28689687eb9..359cd229d09 100644 --- a/Standard/tests/Standard.Tests.csproj +++ b/Standard/tests/Standard.Tests.csproj @@ -1,4 +1,4 @@ - + From 01dc4e17db64a8cfca6d43fe9690e6ebc1697637 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Sun, 23 Feb 2020 01:20:08 +0000 Subject: [PATCH 3/4] Addressing @bettinaheim's feedback. --- MachineLearning/src/Types.qs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/MachineLearning/src/Types.qs b/MachineLearning/src/Types.qs index 39428cd654a..5166302d0aa 100644 --- a/MachineLearning/src/Types.qs +++ b/MachineLearning/src/Types.qs @@ -211,10 +211,6 @@ namespace Microsoft.Quantum.MachineLearning { VerboseMessage: (String -> Unit) ); - // Used only to populate default training options to ignore verbose - // messages. - function _Ignored(value : String) : Unit {} - /// # Summary /// Returns a default set of options for training classifiers. /// @@ -232,7 +228,7 @@ namespace Microsoft.Quantum.MachineLearning { function DefaultTrainingOptions() : TrainingOptions { return TrainingOptions( 0.1, 0.005, 15, 10000, 16, 8, 0.01, - _Ignored + Ignore ); } From bdc04baac085a0708464ec8439255b824ad0c016 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Sun, 23 Feb 2020 01:32:02 +0000 Subject: [PATCH 4/4] Fix type of Ignore. --- MachineLearning/src/Types.qs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MachineLearning/src/Types.qs b/MachineLearning/src/Types.qs index 5166302d0aa..25b15e6de69 100644 --- a/MachineLearning/src/Types.qs +++ b/MachineLearning/src/Types.qs @@ -228,7 +228,7 @@ namespace Microsoft.Quantum.MachineLearning { function DefaultTrainingOptions() : TrainingOptions { return TrainingOptions( 0.1, 0.005, 15, 10000, 16, 8, 0.01, - Ignore + Ignore ); }