From ee3fb7edcf17db2d3f9e20c424f7459da55aed7b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 9 Dec 2019 16:42:02 -0800 Subject: [PATCH 01/21] Begin simplifying training API. --- MachineLearning/src/Runtime/Circuits.qs | 49 +------- MachineLearning/src/Runtime/Deprecated.qs | 88 ++++++------- MachineLearning/src/Runtime/Training.qs | 146 ++++++++++++---------- MachineLearning/src/Runtime/Types.qs | 13 ++ 4 files changed, 137 insertions(+), 159 deletions(-) diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index 49df95cd18e..622e69da5bb 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -332,55 +332,12 @@ namespace Microsoft.Quantum.MachineLearning { /// (when it falls on the tail end of the list of 'locations') /// function ExtractMiniBatch(size: Int, ixLoc: Int, locations: Int[], samples: LabeledSample[]): LabeledSample[] { - mutable cnt = Length(locations)-ixLoc; - if (cnt > size) - { - set cnt = size; - } + let cnt = MinI(size, Length(locations) - ixLoc); mutable rgSamples = new LabeledSample[0]; - if (cnt > 0) - { - set rgSamples = new LabeledSample[cnt]; - for (isa in 0..(cnt-1)) - { - set rgSamples w/=isa<- samples[locations[ixLoc+isa]]; - } + for (location in locations[ixLoc...]) { + set rgSamples += [samples[location]]; } return rgSamples; } - /// # Summary - /// (Randomly) inflate of deflate the source number - operation randomize(src : Double, relativeFuzz : Double) : Double { - return src * ( - 1.0 + relativeFuzz * (Random([0.5, 0.5]) > 0 ? 1.0 | -1.0) - ); - } - - - - /// Summary - /// One possible C#-friendly wrap around the StochasticTrainingLoop - /// - operation StochasticTrainingLoopPlainAdapter(vectors: Double[][], labels: Int[], sched: Int[][], schedScore: Int[][], periodScore: Int, - miniBatchSize: Int, param: Double[],gates: Int[][], bias: Double, lrate: Double, maxEpochs: Int, tol: Double, measCount: Int ) : Double[] // - { - let samples = unFlattenLabeledSamples(vectors,labels); - let sch = unFlattenSchedule(sched); - let schScore = unFlattenSchedule(sched); - let gts = unFlattenGateSequence(gates); - let ((h,m),(b,parpar)) = StochasticTrainingLoop(samples, sch, schScore, periodScore, - miniBatchSize, param, gts, bias, lrate, maxEpochs, tol, measCount); - mutable ret = new Double[Length(parpar)+3]; - set ret w/=0<-IntAsDouble (h); - set ret w/=1<-IntAsDouble (m); - set ret w/=2<-b; - for (j in 0..(Length(parpar)-1)) - { - set ret w/=(j+3)<-parpar[j]; - } - return ret; - } - - } diff --git a/MachineLearning/src/Runtime/Deprecated.qs b/MachineLearning/src/Runtime/Deprecated.qs index a27d203ba34..fd0b607d614 100644 --- a/MachineLearning/src/Runtime/Deprecated.qs +++ b/MachineLearning/src/Runtime/Deprecated.qs @@ -3,45 +3,45 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Arithmetic; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Math; + open Microsoft.Quantum.Math; - /// Sample container access method + /// Sample container access method @Deprecated("") - function getSample(samples: LabeledSampleContainer, ix: Int): LabeledSample { - return (samples!)[ix]; - } + function getSample(samples: LabeledSampleContainer, ix: Int): LabeledSample { + return (samples!)[ix]; + } - /// Access the raw data in a labeled sample + /// Access the raw data in a labeled sample @Deprecated("") - function getData(samp: LabeledSample): Double[] { - return Fst(samp!); - } + function getData(samp: LabeledSample): Double[] { + return Fst(samp!); + } - /// Access the label in a labeled sample + /// Access the label in a labeled sample @Deprecated("") - function getLabel(samp:LabeledSample) : Int - { - return Snd(samp!); - } + function getLabel(samp:LabeledSample) : Int + { + return Snd(samp!); + } - /// Abstraction for a container of labeled samples + /// Abstraction for a container of labeled samples @Deprecated("") - newtype LabeledSampleContainer = LabeledSample[]; + newtype LabeledSampleContainer = LabeledSample[]; @Deprecated("Microsoft.Quantum.Diagnostics.DumpRegister") - function dumpRegisterToConsole ( qs: Qubit[]) : Unit - {} - //{DumpRegister((),qs);} //Swap for empty body when some dumping of registers is needed + function dumpRegisterToConsole ( qs: Qubit[]) : Unit + {} + //{DumpRegister((),qs);} //Swap for empty body when some dumping of registers is needed @Deprecated("Microsoft.Quantum.MachineLearning.NQubitsRequired") function qubitSpan(seq : GateSequence) : Int { return NQubitsRequired(seq); } - /// Set force a qubit into a desired basis state + /// Set force a qubit into a desired basis state @Deprecated("Microsoft.Quantum.Measurement.SetToBasisState") - operation Set (desired: Result, q1: Qubit) : Unit + operation Set (desired: Result, q1: Qubit) : Unit { //body //{ @@ -54,31 +54,31 @@ namespace Microsoft.Quantum.MachineLearning { } @Deprecated("Microsoft.Quantum.Math.SquaredNorm") - function squareNorm(v:Double[]):Double - { - mutable ret = 0.0; - for (u in v) - { - set ret = ret + u*u; - } - return ret; - } + function squareNorm(v:Double[]):Double + { + mutable ret = 0.0; + for (u in v) + { + set ret = ret + u*u; + } + return ret; + } - @Deprecated("") // replace with ForEach. - operation randomizeArray(src:Double[], relativeFuzz: Double) : Double[] - { - mutable ret = new Double[Length(src)]; - for (ix in 0..(Length(src)-1)) - { - set ret w/=ix<-randomize(src[ix], relativeFuzz); - } - return ret; - } + @Deprecated("") // replace with ForEach. + operation randomizeArray(src:Double[], relativeFuzz: Double) : Double[] + { + mutable ret = new Double[Length(src)]; + for (ix in 0..(Length(src)-1)) + { + set ret w/=ix <- _RandomlyRescale(src[ix], relativeFuzz); + } + return ret; + } - @Deprecated("Microsoft.Quantum.Math.NearlyEqualD") - function nearIdenticalDoubles(x:Double,y:Double):Bool { - return NearlyEqualD(x, y); //Note key tolerance constant here - } + @Deprecated("Microsoft.Quantum.Math.NearlyEqualD") + function nearIdenticalDoubles(x:Double,y:Double):Bool { + return NearlyEqualD(x, y); //Note key tolerance constant here + } } diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index a6072d499da..a2996ba6284 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -51,13 +51,9 @@ namespace Microsoft.Quantum.MachineLearning { gates: GateSequence, parameterSource: Double[][], samples: LabeledSample[], + options : TrainingOptions, trainingSchedule: SamplingSchedule, - validationSchedule: SamplingSchedule, - learningRate: Double, - tolerance: Double, - miniBatchSize: Int, - maxEpochs: Int, - nMeasurements: Int + validationSchedule: SamplingSchedule ) : (Double[], Double) { mutable retParam = [-1E12]; mutable retBias = -2.0; //Indicates non-informative start @@ -69,19 +65,18 @@ namespace Microsoft.Quantum.MachineLearning { for (idxStart in 0..(Length(parameterSource) - 1)) { Message($"Beginning training at start point #{idxStart}..."); let ((h, m), (b, parpar)) = StochasticTrainingLoop( - samples, trainingSchedule, trainingSchedule, 1, miniBatchSize, - parameterSource[idxStart], gates, 0.0, learningRate, maxEpochs, - tolerance, nMeasurements + samples, trainingSchedule, trainingSchedule, options, 1, + parameterSource[idxStart], gates, 0.0 ); let probsValidation = EstimateClassificationProbabilitiesClassicalData( - tolerance, features, validationSchedule, nQubits, - gates, parpar, nMeasurements + options::Tolerance, features, validationSchedule, nQubits, + gates, parpar, options::NMeasurements ); // Find the best bias for the new classification parameters. let localBias = _UpdatedBias( Zip(probsValidation, Sampled(validationSchedule, labels)), 0.0, - tolerance + options::Tolerance ); let localPL = InferredLabels(localBias, probsValidation); let localMisses = NMismatches(localPL, Sampled(validationSchedule, labels)); @@ -148,11 +143,11 @@ namespace Microsoft.Quantum.MachineLearning { let sch = unFlattenSchedule(trainingSchedule); let schValidate = unFlattenSchedule(validationSchedule); let gateSequence = unFlattenGateSequence(gates); + let options = TrainingOptions(learningRate, tolerance, miniBatchSize, nMeasurements, maxEpochs); return TrainSequentialClassifier( nQubits, gateSequence, parameterSource, samples, - sch, schValidate, learningRate, tolerance, miniBatchSize, - maxEpochs, nMeasurements + options, sch, schValidate ); } //TrainQcccSequential @@ -261,7 +256,7 @@ namespace Microsoft.Quantum.MachineLearning { for (ixLoc in 0..miniBatchSize..(Length(missLocations) - 1)) { let miniBatch = ExtractMiniBatch(miniBatchSize, ixLoc, missLocations, samples); let (utility,upParam) = OneStochasticTrainingStep(tolerance, miniBatch, paramCurrent, gates, lrate, measCount); - if (Microsoft.Quantum.Math.AbsD(utility) > 0.0000001) { + if (AbsD(utility) > 0.0000001) { //There had been some parameter update if (utility > 0.0) { //good parameter update set paramCurrent = upParam; @@ -325,6 +320,14 @@ namespace Microsoft.Quantum.MachineLearning { return ((hCur, mCur),(biasCurrent,paramCurrent)); } //OneUncontrolledStochasticTrainingEpoch + /// # Summary + /// Randomly rescales an input to either grow or shrink by a given factor. + operation _RandomlyRescale(scale : Double, value : Double) : Double { + return value * ( + 1.0 + scale * (Random([0.5, 0.5]) > 0 ? 1.0 | -1.0) + ); + } + /// # Summary /// Run a full circuit training loop on a subset of data samples /// @@ -369,78 +372,83 @@ namespace Microsoft.Quantum.MachineLearning { /// # Output /// ((no.hits,no.misses),(opt.bias,opt.parameters)) /// - operation StochasticTrainingLoop(samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, periodScore: Int, - miniBatchSizeInital: Int, param: Double[], gates: GateSequence, bias: Double, lrateInitial: Double, maxEpochs: Int, tol: Double, measCount: Int): ((Int,Int),(Double,Double[])) - { - //const - let manyNoops = 4; + operation StochasticTrainingLoop( + samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, + options : TrainingOptions, + periodScore: Int, + param: Double[], gates: GateSequence, + bias: Double + ) + : ((Int, Int),(Double, Double[])) { //const let relFuzz = 0.01; - let HARDCODEDmaxNoops = 2*manyNoops; - mutable pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, measCount); - mutable biasBest = _UpdatedBias(pls, bias, tol); - let (h0, m0) = TallyHitsMisses(pls,biasBest); - mutable hBest = h0; - mutable mBest = m0; + let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, options::NMeasurements); + mutable biasBest = _UpdatedBias(pls, bias, options::Tolerance); + mutable (hBest, mBest) = TallyHitsMisses(pls, biasBest); mutable paramBest = param; mutable paramCurrent = param; mutable biasCurrent = biasBest; //reintroducing learning rate heuristics - mutable lrate = lrateInitial; - mutable batchSize = miniBatchSizeInital; - mutable noopCount = 0; - mutable upBias = biasCurrent; - mutable upParam = paramCurrent; - for (ep in 1..maxEpochs) { - let ((h1,m1),(upB,upP)) = OneStochasticTrainingEpoch(samples, sched, schedScore, periodScore, - batchSize, paramCurrent, gates, biasCurrent, lrate, tol, measCount, hBest, mBest); - set upBias = upB; - set upParam = upP; - if (m1 < mBest) - { + mutable lrate = options::LearningRate; + mutable batchSize = options::MinibatchSize; + + // Keep track of how many times a bias update has stalled out. + mutable nStalls = 0; + + for (ep in 1..options::MaxEpochs) { + let ((h1, m1), (upBias, upParam)) = OneStochasticTrainingEpoch( + samples, sched, schedScore, periodScore, + batchSize, paramCurrent, gates, biasCurrent, lrate, + options::Tolerance, options::NMeasurements, hBest, mBest + ); + if (m1 < mBest) { set hBest = h1; set mBest = m1; set paramBest = upParam; set biasBest = upBias; - if (IntAsDouble (mBest)/IntAsDouble (mBest+hBest)< tol) //Terminate based on tolerance - { - return ((hBest,mBest),(biasBest,paramBest)); + if (IntAsDouble(mBest) / IntAsDouble(mBest + hBest) < options::Tolerance) { //Terminate based on tolerance + return ((hBest, mBest), (biasBest, paramBest)); } - set noopCount = 0; //Reset the counter of consequtive noops - set lrate = lrateInitial; - set batchSize = miniBatchSizeInital; + set nStalls = 0; //Reset the counter of consequtive noops + set lrate = options::LearningRate; + set batchSize = options::MinibatchSize; } - if (NearlyEqualD(biasCurrent,upBias) and _AllNearlyEqualD(paramCurrent,upParam)) - { - set noopCount = noopCount+1; - if (noopCount > manyNoops) - { - if (noopCount > HARDCODEDmaxNoops) - { - return ((hBest,mBest),(biasBest,paramBest)); //Too many non-steps. Continuation makes no sense - } - else - { - set upBias = randomize(upBias, relFuzz); - set upParam = ForEach(randomize(_, relFuzz), upParam); - } + + if (NearlyEqualD(biasCurrent, upBias) and _AllNearlyEqualD(paramCurrent, upParam)) { + set nStalls += 1; + // If we're more than halfway through our maximum allowed number of stalls, + // exit early with the best we actually found. + if (nStalls > options::MaxStalls) { + return ((hBest, mBest), (biasBest, paramBest)); //Too many non-steps. Continuation makes no sense } - set batchSize = noopCount; //batchSize + 1; //Try to fuzz things up with smaller batch count + + // Otherwise, heat up the learning rate and batch size. + set batchSize = nStalls; //batchSize + 1; //Try to fuzz things up with smaller batch count //and heat up a bit - set lrate = 1.25*lrate; - } - else - { - set noopCount = 0; //Reset the counter of consequtive noops - set lrate = lrateInitial; - set batchSize = miniBatchSizeInital; + set lrate *= 1.25; + + // If we stalled out, we'll also randomly rescale our parameters + // and bias before updating. + if (nStalls > options::MaxStalls / 2) { + set biasCurrent = _RandomlyRescale(relFuzz, upBias); + set paramCurrent = ForEach(_RandomlyRescale(relFuzz, _), upParam); + } + } else { + // If we learned successfully this iteration, reset the number of + // stalls so far. + set nStalls = 0; //Reset the counter of consequtive noops + set lrate = options::LearningRate; + set batchSize = options::MinibatchSize; + + // Since we didn't stall out, we can set the parameters and bias + // as normal, without randomizing. + set paramCurrent = upParam; + set biasCurrent = upBias; } - set paramCurrent = upParam; - set biasCurrent = upBias; } - return ((hBest,mBest),(biasBest,paramBest)); + return ((hBest, mBest), (biasBest, paramBest)); } } diff --git a/MachineLearning/src/Runtime/Types.qs b/MachineLearning/src/Runtime/Types.qs index 759acbc4094..79e612e5508 100644 --- a/MachineLearning/src/Runtime/Types.qs +++ b/MachineLearning/src/Runtime/Types.qs @@ -89,6 +89,19 @@ namespace Microsoft.Quantum.MachineLearning { NMisclassifications: Int ); + newtype TrainingOptions = ( + LearningRate: Double, + Tolerance: Double, + MinibatchSize: Int, + NMeasurements: Int, + MaxEpochs: Int, + MaxStalls: Int + ); + function DefaultTrainingOptions() : TrainingOptions { + return TrainingOptions( + 0.1, 0.005, 15, 10000, 16, 8 + ); + } } From 343eeb88e9b5dd4c8fb4fa8de9bd0152d64b5df0 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 9 Dec 2019 16:44:08 -0800 Subject: [PATCH 02/21] Fix for TrainQcccSequential. --- MachineLearning/src/Runtime/Training.qs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index a2996ba6284..aa52bc22520 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -143,7 +143,12 @@ namespace Microsoft.Quantum.MachineLearning { let sch = unFlattenSchedule(trainingSchedule); let schValidate = unFlattenSchedule(validationSchedule); let gateSequence = unFlattenGateSequence(gates); - let options = TrainingOptions(learningRate, tolerance, miniBatchSize, nMeasurements, maxEpochs); + let options = DefaultTrainingOptions() + w/ LearningRate <- learningRate + w/ Tolerance <- tolerance + w/ MinibatchSize <- miniBatchSize + w/ NMeasurements <- nMeasurements + w/ MaxEpochs <- maxEpochs; return TrainSequentialClassifier( nQubits, gateSequence, parameterSource, samples, From 547c893f92fa99b9f5bd87c8ec0a673020f23e71 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Mon, 9 Dec 2019 16:46:52 -0800 Subject: [PATCH 03/21] Simplify OneStochasticTrainingEpoch. --- MachineLearning/src/Runtime/Training.qs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index aa52bc22520..54ecb60e98d 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -238,8 +238,8 @@ namespace Microsoft.Quantum.MachineLearning { /// ## measCount /// number of true quantum measurements to estimate probabilities. /// - operation OneStochasticTrainingEpoch(samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, periodScore: Int, - miniBatchSize: Int, param: Double[], gates: GateSequence, bias: Double, lrate: Double, tolerance: Double, measCount: Int, + operation OneStochasticTrainingEpoch(samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, periodScore: Int, options : TrainingOptions, + param: Double[], gates: GateSequence, bias: Double, h0: Int, m0: Int): ((Int,Int),(Double,Double[])) { let HARDCODEDunderage = 3; //4/26 slack greater than 3 is not recommended @@ -249,7 +249,7 @@ namespace Microsoft.Quantum.MachineLearning { mutable mBest = m0; mutable biasBest = bias; - let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, measCount); + let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, options::NMeasurements); let (h2,m2) = TallyHitsMisses(pls,biasBest); let missLocations = MissLocations(schedScore, pls, biasBest); @@ -258,15 +258,15 @@ namespace Microsoft.Quantum.MachineLearning { mutable biasCurrent = biasBest; //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters - for (ixLoc in 0..miniBatchSize..(Length(missLocations) - 1)) { - let miniBatch = ExtractMiniBatch(miniBatchSize, ixLoc, missLocations, samples); - let (utility,upParam) = OneStochasticTrainingStep(tolerance, miniBatch, paramCurrent, gates, lrate, measCount); + for (ixLoc in 0..options::MinibatchSize..(Length(missLocations) - 1)) { + let miniBatch = ExtractMiniBatch(options::MinibatchSize, ixLoc, missLocations, samples); + let (utility,upParam) = OneStochasticTrainingStep(options::Tolerance, miniBatch, paramCurrent, gates, options::LearningRate, options::NMeasurements); if (AbsD(utility) > 0.0000001) { //There had been some parameter update if (utility > 0.0) { //good parameter update set paramCurrent = upParam; - let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedScore, paramCurrent, gates, measCount); - set biasCurrent = _UpdatedBias(plsCurrent, bias, tolerance); + let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedScore, paramCurrent, gates, options::NMeasurements); + set biasCurrent = _UpdatedBias(plsCurrent, bias, options::Tolerance); let (h1,m1) = TallyHitsMisses(plsCurrent,biasCurrent); if (m1 < mBest + HARDCODEDunderage) { //we allow limited non-greediness @@ -404,8 +404,10 @@ namespace Microsoft.Quantum.MachineLearning { for (ep in 1..options::MaxEpochs) { let ((h1, m1), (upBias, upParam)) = OneStochasticTrainingEpoch( samples, sched, schedScore, periodScore, - batchSize, paramCurrent, gates, biasCurrent, lrate, - options::Tolerance, options::NMeasurements, hBest, mBest + options + w/ LearningRate <- lrate + w/ MinibatchSize <- batchSize, + paramCurrent, gates, biasCurrent, hBest, mBest ); if (m1 < mBest) { set hBest = h1; From a816c55fb6491bef5da886415e87c2e38b66907c Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 10 Dec 2019 13:29:21 -0800 Subject: [PATCH 04/21] Simplified training API a little more. --- MachineLearning/src/Runtime/Training.qs | 39 +++++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index 54ecb60e98d..b1b3456ec8a 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -179,10 +179,11 @@ namespace Microsoft.Quantum.MachineLearning { /// (utility, (new)parameters) pair /// operation OneStochasticTrainingStep( - tolerance: Double, miniBatch: LabeledSample[], param: Double[], gates: GateSequence, - lrate: Double, measCount: Int - ) : (Double, Double[]) { - mutable upParam = new Double[Length(param)]; + miniBatch : LabeledSample[], + options : TrainingOptions, + param : Double[], gates : GateSequence + ) + : (Double, Double[]) { mutable batchGradient = ConstantArray(Length(param), 0.0); for (samp in miniBatch) { @@ -190,17 +191,19 @@ namespace Microsoft.Quantum.MachineLearning { if (err < 1.0) { set err = -1.0; //class 0 misclassified to class 1; strive to reduce the probability } - let grad = EstimateGradientFromClassicalSample(tolerance, param, gates, samp::Features, measCount); + let grad = EstimateGradientFromClassicalSample( + options::Tolerance, param, gates, samp::Features, + options::NMeasurements + ); for (ip in 0..(Length(param) - 1)) { // GradientClassicalSample actually computes antigradient, but err*grad corrects it back to gradient - set batchGradient w/= ip <- (batchGradient[ip] + lrate * err * grad[ip]); + set batchGradient w/= ip <- (batchGradient[ip] + options::LearningRate * err * grad[ip]); } } - for (ip in 0..(Length(param)-1)) { - set upParam w/= ip <- (param[ip] + batchGradient[ip]); - } - return (SquaredNorm(batchGradient), upParam); //TODO:REVIEW: Ok to interpret utility as size of the overall move? + let updatedParameters = Mapped(PlusD, Zip(param, batchGradient)); + // TODO:REVIEW: Ok to interpret utility as size of the overall move? + return (SquaredNorm(batchGradient), updatedParameters); } @@ -238,11 +241,15 @@ namespace Microsoft.Quantum.MachineLearning { /// ## measCount /// number of true quantum measurements to estimate probabilities. /// - operation OneStochasticTrainingEpoch(samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, periodScore: Int, options : TrainingOptions, - param: Double[], gates: GateSequence, bias: Double, - h0: Int, m0: Int): ((Int,Int),(Double,Double[])) - { - let HARDCODEDunderage = 3; //4/26 slack greater than 3 is not recommended + operation OneStochasticTrainingEpoch( + samples: LabeledSample[], sched: SamplingSchedule, + schedScore: SamplingSchedule, periodScore: Int, + options : TrainingOptions, + param: Double[], gates: GateSequence, bias: Double, + h0: Int, m0: Int + ) + : ((Int, Int), (Double, Double[])) { + let HARDCODEDunderage = 3; // 4/26 slack greater than 3 is not recommended mutable hBest = h0; @@ -260,7 +267,7 @@ namespace Microsoft.Quantum.MachineLearning { //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters for (ixLoc in 0..options::MinibatchSize..(Length(missLocations) - 1)) { let miniBatch = ExtractMiniBatch(options::MinibatchSize, ixLoc, missLocations, samples); - let (utility,upParam) = OneStochasticTrainingStep(options::Tolerance, miniBatch, paramCurrent, gates, options::LearningRate, options::NMeasurements); + let (utility, upParam) = OneStochasticTrainingStep(miniBatch, options, paramCurrent, gates); if (AbsD(utility) > 0.0000001) { //There had been some parameter update if (utility > 0.0) { //good parameter update From ed30a10c0f9640dfce8bc9d35fdfb5e0610d9f2f Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 10 Dec 2019 13:41:44 -0800 Subject: [PATCH 05/21] Remove unused operation. --- MachineLearning/src/Runtime/Training.qs | 37 ------------------------- 1 file changed, 37 deletions(-) diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index b1b3456ec8a..d8e2fd514ec 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -295,43 +295,6 @@ namespace Microsoft.Quantum.MachineLearning { } return ((hBest, mBest), (biasBest, paramBest)); } - - //Make some oblivious gradien descent steps without checking the prediction quality - operation OneUncontrolledStochasticTrainingEpoch(samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, periodScore: Int, - miniBatchSize: Int, param: Double[], gates: GateSequence, bias: Double, lrate: Double, tolerance: Double, measCount: Int): ((Int,Int),(Double,Double[])) - { - let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, measCount); - mutable biasBest = _UpdatedBias(pls, bias, tolerance); - let (h0,m0) = TallyHitsMisses(pls,biasBest); // ClassificationScoreSimulated(samples, schedScore, param, gates, bias); //Deprecated - mutable hCur = h0; - mutable mCur = m0; - let missLocations = MissLocations(schedScore, pls, biasBest); - - mutable paramBest = param; - mutable paramCurrent = paramBest; - mutable biasCurrent = biasBest; - - //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters - for (ixLoc in 0..miniBatchSize..(Length(missLocations) - 1)) { - let miniBatch = ExtractMiniBatch(miniBatchSize,ixLoc,missLocations,samples); - let (utility,upParam) = OneStochasticTrainingStep(tolerance, miniBatch, paramCurrent, gates, lrate, measCount); - if (AbsD(utility) > 0.0000001) { - //There had been some parameter update - if (utility > 0.0) { //good parameter update - set paramCurrent = upParam; - let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedScore, paramCurrent, gates, measCount); - set biasCurrent = _UpdatedBias(plsCurrent, bias, tolerance); - let (h1,m1) = TallyHitsMisses(plsCurrent,biasCurrent); - set hCur = h1; - set mCur = m1; - } - - } - - } - return ((hCur, mCur),(biasCurrent,paramCurrent)); - } //OneUncontrolledStochasticTrainingEpoch - /// # Summary /// Randomly rescales an input to either grow or shrink by a given factor. operation _RandomlyRescale(scale : Double, value : Double) : Double { From 1a0b1400101331df031f65a72a6540329ad4e2fd Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 10 Dec 2019 17:50:26 -0800 Subject: [PATCH 06/21] Started Chunks function. --- Standard/src/Arrays/Arrays.qs | 26 ++++++++++++++++++++++++++ Standard/tests/ArrayTests.qs | 24 ++++++++++++++++++++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Standard/src/Arrays/Arrays.qs b/Standard/src/Arrays/Arrays.qs index 13a111729e5..c09b822afab 100644 --- a/Standard/src/Arrays/Arrays.qs +++ b/Standard/src/Arrays/Arrays.qs @@ -249,6 +249,32 @@ namespace Microsoft.Quantum.Arrays { | inputArray + padArray; // Padded at tail. } + + /// # Summary + /// Splits an array into multiple parts of equal length. + /// + /// # Input + /// ## nElements + /// The length of each chunk. + /// ## arr + /// The array to be split. + /// + /// # Output + /// A array containing each chunk of the original array. + /// + /// # Remarks + /// Note that the last element of the output may be shorter + /// than `nElements` if `Length(arr)` is not divisible by `nElements`. + function Chunks<'T>(nElements : Int, arr : 'T[]) : 'T[][] { + mutable output = new 'T[][0]; + mutable remaining = arr; + while (not IsEmpty(remaining)) { + set output += [remaining[...nElements - 1]]; + set remaining = remaining[nElements...]; + } + return output; + } + /// # Summary /// Splits an array into multiple parts. /// diff --git a/Standard/tests/ArrayTests.qs b/Standard/tests/ArrayTests.qs index 256b42468dd..88972b5e91c 100644 --- a/Standard/tests/ArrayTests.qs +++ b/Standard/tests/ArrayTests.qs @@ -1,13 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. namespace Microsoft.Quantum.Tests { + open Microsoft.Quantum.Logical; open Microsoft.Quantum.Diagnostics; open Microsoft.Quantum.Canon; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Arrays; - - function ZipTest () : Unit { + @Test("QuantumSimulator") + function ZipTest() : Unit { let left = [1, 2, 101]; let right = [PauliY, PauliI]; @@ -26,6 +27,7 @@ namespace Microsoft.Quantum.Tests { } + @Test("QuantumSimulator") function LookupTest () : Unit { let array = [1, 12, 71, 103]; @@ -38,8 +40,22 @@ namespace Microsoft.Quantum.Tests { EqualityFactI(fn(1), 12, $"fn(1) did not return array[1]"); } + function _AllEqualI(expected : Int[], actual : Int[]) : Bool { + return All(EqualI, Zip(expected, actual)); + } + + @Test("QuantumSimulator") + function ChunksTest() : Unit { + let data = [10, 11, 12, 13, 14, 15]; + + // 2 × 3 case. + Fact(All(_AllEqualI, Zip( + [[10, 11], [12, 13], [14, 15]], + Chunks(2, data) + )), "Wrong chunks in 2x3 case."); + } - function ConstantArrayTestHelper (x : Int) : Int { + function _Squared(x : Int) : Int { return x * x; } @@ -52,7 +68,7 @@ namespace Microsoft.Quantum.Tests { let ignore = Mapped(NearEqualityFactD(_, 2.17), dblArray); // Stress test by making an array of Int -> Int. - let fnArray = ConstantArray(7, ConstantArrayTestHelper); + let fnArray = ConstantArray(7, _Squared); EqualityFactI(Length(fnArray), 7, $"ConstantArray(Int, Int -> Int) had the wrong length."); EqualityFactI(fnArray[3](7), 49, $"ConstantArray(Int, Int -> Int) had the wrong value."); } From 8a7d1426efc84af38bd941d17b290c9d4467c7e9 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Tue, 10 Dec 2019 17:51:30 -0800 Subject: [PATCH 07/21] Began removing ExtractMiniBatch. --- MachineLearning/src/Runtime/Circuits.qs | 2 +- MachineLearning/src/Runtime/Training.qs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index 622e69da5bb..7cf4cf2d872 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -334,7 +334,7 @@ namespace Microsoft.Quantum.MachineLearning { function ExtractMiniBatch(size: Int, ixLoc: Int, locations: Int[], samples: LabeledSample[]): LabeledSample[] { let cnt = MinI(size, Length(locations) - ixLoc); mutable rgSamples = new LabeledSample[0]; - for (location in locations[ixLoc...]) { + for (location in locations[ixLoc..ixLoc + cnt]) { set rgSamples += [samples[location]]; } return rgSamples; diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index d8e2fd514ec..e8f32f622a8 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -265,9 +265,9 @@ namespace Microsoft.Quantum.MachineLearning { mutable biasCurrent = biasBest; //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters - for (ixLoc in 0..options::MinibatchSize..(Length(missLocations) - 1)) { - let miniBatch = ExtractMiniBatch(options::MinibatchSize, ixLoc, missLocations, samples); - let (utility, upParam) = OneStochasticTrainingStep(miniBatch, options, paramCurrent, gates); + let minibatches = Mapped(Subarray(_, samples), Chunks(options::MinibatchSize, missLocations)); + for (minibatch in minibatches) { + let (utility, upParam) = OneStochasticTrainingStep(minibatch, options, paramCurrent, gates); if (AbsD(utility) > 0.0000001) { //There had been some parameter update if (utility > 0.0) { //good parameter update From 99953698d04425f2664895ce4e2ad6b7045807cb Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 12:42:43 -0800 Subject: [PATCH 08/21] Simplify training API with new SequentialModel UDT. --- MachineLearning/src/Runtime/Circuits.qs | 1 + MachineLearning/src/Runtime/Training.qs | 136 ++++++++++++------------ MachineLearning/src/Runtime/Types.qs | 5 + 3 files changed, 73 insertions(+), 69 deletions(-) diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index 7cf4cf2d872..f0725f25765 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -331,6 +331,7 @@ namespace Microsoft.Quantum.MachineLearning { /// the resulting mini batch can be occasionally shorter than the requested 'size' /// (when it falls on the tail end of the list of 'locations') /// + @Deprecated("Microsoft.Quantum.Arrays.Chunks") function ExtractMiniBatch(size: Int, ixLoc: Int, locations: Int[], samples: LabeledSample[]): LabeledSample[] { let cnt = MinI(size, Length(locations) - ixLoc); mutable rgSamples = new LabeledSample[0]; diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index e8f32f622a8..790a4d626e0 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -54,9 +54,8 @@ namespace Microsoft.Quantum.MachineLearning { options : TrainingOptions, trainingSchedule: SamplingSchedule, validationSchedule: SamplingSchedule - ) : (Double[], Double) { - mutable retParam = [-1E12]; - mutable retBias = -2.0; //Indicates non-informative start + ) : SequentialModel { + mutable bestSoFar = SequentialModel([-1E12], -2.0); mutable bestValidation = Length(samples) + 1; let features = Mapped(_Features, samples); @@ -64,13 +63,13 @@ namespace Microsoft.Quantum.MachineLearning { for (idxStart in 0..(Length(parameterSource) - 1)) { Message($"Beginning training at start point #{idxStart}..."); - let ((h, m), (b, parpar)) = StochasticTrainingLoop( - samples, trainingSchedule, trainingSchedule, options, 1, - parameterSource[idxStart], gates, 0.0 + let ((h, m), proposedUpdate) = StochasticTrainingLoop( + gates, SequentialModel(parameterSource[idxStart], 0.0), + samples, options, trainingSchedule, 1 ); let probsValidation = EstimateClassificationProbabilitiesClassicalData( options::Tolerance, features, validationSchedule, nQubits, - gates, parpar, options::NMeasurements + gates, proposedUpdate::Parameters, options::NMeasurements ); // Find the best bias for the new classification parameters. let localBias = _UpdatedBias( @@ -82,12 +81,11 @@ namespace Microsoft.Quantum.MachineLearning { let localMisses = NMismatches(localPL, Sampled(validationSchedule, labels)); if (bestValidation > localMisses) { set bestValidation = localMisses; - set retParam = parpar; - set retBias = localBias; + set bestSoFar = proposedUpdate; } } - return (retParam, retBias); + return bestSoFar; } /// # Summary @@ -150,10 +148,10 @@ namespace Microsoft.Quantum.MachineLearning { w/ NMeasurements <- nMeasurements w/ MaxEpochs <- maxEpochs; - return TrainSequentialClassifier( + return (TrainSequentialClassifier( nQubits, gateSequence, parameterSource, samples, options, sch, schValidate - ); + ))!; } //TrainQcccSequential /// # Summary @@ -242,59 +240,55 @@ namespace Microsoft.Quantum.MachineLearning { /// number of true quantum measurements to estimate probabilities. /// operation OneStochasticTrainingEpoch( - samples: LabeledSample[], sched: SamplingSchedule, - schedScore: SamplingSchedule, periodScore: Int, + samples: LabeledSample[], + schedule: SamplingSchedule, periodScore: Int, options : TrainingOptions, - param: Double[], gates: GateSequence, bias: Double, + model : SequentialModel, gates: GateSequence, h0: Int, m0: Int ) - : ((Int, Int), (Double, Double[])) { + : ((Int, Int), SequentialModel) { let HARDCODEDunderage = 3; // 4/26 slack greater than 3 is not recommended mutable hBest = h0; mutable mBest = m0; - mutable biasBest = bias; - - let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, options::NMeasurements); - let (h2,m2) = TallyHitsMisses(pls,biasBest); - let missLocations = MissLocations(schedScore, pls, biasBest); + mutable bestSoFar = model; - mutable paramBest = param; - mutable paramCurrent = paramBest; - mutable biasCurrent = biasBest; + let pls = ClassificationProbabilitiesClassicalData( + samples, schedule, model::Parameters, gates, options::NMeasurements + ); + let missLocations = MissLocations(schedule, pls, model::Bias); //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters let minibatches = Mapped(Subarray(_, samples), Chunks(options::MinibatchSize, missLocations)); for (minibatch in minibatches) { - let (utility, upParam) = OneStochasticTrainingStep(minibatch, options, paramCurrent, gates); - if (AbsD(utility) > 0.0000001) { - //There had been some parameter update - if (utility > 0.0) { //good parameter update - set paramCurrent = upParam; - let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedScore, paramCurrent, gates, options::NMeasurements); - set biasCurrent = _UpdatedBias(plsCurrent, bias, options::Tolerance); - let (h1,m1) = TallyHitsMisses(plsCurrent,biasCurrent); - if (m1 < mBest + HARDCODEDunderage) { - //we allow limited non-greediness - if (m1 < mBest) { - set hBest = h1; - set mBest = m1; - set paramBest = paramCurrent; - set biasBest = biasCurrent; - } - } else { - //otherwise we scrap the parameter update - set paramCurrent = paramBest; - set biasCurrent = biasBest; + let (utility, updatedParameters) = OneStochasticTrainingStep( + minibatch, options, bestSoFar::Parameters, gates + ); + if (utility > 0.0000001) { + // There has been some good parameter update. + // Check if it actually improves things, and if so, + // commit it. + let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedule, updatedParameters, gates, options::NMeasurements); + let updatedBias = _UpdatedBias( + plsCurrent, model::Bias, options::Tolerance + ); + let (h1, m1) = TallyHitsMisses(plsCurrent, updatedBias); + if (m1 < mBest + HARDCODEDunderage) { + //we allow limited non-greediness + if (m1 < mBest) { + set hBest = h1; + set mBest = m1; + set bestSoFar = SequentialModel(updatedParameters, updatedBias); } } } } - return ((hBest, mBest), (biasBest, paramBest)); + return ((hBest, mBest), bestSoFar); } + /// # Summary /// Randomly rescales an input to either grow or shrink by a given factor. operation _RandomlyRescale(scale : Double, value : Double) : Double { @@ -348,21 +342,23 @@ namespace Microsoft.Quantum.MachineLearning { /// ((no.hits,no.misses),(opt.bias,opt.parameters)) /// operation StochasticTrainingLoop( - samples: LabeledSample[], sched: SamplingSchedule, schedScore: SamplingSchedule, + gates: GateSequence, + model : SequentialModel, + samples: LabeledSample[], options : TrainingOptions, - periodScore: Int, - param: Double[], gates: GateSequence, - bias: Double + schedule: SamplingSchedule, + periodScore: Int ) - : ((Int, Int),(Double, Double[])) { + : ((Int, Int), SequentialModel) { //const let relFuzz = 0.01; - let pls = ClassificationProbabilitiesClassicalData(samples, schedScore, param, gates, options::NMeasurements); - mutable biasBest = _UpdatedBias(pls, bias, options::Tolerance); - mutable (hBest, mBest) = TallyHitsMisses(pls, biasBest); - mutable paramBest = param; - mutable paramCurrent = param; - mutable biasCurrent = biasBest; + let pls = ClassificationProbabilitiesClassicalData( + samples, schedule, model::Parameters, gates, options::NMeasurements + ); + mutable bestSoFar = model + w/ Bias <- _UpdatedBias(pls, model::Bias, options::Tolerance); + mutable (hBest, mBest) = TallyHitsMisses(pls, model::Bias); + mutable current = bestSoFar; //reintroducing learning rate heuristics mutable lrate = options::LearningRate; @@ -372,32 +368,33 @@ namespace Microsoft.Quantum.MachineLearning { mutable nStalls = 0; for (ep in 1..options::MaxEpochs) { - let ((h1, m1), (upBias, upParam)) = OneStochasticTrainingEpoch( - samples, sched, schedScore, periodScore, + let ((h1, m1), proposedUpdate) = OneStochasticTrainingEpoch( + samples, schedule, periodScore, options w/ LearningRate <- lrate w/ MinibatchSize <- batchSize, - paramCurrent, gates, biasCurrent, hBest, mBest + current, gates, hBest, mBest ); if (m1 < mBest) { set hBest = h1; set mBest = m1; - set paramBest = upParam; - set biasBest = upBias; + set bestSoFar = proposedUpdate; if (IntAsDouble(mBest) / IntAsDouble(mBest + hBest) < options::Tolerance) { //Terminate based on tolerance - return ((hBest, mBest), (biasBest, paramBest)); + return ((hBest, mBest), bestSoFar); } set nStalls = 0; //Reset the counter of consequtive noops set lrate = options::LearningRate; set batchSize = options::MinibatchSize; } - if (NearlyEqualD(biasCurrent, upBias) and _AllNearlyEqualD(paramCurrent, upParam)) { + if ( + 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, // exit early with the best we actually found. if (nStalls > options::MaxStalls) { - return ((hBest, mBest), (biasBest, paramBest)); //Too many non-steps. Continuation makes no sense + return ((hBest, mBest), bestSoFar); //Too many non-steps. Continuation makes no sense } // Otherwise, heat up the learning rate and batch size. @@ -408,8 +405,10 @@ namespace Microsoft.Quantum.MachineLearning { // If we stalled out, we'll also randomly rescale our parameters // and bias before updating. if (nStalls > options::MaxStalls / 2) { - set biasCurrent = _RandomlyRescale(relFuzz, upBias); - set paramCurrent = ForEach(_RandomlyRescale(relFuzz, _), upParam); + set current = SequentialModel( + ForEach(_RandomlyRescale(relFuzz, _), proposedUpdate::Parameters), + _RandomlyRescale(relFuzz, proposedUpdate::Bias) + ); } } else { // If we learned successfully this iteration, reset the number of @@ -420,12 +419,11 @@ namespace Microsoft.Quantum.MachineLearning { // Since we didn't stall out, we can set the parameters and bias // as normal, without randomizing. - set paramCurrent = upParam; - set biasCurrent = upBias; + set current = proposedUpdate; } } - return ((hBest, mBest), (biasBest, paramBest)); + return ((hBest, mBest), bestSoFar); } } diff --git a/MachineLearning/src/Runtime/Types.qs b/MachineLearning/src/Runtime/Types.qs index 79e612e5508..1d0f90b8a60 100644 --- a/MachineLearning/src/Runtime/Types.qs +++ b/MachineLearning/src/Runtime/Types.qs @@ -104,4 +104,9 @@ namespace Microsoft.Quantum.MachineLearning { ); } + newtype SequentialModel = ( + Parameters: Double[], + Bias: Double + ); + } From 0478145b0db3d3d9a8340583ee904e62c9ff1387 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 13:25:54 -0800 Subject: [PATCH 09/21] Further consolidate APIs. --- MachineLearning/src/Runtime/Circuits.qs | 72 +++++-------------------- MachineLearning/src/Runtime/Training.qs | 26 +++++---- 2 files changed, 29 insertions(+), 69 deletions(-) diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index f0725f25765..e400079037f 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -206,30 +206,24 @@ namespace Microsoft.Quantum.MachineLearning { /// /// # Output /// TODO - operation ClassificationProbabilitiesClassicalData(samples: LabeledSample[], sched: SamplingSchedule, param: Double[], gates: GateSequence, nMeasurements: Int): - (Double,Int)[] { - mutable N = IsEmpty(samples) - ? NQubitsRequired(gates) - | MaxI(NQubitsRequired(gates), FeatureRegisterSize(_Features(Head(samples)))); + operation ClassificationProbabilitiesClassicalData( + gates: GateSequence, + param: Double[], + samples: LabeledSample[], + nMeasurements: Int, + schedule: SamplingSchedule + ) + : (Double, Int)[] { mutable ret = new (Double, Int)[0]; - for (rg in sched!) { - for (ix in rg) { - let sample = samples[ix]; - //agnostic w.r.t. simulator (may still be simulable) - let prob1 = EstimateClassificationProbabilityFromSample(1E-12, param, gates, sample::Features, nMeasurements); - set ret += [(prob1, sample::Label)]; - } + for (sample in Sampled(schedule, samples)) { + let prob1 = EstimateClassificationProbabilityFromSample( + 1E-12, param, gates, sample::Features, nMeasurements + ); + set ret += [(prob1, sample::Label)]; } - return ret; } - operation EstimateClassificationProbabilitiesClassicalDataAdapter(tolerance: Double, samples: Double[][], schedule: Int[][], nQubits: Int, gates: Int[][], param: Double[], measCount: Int): Double[] - { - return EstimateClassificationProbabilitiesClassicalData(tolerance, samples, unFlattenSchedule(schedule), nQubits, unFlattenGateSequence(gates), param, measCount); - } - - /// # Summary /// generate a flat list of sample indices where mispredictions occur /// @@ -268,46 +262,6 @@ namespace Microsoft.Quantum.MachineLearning { return ret; } - /// # Summary - /// C#-friendly adapter to misclassification tally - /// - /// # Input - /// ## vectors - /// data vectors in flat encoding - /// - /// ## labels - /// array of corresponding class lables - /// - /// ## schedule - /// flat representation of index subset on which the circuit is scored - /// - /// ## param - /// circuit parameters - /// - /// ## gateStructure - /// gate structure in flat representation - /// - /// ## bias - /// prediction bias to be tested - /// - /// ## measCount - /// maximum number of quantum measurements per estimation (measCount==0 implies simulator deployment) - /// - /// # Output - /// the number of misclassifications - /// - operation MisclassificationScoreAdapter(vectors: Double[][], labels: Int[], schedule: Int[][], param: Double[], gateStructure: Int[][], bias: Double, measCount: Int) : Int { - mutable misses = 0; - let samples = unFlattenLabeledSamples(vectors,labels); - let gates = unFlattenGateSequence(gateStructure); - let sched = unFlattenSchedule(schedule); - - let pls = ClassificationProbabilitiesClassicalData(samples,sched,param,gates,measCount); - let biasCurrent = _UpdatedBias(pls, bias, 0.01); - let (h1,m1) = TallyHitsMisses(pls,biasCurrent); - return m1; - } - /// # Summary /// Extract a mini batch of samples and wrap the batch as a LabeledSampleContainer /// diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index 790a4d626e0..c4a33f5f96c 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -250,17 +250,20 @@ namespace Microsoft.Quantum.MachineLearning { let HARDCODEDunderage = 3; // 4/26 slack greater than 3 is not recommended - mutable hBest = h0; - mutable mBest = m0; + mutable (hBest, mBest) = (h0, m0); mutable bestSoFar = model; let pls = ClassificationProbabilitiesClassicalData( - samples, schedule, model::Parameters, gates, options::NMeasurements + gates, model::Parameters, samples, options::NMeasurements, + schedule ); let missLocations = MissLocations(schedule, pls, model::Bias); //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters - let minibatches = Mapped(Subarray(_, samples), Chunks(options::MinibatchSize, missLocations)); + let minibatches = Mapped( + Subarray(_, samples), + Chunks(options::MinibatchSize, missLocations) + ); for (minibatch in minibatches) { let (utility, updatedParameters) = OneStochasticTrainingStep( minibatch, options, bestSoFar::Parameters, gates @@ -269,16 +272,18 @@ namespace Microsoft.Quantum.MachineLearning { // There has been some good parameter update. // Check if it actually improves things, and if so, // commit it. - let plsCurrent = ClassificationProbabilitiesClassicalData(samples, schedule, updatedParameters, gates, options::NMeasurements); + let probabilities = ClassificationProbabilitiesClassicalData( + gates, updatedParameters, samples, options::NMeasurements, + schedule + ); let updatedBias = _UpdatedBias( - plsCurrent, model::Bias, options::Tolerance + probabilities, model::Bias, options::Tolerance ); - let (h1, m1) = TallyHitsMisses(plsCurrent, updatedBias); + let (h1, m1) = TallyHitsMisses(probabilities, updatedBias); if (m1 < mBest + HARDCODEDunderage) { //we allow limited non-greediness if (m1 < mBest) { - set hBest = h1; - set mBest = m1; + set (hBest, mBest) = (h1, m1); set bestSoFar = SequentialModel(updatedParameters, updatedBias); } } @@ -353,7 +358,8 @@ namespace Microsoft.Quantum.MachineLearning { //const let relFuzz = 0.01; let pls = ClassificationProbabilitiesClassicalData( - samples, schedule, model::Parameters, gates, options::NMeasurements + gates, model::Parameters, samples, options::NMeasurements, + schedule ); mutable bestSoFar = model w/ Bias <- _UpdatedBias(pls, model::Bias, options::Tolerance); From 931e0e4295543a573d036f1555ece5928755d2a1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 14:11:02 -0800 Subject: [PATCH 10/21] Consolidated training and classification APIs further. --- MachineLearning/src/DataModel/Interop.cs | 29 +-- MachineLearning/src/Runtime/Circuits.qs | 77 ------- MachineLearning/src/Runtime/Classification.qs | 206 ++++++------------ MachineLearning/src/Runtime/Training.qs | 108 +++++---- MachineLearning/src/Runtime/Validation.qs | 46 ++-- 5 files changed, 151 insertions(+), 315 deletions(-) diff --git a/MachineLearning/src/DataModel/Interop.cs b/MachineLearning/src/DataModel/Interop.cs index 262b17365b2..66fbe180b02 100644 --- a/MachineLearning/src/DataModel/Interop.cs +++ b/MachineLearning/src/DataModel/Interop.cs @@ -176,7 +176,7 @@ public static List PartialLocalLayer(long[] indices, char pauli) /// /// Creates a cyclic block of nQubits controlled rotations that starts - /// with contol qubit (nQubits-1), target qubit (cspan-1) % n , followed by the + /// with contol qubit (nQubits-1), target qubit (cspan-1) % n , followed by the /// ladder of entanglers with control qubit iq and target qubit (iq+cspan) % n /// /// Number of qubits to entangle @@ -253,7 +253,7 @@ public void QcccTrainParallel(IQArray> parameterSource, IQArray< (j) => { - var rslt = + var rslt = TrainQcccSequential.Run(simAll[j], this._nQubits, this._structure, parameterComb[j], trainingSet, trainingLabels, trainingSchedule, validationSchedule, learningRate, tolerance, miniBatchSize, maxEpochs, nMeasurements).Result; resultsAll[j] = rslt; } @@ -304,31 +304,6 @@ public long CountMisclassifications(double tolerance, List samples, Li return CountMisclassifications(tolerance, Qonvert.ToQ(samples), Qonvert.ToQ(knownLabels), Qonvert.ToQ(validationSchedule), nMeasurements, randomizationSeed); } - //EstimateClassificationProbabilitiesClassicalDataAdapter(samples: Double[][], schedule: Int[][], nQubits: Int, gates: Int[][], param: Double[], measCount: Int): Double[] - public double[] EstimateClassificationProbabilities(double tolerance, IQArray> samples, IQArray> schedule, long nMeasurements, uint randomizationSeed) - { - if (this.isTrained) - { - var sim = new QuantumSimulator(false, randomizationSeed); - IQArray probs = EstimateClassificationProbabilitiesClassicalDataAdapter.Run(sim, tolerance, samples, schedule, this._nQubits, this._structure, this.CachedParameters, nMeasurements).Result; - return probs.ToArray(); - } - return new double[] { -1.0 }; - } - - public double[] EstimateClassificationProbabilities(double tolerance, List samples, List schedule, long nMeasurements, uint randomizationSeed) - { - return EstimateClassificationProbabilities(tolerance, Qonvert.ToQ(samples), Qonvert.ToQ(schedule), nMeasurements, randomizationSeed); - } - - public double[] EstimateClassificationProbabilities(double tolerance, List samples, long nMeasurements, uint randomizationSeed) - { - List sched = new List(1); - sched.Add(new long[] { 0L, 1L, (long)(samples.Count - 1) }); - return EstimateClassificationProbabilities(tolerance, Qonvert.ToQ(samples), Qonvert.ToQ(sched), nMeasurements, randomizationSeed); - } - - } //class ClassificationModel } diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index e400079037f..0ffd5770965 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -185,83 +185,6 @@ namespace Microsoft.Quantum.MachineLearning { } - /// # Summary - /// Get a list of all the classification probabilities. In the from of (prob1,label) pairs. THIS operation is IN DEPRECATION - /// - /// # Input - /// ## samples - /// a container of labeled samples - /// - /// ## sched - /// a schedule to define a subset of samples - /// - /// ## param - /// parameters of the circuits - /// - /// ## gates - /// the sequence of gates in the circuit - /// - /// ## nMeasurements - /// the maximum number of quantum measurements used in the probability estimation - /// - /// # Output - /// TODO - operation ClassificationProbabilitiesClassicalData( - gates: GateSequence, - param: Double[], - samples: LabeledSample[], - nMeasurements: Int, - schedule: SamplingSchedule - ) - : (Double, Int)[] { - mutable ret = new (Double, Int)[0]; - for (sample in Sampled(schedule, samples)) { - let prob1 = EstimateClassificationProbabilityFromSample( - 1E-12, param, gates, sample::Features, nMeasurements - ); - set ret += [(prob1, sample::Label)]; - } - return ret; - } - - /// # Summary - /// generate a flat list of sample indices where mispredictions occur - /// - /// # Input - /// ## sched - /// a sampling schedule - /// - /// ## pls - /// a list of estimated probabilities with the corresponding class labels - /// - /// ## bias - /// bias on record - /// - /// # Output - /// the list of indices where mispredictions occur - /// - function MissLocations(sched : SamplingSchedule, pls : (Double, Int)[], bias: Double) : Int[] { - mutable ret = new Int[0]; - mutable ir = 0; - - for (rg in sched!) { - for (ix in rg) { - let (prob1, lab) = pls[ir]; - set ir += 1; - if (prob1 + bias > 0.5) { - if (lab < 1) { - set ret += [ix]; - } - } else { - if (lab > 0) { - set ret += [ix]; - } - } - } - } - return ret; - } - /// # Summary /// Extract a mini batch of samples and wrap the batch as a LabeledSampleContainer /// diff --git a/MachineLearning/src/Runtime/Classification.qs b/MachineLearning/src/Runtime/Classification.qs index 65c3e67293e..376dc1fcd4a 100644 --- a/MachineLearning/src/Runtime/Classification.qs +++ b/MachineLearning/src/Runtime/Classification.qs @@ -5,146 +5,82 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Convert; - operation EstimateClassificationProbabilityFromEncodedSample( - encodedSample : StateGenerator, - parameters: Double[], - gates: GateSequence, nMeasurements : Int - ) - : Double { - return 1.0 - EstimateFrequencyA( - endToEndPreparation(encodedSample::Apply, parameters,gates), - measureLastQubit(encodedSample::NQubits), - encodedSample::NQubits, - nMeasurements - ); - } + operation EstimateClassificationProbabilityFromEncodedSample( + encodedSample : StateGenerator, + parameters: Double[], + gates: GateSequence, nMeasurements : Int + ) + : Double { + return 1.0 - EstimateFrequencyA( + endToEndPreparation(encodedSample::Apply, parameters,gates), + measureLastQubit(encodedSample::NQubits), + encodedSample::NQubits, + nMeasurements + ); + } - operation EstimateClassificationProbabilityFromSample(tolerance: Double, parameters : Double[], gates: GateSequence, sample: Double[], nMeasurements: Int) - : Double { - let nQubits = FeatureRegisterSize(sample); - let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); - return EstimateClassificationProbabilityFromEncodedSample( - StateGenerator(nQubits, circEnc), parameters, gates, nMeasurements - ); + operation EstimateClassificationProbabilityFromSample(tolerance: Double, parameters : Double[], gates: GateSequence, sample: Double[], nMeasurements: Int) + : Double { + let nQubits = FeatureRegisterSize(sample); + let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); + return EstimateClassificationProbabilityFromEncodedSample( + StateGenerator(nQubits, circEnc), parameters, gates, nMeasurements + ); - } + } - /// # Summary - /// Given a of classification probability and a bias, returns the - /// label inferred from that probability. - /// - /// # Input - /// ## bias - /// The bias between two classes, typically the result of training a - /// classifier. - /// ## probability - /// A classification probabilities for a particular sample, typicaly - /// resulting from estimating its classification frequency. - /// - /// # Output - /// The label inferred from the given classification probability. - function InferredLabel(bias : Double, probability : Double) : Int { - return probability + bias > 0.5 ? 1 | 0; - } + operation EstimateClassificationProbabilities( + tolerance : Double, + parameters : Double[], + structure : GateSequence, + samples : Double[][], + nMeasurements : Int + ) : Double[] { + let effectiveTolerance = tolerance / IntAsDouble(Length(structure!)); + return ForEach( + EstimateClassificationProbabilityFromSample( + effectiveTolerance, parameters, structure, _, nMeasurements + ), + samples + ); + } - /// # Summary - /// Given an array of classification probabilities and a bias, returns the - /// label inferred from each probability. - /// - /// # Input - /// ## bias - /// The bias between two classes, typically the result of training a - /// classifier. - /// ## probabilities - /// An array of classification probabilities for a set of samples, typicaly - /// resulting from estimating classification frequencies. - /// - /// # Output - /// The label inferred from each classification probability. - function InferredLabels(bias : Double, probabilities : Double[]): Int[] { - return Mapped(InferredLabel(bias, _), probabilities); - } + /// # Summary + /// Given a of classification probability and a bias, returns the + /// label inferred from that probability. + /// + /// # Input + /// ## bias + /// The bias between two classes, typically the result of training a + /// classifier. + /// ## probability + /// A classification probabilities for a particular sample, typicaly + /// resulting from estimating its classification frequency. + /// + /// # Output + /// The label inferred from the given classification probability. + function InferredLabel(bias : Double, probability : Double) : Int { + return probability + bias > 0.5 ? 1 | 0; + } - /// # Summary - /// Estimates all classification probabilities for a given dataset. - /// - /// # Input - /// ## samples - /// a container of labeled samples - /// - /// ## sched - /// a schedule to define a subset of samples - /// - /// ## nQubits - /// number of qubits in the classification circuit - /// - /// ## gates - /// the sequence of gates in the circuit - /// - /// ## param - /// parameters of the circuits - /// - /// ## measCount - /// - /// # Output - /// array of corresponding estimated probabilities of the top class label - /// - operation EstimateClassificationProbabilitiesClassicalData( - tolerance : Double, samples : Double[][], sched : SamplingSchedule, - nQubits : Int, gates : GateSequence, param : Double[], - nMeasurements : Int - ) : Double[] { - let effectiveTolerance = tolerance / IntAsDouble(Length(gates!)); - mutable ret = new Double[0]; - for (rg in sched!) { - for (ix in rg) { - let samp = samples[ix]; - set ret += [EstimateClassificationProbabilityFromEncodedSample( - StateGenerator(nQubits, NoisyInputEncoder(effectiveTolerance, samp)), - param, gates, nMeasurements - )]; - } - } - - return ret; - } - - /// # Summary - /// Using a flat description of a classification model, assign estimated probability of top class label - /// to each vector in the test set - /// - /// # Input - /// ## nQubits - /// the number of qubits used for data encoding - /// - /// ## gates - /// Flattened representation of classifier structure. Each element is - /// [parameterIndex, pauliCode, targetQubit, sequence of control qubits] - /// - /// ## parameters - /// an array of circuit parameters - /// - /// ## samples - /// the set of vectors to be labeled - /// - /// ## bias - /// top class bias - /// - /// ## nMeasurenets - /// number of the measurement cycles to be used for estimation of each probability - /// - /// # Output - /// Array of predicted class labels for each sample of the test set - /// - operation DoClassification(tolerance: Double, nQubits: Int, gates: Int[][], parameters: Double[], bias: Double, samples : Double[][], nMeasurements: Int) : Int[] { - let schedule = SamplingSchedule([0..Length(samples) - 1]); - let sequence = unFlattenGateSequence(gates); - let probs = EstimateClassificationProbabilitiesClassicalData( - tolerance, samples, schedule, nQubits, sequence, parameters, nMeasurements - ); - return InferredLabels(bias, probs); - } + /// # Summary + /// Given an array of classification probabilities and a bias, returns the + /// label inferred from each probability. + /// + /// # Input + /// ## bias + /// The bias between two classes, typically the result of training a + /// classifier. + /// ## probabilities + /// An array of classification probabilities for a set of samples, typicaly + /// resulting from estimating classification frequencies. + /// + /// # Output + /// The label inferred from each classification probability. + function InferredLabels(bias : Double, probabilities : Double[]): Int[] { + return Mapped(InferredLabel(bias, _), probabilities); + } } diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index c4a33f5f96c..77f5d591c72 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -63,21 +63,24 @@ namespace Microsoft.Quantum.MachineLearning { for (idxStart in 0..(Length(parameterSource) - 1)) { Message($"Beginning training at start point #{idxStart}..."); - let ((h, m), proposedUpdate) = StochasticTrainingLoop( + let proposedUpdate = StochasticTrainingLoop( gates, SequentialModel(parameterSource[idxStart], 0.0), samples, options, trainingSchedule, 1 ); - let probsValidation = EstimateClassificationProbabilitiesClassicalData( - options::Tolerance, features, validationSchedule, nQubits, - gates, proposedUpdate::Parameters, options::NMeasurements + let probabilities = EstimateClassificationProbabilities( + options::Tolerance, + proposedUpdate::Parameters, + gates, + Sampled(validationSchedule, features), + options::NMeasurements ); // Find the best bias for the new classification parameters. let localBias = _UpdatedBias( - Zip(probsValidation, Sampled(validationSchedule, labels)), + Zip(probabilities, Sampled(validationSchedule, labels)), 0.0, options::Tolerance ); - let localPL = InferredLabels(localBias, probsValidation); + let localPL = InferredLabels(localBias, probabilities); let localMisses = NMismatches(localPL, Sampled(validationSchedule, labels)); if (bestValidation > localMisses) { set bestValidation = localMisses; @@ -202,8 +205,8 @@ namespace Microsoft.Quantum.MachineLearning { let updatedParameters = Mapped(PlusD, Zip(param, batchGradient)); // TODO:REVIEW: Ok to interpret utility as size of the overall move? return (SquaredNorm(batchGradient), updatedParameters); - } + } /// # Summary /// Perform one epoch of circuit training on a subset of data samples to a quantum simulator @@ -239,25 +242,29 @@ namespace Microsoft.Quantum.MachineLearning { /// ## measCount /// number of true quantum measurements to estimate probabilities. /// - operation OneStochasticTrainingEpoch( + operation RunSingleTrainingEpoch( samples: LabeledSample[], schedule: SamplingSchedule, periodScore: Int, options : TrainingOptions, model : SequentialModel, gates: GateSequence, - h0: Int, m0: Int + nPreviousBestMisses : Int ) - : ((Int, Int), SequentialModel) { + : (Int, SequentialModel) { let HARDCODEDunderage = 3; // 4/26 slack greater than 3 is not recommended - - mutable (hBest, mBest) = (h0, m0); + mutable nBestMisses = nPreviousBestMisses; mutable bestSoFar = model; - - let pls = ClassificationProbabilitiesClassicalData( - gates, model::Parameters, samples, options::NMeasurements, - schedule + let features = Mapped(_Features, samples); + let actualLabels = Mapped(_Label, samples); + + let inferredLabels = InferredLabels( + model::Bias, + EstimateClassificationProbabilities( + options::Tolerance, model::Parameters, gates, + features, options::NMeasurements + ) ); - let missLocations = MissLocations(schedule, pls, model::Bias); + let missLocations = MissLocations(inferredLabels, actualLabels); //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters let minibatches = Mapped( @@ -272,26 +279,28 @@ namespace Microsoft.Quantum.MachineLearning { // There has been some good parameter update. // Check if it actually improves things, and if so, // commit it. - let probabilities = ClassificationProbabilitiesClassicalData( - gates, updatedParameters, samples, options::NMeasurements, - schedule + let probabilities = EstimateClassificationProbabilities( + options::Tolerance, updatedParameters, gates, + features, options::NMeasurements ); let updatedBias = _UpdatedBias( - probabilities, model::Bias, options::Tolerance + Zip(probabilities, actualLabels), model::Bias, options::Tolerance ); - let (h1, m1) = TallyHitsMisses(probabilities, updatedBias); - if (m1 < mBest + HARDCODEDunderage) { - //we allow limited non-greediness - if (m1 < mBest) { - set (hBest, mBest) = (h1, m1); - set bestSoFar = SequentialModel(updatedParameters, updatedBias); - } + let updatedLabels = InferredLabels( + updatedBias, probabilities + ); + let nMisses = Length(MissLocations( + updatedLabels, actualLabels + )); + if (nMisses < nBestMisses) { + set nBestMisses = nMisses; + set bestSoFar = SequentialModel(updatedParameters, updatedBias); } } } - return ((hBest, mBest), bestSoFar); + return (nBestMisses, bestSoFar); } /// # Summary @@ -354,16 +363,27 @@ namespace Microsoft.Quantum.MachineLearning { schedule: SamplingSchedule, periodScore: Int ) - : ((Int, Int), SequentialModel) { + : SequentialModel { //const let relFuzz = 0.01; - let pls = ClassificationProbabilitiesClassicalData( - gates, model::Parameters, samples, options::NMeasurements, - schedule + let nSamples = Length(samples); + let features = Mapped(_Features, samples); + let actualLabels = Mapped(_Label, samples); + let probabilities = EstimateClassificationProbabilities( + options::Tolerance, model::Parameters, gates, + features, options::NMeasurements ); mutable bestSoFar = model - w/ Bias <- _UpdatedBias(pls, model::Bias, options::Tolerance); - mutable (hBest, mBest) = TallyHitsMisses(pls, model::Bias); + w/ Bias <- _UpdatedBias( + Zip(probabilities, actualLabels), + model::Bias, options::Tolerance + ); + let inferredLabels = InferredLabels( + bestSoFar::Bias, probabilities + ); + mutable nBestMisses = Length( + MissLocations(inferredLabels, actualLabels) + ); mutable current = bestSoFar; //reintroducing learning rate heuristics @@ -374,19 +394,19 @@ namespace Microsoft.Quantum.MachineLearning { mutable nStalls = 0; for (ep in 1..options::MaxEpochs) { - let ((h1, m1), proposedUpdate) = OneStochasticTrainingEpoch( + let (nMisses, proposedUpdate) = RunSingleTrainingEpoch( samples, schedule, periodScore, options w/ LearningRate <- lrate w/ MinibatchSize <- batchSize, - current, gates, hBest, mBest + current, gates, + nBestMisses ); - if (m1 < mBest) { - set hBest = h1; - set mBest = m1; + if (nMisses < nBestMisses) { + set nBestMisses = nMisses; set bestSoFar = proposedUpdate; - if (IntAsDouble(mBest) / IntAsDouble(mBest + hBest) < options::Tolerance) { //Terminate based on tolerance - return ((hBest, mBest), bestSoFar); + if (IntAsDouble(nMisses) / IntAsDouble(nSamples) < options::Tolerance) { //Terminate based on tolerance + return bestSoFar; } set nStalls = 0; //Reset the counter of consequtive noops set lrate = options::LearningRate; @@ -400,7 +420,7 @@ namespace Microsoft.Quantum.MachineLearning { // If we're more than halfway through our maximum allowed number of stalls, // exit early with the best we actually found. if (nStalls > options::MaxStalls) { - return ((hBest, mBest), bestSoFar); //Too many non-steps. Continuation makes no sense + return bestSoFar; //Too many non-steps. Continuation makes no sense } // Otherwise, heat up the learning rate and batch size. @@ -429,7 +449,7 @@ namespace Microsoft.Quantum.MachineLearning { } } - return ((hBest, mBest), bestSoFar); + return bestSoFar; } } diff --git a/MachineLearning/src/Runtime/Validation.qs b/MachineLearning/src/Runtime/Validation.qs index fca2e87397a..78d9c68a31b 100644 --- a/MachineLearning/src/Runtime/Validation.qs +++ b/MachineLearning/src/Runtime/Validation.qs @@ -3,40 +3,19 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - function NMismatches(proposed: Int[], actual: Int[]): Int { - mutable count = 0; - for ((proposedLabel, actualLabel) in Zip(proposed, actual)) { - if (proposedLabel != actualLabel) { - set count += 1; + function MissLocations(inferredLabels : Int[], actualLabels : Int[]) + : Int[] { + mutable ret = new Int[0]; + for ((idx, (inferred, actual)) in Enumerated(Zip(inferredLabels, actualLabels))) { + if (inferred != actual) { + set ret += [idx]; } } - return count; + return ret; } - /// # Summary - /// tallies hits and misses off a list of probability estimates - /// - /// # Input - /// ## pls - /// a list of estimated probabilities with the corresponding class labels - /// - /// ## bias - /// bias on record - /// - /// # Output - /// (no.hits, no.misses) pair - /// - function TallyHitsMisses(pls : (Double, Int)[], bias : Double) : (Int, Int) { - mutable hits = 0; - mutable misses = 0; - for ((classificationProbability, label) in pls) { - if (label == InferredLabel(bias, classificationProbability)) { - set hits += 1; - } else { - set misses += 1; - } - } - return (hits, misses); + function NMismatches(proposed: Int[], actual: Int[]): Int { + return Length(MissLocations(proposed, actual)); } /// # Summary @@ -87,8 +66,11 @@ namespace Microsoft.Quantum.MachineLearning { { let features = Mapped(_Features, samples); let labels = Sampled(validationSchedule, Mapped(_Label, samples)); - let probsValidation = EstimateClassificationProbabilitiesClassicalData(tolerance, features, validationSchedule, nQubits, gates, parameters, nMeasurements); - let localPL = InferredLabels(bias, probsValidation); + let probabilities = EstimateClassificationProbabilities( + tolerance, parameters, gates, + Sampled(validationSchedule, features), nMeasurements + ); + let localPL = InferredLabels(bias, probabilities); let nMismatches = NMismatches(localPL, labels); return ValidationResults( nMismatches From f10584a16ecffd448db045aa90bd0b905ea186ca Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 14:26:15 -0800 Subject: [PATCH 11/21] Fixed bug in Chunks. --- Standard/src/Arrays/Arrays.qs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Standard/src/Arrays/Arrays.qs b/Standard/src/Arrays/Arrays.qs index c09b822afab..bb45ed9bcc3 100644 --- a/Standard/src/Arrays/Arrays.qs +++ b/Standard/src/Arrays/Arrays.qs @@ -269,8 +269,9 @@ namespace Microsoft.Quantum.Arrays { mutable output = new 'T[][0]; mutable remaining = arr; while (not IsEmpty(remaining)) { - set output += [remaining[...nElements - 1]]; - set remaining = remaining[nElements...]; + let nElementsToTake = MinI(Length(remaining), nElements); + set output += [remaining[...nElementsToTake - 1]]; + set remaining = remaining[nElementsToTake...]; } return output; } From 76069f5b0451f9acc0102bf60fd503e69764174a Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 14:27:15 -0800 Subject: [PATCH 12/21] Regression test for prev commit. --- Standard/tests/ArrayTests.qs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Standard/tests/ArrayTests.qs b/Standard/tests/ArrayTests.qs index 88972b5e91c..556c3c51fe6 100644 --- a/Standard/tests/ArrayTests.qs +++ b/Standard/tests/ArrayTests.qs @@ -53,6 +53,12 @@ namespace Microsoft.Quantum.Tests { [[10, 11], [12, 13], [14, 15]], Chunks(2, data) )), "Wrong chunks in 2x3 case."); + + // Case with some leftovers. + Fact(All(_AllEqualI, Zip( + [[10, 11, 12, 13], [14, 15]], + Chunks(4, data) + )), "Wrong chunks in case with leftover elements."); } function _Squared(x : Int) : Int { From 932cb9d8a2bbc10f29c9500c3b100f2b52eafdb3 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 14:28:10 -0800 Subject: [PATCH 13/21] Make internal implementation of sequential model trainer private. --- MachineLearning/src/Runtime/Training.qs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index 77f5d591c72..a9b5054c170 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -179,7 +179,7 @@ namespace Microsoft.Quantum.MachineLearning { /// # Output /// (utility, (new)parameters) pair /// - operation OneStochasticTrainingStep( + operation _RunSingleTrainingStep( miniBatch : LabeledSample[], options : TrainingOptions, param : Double[], gates : GateSequence @@ -242,7 +242,7 @@ namespace Microsoft.Quantum.MachineLearning { /// ## measCount /// number of true quantum measurements to estimate probabilities. /// - operation RunSingleTrainingEpoch( + operation _RunSingleTrainingEpoch( samples: LabeledSample[], schedule: SamplingSchedule, periodScore: Int, options : TrainingOptions, @@ -272,7 +272,7 @@ namespace Microsoft.Quantum.MachineLearning { Chunks(options::MinibatchSize, missLocations) ); for (minibatch in minibatches) { - let (utility, updatedParameters) = OneStochasticTrainingStep( + let (utility, updatedParameters) = RunSingleTrainingStep( minibatch, options, bestSoFar::Parameters, gates ); if (utility > 0.0000001) { From 7e473692798ee1380c6d90a7429b823f7f458ca8 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 15:05:10 -0800 Subject: [PATCH 14/21] Make APIs more uniform. --- MachineLearning/src/DataModel/Interop.cs | 62 -------------- MachineLearning/src/Runtime/Classification.qs | 28 +++--- MachineLearning/src/Runtime/Training.qs | 85 +++---------------- MachineLearning/src/Runtime/Validation.qs | 33 ++++--- 4 files changed, 43 insertions(+), 165 deletions(-) diff --git a/MachineLearning/src/DataModel/Interop.cs b/MachineLearning/src/DataModel/Interop.cs index 66fbe180b02..d1a9a04a692 100644 --- a/MachineLearning/src/DataModel/Interop.cs +++ b/MachineLearning/src/DataModel/Interop.cs @@ -220,68 +220,6 @@ public static void reindex(List struc) } } - public void QcccTrainSequential(IQArray> parameterSource, IQArray> trainingSet, IQArray trainingLabels, IQArray> trainingSchedule, - IQArray> validationSchedule, double learningRate, double tolerance, long miniBatchSize, long maxEpochs, long nMeasurements, uint randomizationSeed) - { - var sim = new QuantumSimulator(false, randomizationSeed); - (this._cachedParameters, this._bias) = - TrainQcccSequential.Run(sim, this._nQubits, this._structure, parameterSource, trainingSet, trainingLabels, trainingSchedule, validationSchedule, learningRate, tolerance, miniBatchSize, maxEpochs, nMeasurements).Result; - } - public void QcccTrainSequential(List parameterSource, List trainingSet, List trainingLabels, List trainingSchedule, - List validationSchedule, double learningRate, double tolerance, long miniBatchSize, long maxEpochs, long nMeasurements, uint randomizationSeed) - { - QcccTrainSequential(Qonvert.ToQ(parameterSource), Qonvert.ToQ(trainingSet), Qonvert.ToQ(trainingLabels), Qonvert.ToQ(trainingSchedule), - Qonvert.ToQ(validationSchedule), learningRate, tolerance, miniBatchSize, maxEpochs, nMeasurements, randomizationSeed); - } - - public void QcccTrainParallel(IQArray> parameterSource, IQArray> trainingSet, IQArray trainingLabels, IQArray> trainingSchedule, - IQArray> validationSchedule, double learningRate, double tolerance, long miniBatchSize, long maxEpochs, long nMeasurements, uint randomizationSeed) - { - var simAll = new List(parameterSource.Count); - var resultsAll = new List<(IQArray, double)>(parameterSource.Count); - var parameterComb = new List>>(parameterSource.Count); - - var indices = new int[parameterSource.Count]; - for (int j = 0; j < parameterSource.Count; j++) - { - indices[j] = j; - simAll.Add(new QuantumSimulator(false, randomizationSeed)); - resultsAll.Add((new QArray(),0.0)); - parameterComb.Add(new QArray>(new IQArray[] { parameterSource[j] })); //Isolating parameter starts - one per thread - } - Parallel.ForEach(indices, - (j) => - { - - var rslt = - TrainQcccSequential.Run(simAll[j], this._nQubits, this._structure, parameterComb[j], trainingSet, trainingLabels, trainingSchedule, validationSchedule, learningRate, tolerance, miniBatchSize, maxEpochs, nMeasurements).Result; - resultsAll[j] = rslt; - } - ); - //Estimated parameters and biases for each proposed parameter start. Now postprocess - long bestValidation = long.MaxValue; - int bestJ = -1; - var sim = new QuantumSimulator(false, randomizationSeed); - for (int j = 0; j < parameterSource.Count; j++) - { - var (pars, bias) = resultsAll[j]; - long misses = CountValidationMisses.Run(sim, tolerance, this._nQubits, trainingSet, trainingLabels, validationSchedule, this._structure, pars, bias, nMeasurements).Result; - if (bestValidation > misses) - { - bestValidation = misses; - bestJ = j; - } - } - (this._cachedParameters, this._bias) = resultsAll[bestJ]; - } //QcccTrainParallel - - public void QcccTrainParallel(List parameterSource, List trainingSet, List trainingLabels, List trainingSchedule, - List validationSchedule, double learningRate, double tolerance, long miniBatchSize, long maxEpochs, long nMeasurements, uint randomizationSeed) - { - QcccTrainParallel(Qonvert.ToQ(parameterSource), Qonvert.ToQ(trainingSet), Qonvert.ToQ(trainingLabels), Qonvert.ToQ(trainingSchedule), - Qonvert.ToQ(validationSchedule), learningRate, tolerance, miniBatchSize, maxEpochs, nMeasurements, randomizationSeed); - } - public long CountMisclassifications(double tolerance, IQArray> samples, IQArray knownLabels, IQArray> validationSchedule, long nMeasurements, uint randomizationSeed) { if (this.isTrained) diff --git a/MachineLearning/src/Runtime/Classification.qs b/MachineLearning/src/Runtime/Classification.qs index 376dc1fcd4a..40c86a40e3c 100644 --- a/MachineLearning/src/Runtime/Classification.qs +++ b/MachineLearning/src/Runtime/Classification.qs @@ -7,12 +7,17 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Convert; - operation EstimateClassificationProbabilityFromEncodedSample( - encodedSample : StateGenerator, - parameters: Double[], - gates: GateSequence, nMeasurements : Int + operation EstimateClassificationProbability( + tolerance: Double, + parameters : Double[], + gates: GateSequence, + sample: Double[], + nMeasurements: Int ) : Double { + let nQubits = FeatureRegisterSize(sample); + let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); + let encodedSample = StateGenerator(nQubits, circEnc); return 1.0 - EstimateFrequencyA( endToEndPreparation(encodedSample::Apply, parameters,gates), measureLastQubit(encodedSample::NQubits), @@ -21,26 +26,17 @@ namespace Microsoft.Quantum.MachineLearning { ); } - operation EstimateClassificationProbabilityFromSample(tolerance: Double, parameters : Double[], gates: GateSequence, sample: Double[], nMeasurements: Int) - : Double { - let nQubits = FeatureRegisterSize(sample); - let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); - return EstimateClassificationProbabilityFromEncodedSample( - StateGenerator(nQubits, circEnc), parameters, gates, nMeasurements - ); - - } - operation EstimateClassificationProbabilities( tolerance : Double, parameters : Double[], structure : GateSequence, samples : Double[][], nMeasurements : Int - ) : Double[] { + ) + : Double[] { let effectiveTolerance = tolerance / IntAsDouble(Length(structure!)); return ForEach( - EstimateClassificationProbabilityFromSample( + EstimateClassificationProbability( effectiveTolerance, parameters, structure, _, nMeasurements ), samples diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index a9b5054c170..4dfa1aaecaf 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -9,7 +9,7 @@ namespace Microsoft.Quantum.MachineLearning { function _MisclassificationRate(probabilities : Double[], labels : Int[], bias : Double) : Double { let proposedLabels = InferredLabels(bias, probabilities); - return IntAsDouble(NMismatches(proposedLabels, labels)) / IntAsDouble(Length(probabilities)); + return IntAsDouble(NMisclassifications(proposedLabels, labels)) / IntAsDouble(Length(probabilities)); } /// # Summary @@ -47,7 +47,6 @@ namespace Microsoft.Quantum.MachineLearning { } operation TrainSequentialClassifier( - nQubits: Int, gates: GateSequence, parameterSource: Double[][], samples: LabeledSample[], @@ -81,7 +80,7 @@ namespace Microsoft.Quantum.MachineLearning { options::Tolerance ); let localPL = InferredLabels(localBias, probabilities); - let localMisses = NMismatches(localPL, Sampled(validationSchedule, labels)); + let localMisses = NMisclassifications(localPL, Sampled(validationSchedule, labels)); if (bestValidation > localMisses) { set bestValidation = localMisses; set bestSoFar = proposedUpdate; @@ -91,72 +90,6 @@ namespace Microsoft.Quantum.MachineLearning { return bestSoFar; } - /// # Summary - /// Using a flat description of a classification model, find a good local optimum - /// for the model parameters and a related calssification bias - /// - /// # Input - /// ## nQubits - /// the number of qubits used for data encoding - /// - /// ## gates - /// flat characterization of circuit structure. Each element is [parameterIndex, pauliCode, targetQubit\,sequence of control qubits\] - /// - /// ## parameterSource - /// an array of parameter arrays, to be used as SGD starting points - /// - /// ## trainingSet - /// the set of training samples - /// - /// ## trainingLabels - /// the set of training labels - /// - /// ## trainingSchedule - /// defines a subset of training data actually used in the training process - /// - /// ## validatioSchedule - /// defines a subset of training data used for validation and computation of the *bias* - /// - /// ## learningRate - /// initial learning rate for stochastic gradient descent - /// - /// ## tolerance - /// sufficient absolute precision of parameter updates - /// - /// ## learningRate - /// initial learning rate for stochastic gradient descent - /// - /// ## miniBatchSize - /// maximum size of SGD mini batches - /// - /// ## maxEpochs - /// limit to the number of training epochs - /// - /// ## nMeasurenets - /// number of the measurement cycles to be used for estimation of each probability - /// - /// # Output - /// (Array of optimal parameters, optimal validation *bias*) - /// - operation TrainQcccSequential(nQubits: Int, gates: Int[][], parameterSource: Double[][], trainingSet: Double[][], trainingLabels: Int[], trainingSchedule: Int[][], validationSchedule: Int[][], - learningRate: Double, tolerance: Double, miniBatchSize: Int, maxEpochs: Int, nMeasurements: Int) : (Double[],Double) { - let samples = unFlattenLabeledSamples(trainingSet,trainingLabels); - let sch = unFlattenSchedule(trainingSchedule); - let schValidate = unFlattenSchedule(validationSchedule); - let gateSequence = unFlattenGateSequence(gates); - let options = DefaultTrainingOptions() - w/ LearningRate <- learningRate - w/ Tolerance <- tolerance - w/ MinibatchSize <- miniBatchSize - w/ NMeasurements <- nMeasurements - w/ MaxEpochs <- maxEpochs; - - return (TrainSequentialClassifier( - nQubits, gateSequence, parameterSource, samples, - options, sch, schValidate - ))!; - } //TrainQcccSequential - /// # Summary /// attempts a single parameter update in the direction of mini batch gradient /// @@ -264,15 +197,17 @@ namespace Microsoft.Quantum.MachineLearning { features, options::NMeasurements ) ); - let missLocations = MissLocations(inferredLabels, actualLabels); //An epoch is just an attempt to update the parameters by learning from misses based on LKG parameters let minibatches = Mapped( Subarray(_, samples), - Chunks(options::MinibatchSize, missLocations) + Chunks( + options::MinibatchSize, + Misclassifications(inferredLabels, actualLabels) + ) ); for (minibatch in minibatches) { - let (utility, updatedParameters) = RunSingleTrainingStep( + let (utility, updatedParameters) = _RunSingleTrainingStep( minibatch, options, bestSoFar::Parameters, gates ); if (utility > 0.0000001) { @@ -289,7 +224,7 @@ namespace Microsoft.Quantum.MachineLearning { let updatedLabels = InferredLabels( updatedBias, probabilities ); - let nMisses = Length(MissLocations( + let nMisses = Length(Misclassifications( updatedLabels, actualLabels )); if (nMisses < nBestMisses) { @@ -382,7 +317,7 @@ namespace Microsoft.Quantum.MachineLearning { bestSoFar::Bias, probabilities ); mutable nBestMisses = Length( - MissLocations(inferredLabels, actualLabels) + Misclassifications(inferredLabels, actualLabels) ); mutable current = bestSoFar; @@ -394,7 +329,7 @@ namespace Microsoft.Quantum.MachineLearning { mutable nStalls = 0; for (ep in 1..options::MaxEpochs) { - let (nMisses, proposedUpdate) = RunSingleTrainingEpoch( + let (nMisses, proposedUpdate) = _RunSingleTrainingEpoch( samples, schedule, periodScore, options w/ LearningRate <- lrate diff --git a/MachineLearning/src/Runtime/Validation.qs b/MachineLearning/src/Runtime/Validation.qs index 78d9c68a31b..c063b4b37a6 100644 --- a/MachineLearning/src/Runtime/Validation.qs +++ b/MachineLearning/src/Runtime/Validation.qs @@ -3,7 +3,7 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - function MissLocations(inferredLabels : Int[], actualLabels : Int[]) + function Misclassifications(inferredLabels : Int[], actualLabels : Int[]) : Int[] { mutable ret = new Int[0]; for ((idx, (inferred, actual)) in Enumerated(Zip(inferredLabels, actualLabels))) { @@ -14,8 +14,8 @@ namespace Microsoft.Quantum.MachineLearning { return ret; } - function NMismatches(proposed: Int[], actual: Int[]): Int { - return Length(MissLocations(proposed, actual)); + function NMisclassifications(proposed: Int[], actual: Int[]): Int { + return Length(Misclassifications(proposed, actual)); } /// # Summary @@ -55,25 +55,34 @@ namespace Microsoft.Quantum.MachineLearning { { let schValidate = unFlattenSchedule(validationSchedule); let results = ValidateModel( - tolerance, nQubits, Mapped(LabeledSample, Zip(trainingSet, trainingLabels)), - schValidate, unFlattenGateSequence(gates), - parameters, bias, nMeasurements + unFlattenGateSequence(gates), + SequentialModel(parameters, bias), + Mapped(LabeledSample, Zip(trainingSet, trainingLabels)), + tolerance, nMeasurements, + schValidate ); return results::NMisclassifications; } - operation ValidateModel(tolerance: Double, nQubits: Int, samples : LabeledSample[], validationSchedule: SamplingSchedule, gates: GateSequence, parameters: Double[], bias:Double, nMeasurements: Int) : ValidationResults - { + operation ValidateModel( + gates: GateSequence, + model : SequentialModel, + samples : LabeledSample[], + tolerance: Double, + nMeasurements: Int, + validationSchedule: SamplingSchedule + ) + : ValidationResults { let features = Mapped(_Features, samples); let labels = Sampled(validationSchedule, Mapped(_Label, samples)); let probabilities = EstimateClassificationProbabilities( - tolerance, parameters, gates, + tolerance, model::Parameters, gates, Sampled(validationSchedule, features), nMeasurements ); - let localPL = InferredLabels(bias, probabilities); - let nMismatches = NMismatches(localPL, labels); + let localPL = InferredLabels(model::Bias, probabilities); + let nMisclassifications = NMisclassifications(localPL, labels); return ValidationResults( - nMismatches + nMisclassifications ); } From c1367bb7b8189e9f500c555af205fdd5fc2853c6 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 15:30:59 -0800 Subject: [PATCH 15/21] Added new array function to simplify Misclassifications. --- MachineLearning/src/Runtime/Validation.qs | 12 +++++----- Standard/src/Arrays/Filter.qs | 27 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/MachineLearning/src/Runtime/Validation.qs b/MachineLearning/src/Runtime/Validation.qs index c063b4b37a6..f146eb4427a 100644 --- a/MachineLearning/src/Runtime/Validation.qs +++ b/MachineLearning/src/Runtime/Validation.qs @@ -1,17 +1,15 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Logical; open Microsoft.Quantum.Canon; function Misclassifications(inferredLabels : Int[], actualLabels : Int[]) : Int[] { - mutable ret = new Int[0]; - for ((idx, (inferred, actual)) in Enumerated(Zip(inferredLabels, actualLabels))) { - if (inferred != actual) { - set ret += [idx]; - } - } - return ret; + return Where( + NotEqualI, + Zip(inferredLabels, actualLabels) + ); } function NMisclassifications(proposed: Int[], actual: Int[]): Int { diff --git a/Standard/src/Arrays/Filter.qs b/Standard/src/Arrays/Filter.qs index baa86f3c742..cadf8d58dae 100644 --- a/Standard/src/Arrays/Filter.qs +++ b/Standard/src/Arrays/Filter.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.Arrays { + open Microsoft.Quantum.Canon; /// # Summary /// Given an array and a predicate that is defined @@ -38,4 +39,30 @@ namespace Microsoft.Quantum.Arrays { return Subarray(idxArray[0 .. totalFound - 1], array); } + /// # Summary + /// Given a predicate and an array, returns the indices of that + /// array where the predicate is true. + /// + /// # Type Parameters + /// ## 'T + /// The type of `array` elements. + /// + /// # Input + /// ## predicate + /// A function from `'T` to Boolean that is used to filter elements. + /// ## array + /// An array of elements over `'T`. + /// + /// # Output + /// An array of indices where `predicate` is true. + function Where<'T>(predicate : ('T -> Bool), array : 'T[]) : Int[] { + return Mapped( + Fst, + Filtered( + Snd, + Enumerated(Mapped(predicate, array)) + ) + ); + } + } From 1d97f900defaa7ad498e458d6dff08c1879328ea Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 15:37:58 -0800 Subject: [PATCH 16/21] Remove ExtractMiniBatch. --- MachineLearning/src/Runtime/Circuits.qs | 33 ------------------------- 1 file changed, 33 deletions(-) diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/Circuits.qs index 0ffd5770965..049ba51211c 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/Circuits.qs @@ -185,37 +185,4 @@ namespace Microsoft.Quantum.MachineLearning { } - /// # Summary - /// Extract a mini batch of samples and wrap the batch as a LabeledSampleContainer - /// - /// # Input - /// ## size - /// desired number of samples in the mini batch - /// - /// ## ixLoc - /// starting index for the batch in the list of locations - /// - /// ## locations - /// list of indices of samples of interest - /// - /// ## samples - /// the container to extract the samples from - /// - /// # Output - /// the mini batched wrapped as a LabeledSampleContainer - /// - /// # Remarks - /// the resulting mini batch can be occasionally shorter than the requested 'size' - /// (when it falls on the tail end of the list of 'locations') - /// - @Deprecated("Microsoft.Quantum.Arrays.Chunks") - function ExtractMiniBatch(size: Int, ixLoc: Int, locations: Int[], samples: LabeledSample[]): LabeledSample[] { - let cnt = MinI(size, Length(locations) - ixLoc); - mutable rgSamples = new LabeledSample[0]; - for (location in locations[ixLoc..ixLoc + cnt]) { - set rgSamples += [samples[location]]; - } - return rgSamples; - } - } From 3fe20f55eed495721a7c8d9380269b1cebc6ff1a Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 15:56:28 -0800 Subject: [PATCH 17/21] Removed deprecated operations. --- MachineLearning/src/Runtime/Deprecated.qs | 84 ----------------------- 1 file changed, 84 deletions(-) delete mode 100644 MachineLearning/src/Runtime/Deprecated.qs diff --git a/MachineLearning/src/Runtime/Deprecated.qs b/MachineLearning/src/Runtime/Deprecated.qs deleted file mode 100644 index fd0b607d614..00000000000 --- a/MachineLearning/src/Runtime/Deprecated.qs +++ /dev/null @@ -1,84 +0,0 @@ -namespace Microsoft.Quantum.MachineLearning { - open Microsoft.Quantum.Logical; - open Microsoft.Quantum.Arithmetic; - open Microsoft.Quantum.Intrinsic; - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Math; - - /// Sample container access method - @Deprecated("") - function getSample(samples: LabeledSampleContainer, ix: Int): LabeledSample { - return (samples!)[ix]; - } - - /// Access the raw data in a labeled sample - @Deprecated("") - function getData(samp: LabeledSample): Double[] { - return Fst(samp!); - } - - /// Access the label in a labeled sample - @Deprecated("") - function getLabel(samp:LabeledSample) : Int - { - return Snd(samp!); - } - - - /// Abstraction for a container of labeled samples - @Deprecated("") - newtype LabeledSampleContainer = LabeledSample[]; - - @Deprecated("Microsoft.Quantum.Diagnostics.DumpRegister") - function dumpRegisterToConsole ( qs: Qubit[]) : Unit - {} - //{DumpRegister((),qs);} //Swap for empty body when some dumping of registers is needed - - @Deprecated("Microsoft.Quantum.MachineLearning.NQubitsRequired") - function qubitSpan(seq : GateSequence) : Int { - return NQubitsRequired(seq); - } - - /// Set force a qubit into a desired basis state - @Deprecated("Microsoft.Quantum.Measurement.SetToBasisState") - operation Set (desired: Result, q1: Qubit) : Unit - { - //body - //{ - let current = M(q1); - if (desired != current) - { - X(q1); - } - //} - } - - @Deprecated("Microsoft.Quantum.Math.SquaredNorm") - function squareNorm(v:Double[]):Double - { - mutable ret = 0.0; - for (u in v) - { - set ret = ret + u*u; - } - return ret; - } - - @Deprecated("") // replace with ForEach. - operation randomizeArray(src:Double[], relativeFuzz: Double) : Double[] - { - mutable ret = new Double[Length(src)]; - for (ix in 0..(Length(src)-1)) - { - set ret w/=ix <- _RandomlyRescale(src[ix], relativeFuzz); - } - return ret; - } - - @Deprecated("Microsoft.Quantum.Math.NearlyEqualD") - function nearIdenticalDoubles(x:Double,y:Double):Bool { - return NearlyEqualD(x, y); //Note key tolerance constant here - } - - -} From bd27873fa334b5e9ef63af0bc7a2fa1491443140 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 12 Dec 2019 17:42:37 -0800 Subject: [PATCH 18/21] Fix type in Examples. --- MachineLearning/src/Runtime/Examples.qs | 174 ++++++++++++------------ 1 file changed, 86 insertions(+), 88 deletions(-) diff --git a/MachineLearning/src/Runtime/Examples.qs b/MachineLearning/src/Runtime/Examples.qs index 5c9d863f18d..b7c3ccd4129 100644 --- a/MachineLearning/src/Runtime/Examples.qs +++ b/MachineLearning/src/Runtime/Examples.qs @@ -1,96 +1,94 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Primitive; - open Microsoft.Quantum.Convert; + open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; - operation IrisTrainingData() : LabeledSampleContainer { - let ret = - [LabeledSample(([0.581557, 0.562824, 0.447721, 0.380219], 1)), - LabeledSample(([0.570241, 0.544165, 0.503041, 0.354484], - 1)), LabeledSample(([0.510784, 0.475476, 0.453884, 0.554087], - 0)), LabeledSample(([0.492527, 0.473762, 0.471326, 0.557511], - 0)), LabeledSample(([0.543273, 0.501972, 0.518341, 0.429186], - 0)), LabeledSample(([0.520013, 0.485702, 0.440061, 0.547747], - 0)), LabeledSample(([0.585261, 0.545431, 0.462126, 0.382641], - 1)), LabeledSample(([0.541059, 0.479438, 0.568697, 0.392401], - 0)), LabeledSample(([0.555604, 0.517196, 0.474722, 0.445479], - 1)), LabeledSample(([0.592542, 0.537541, 0.468725, 0.374486], - 1)), LabeledSample(([0.552254, 0.51027, 0.511855, 0.415505], - 0)), LabeledSample(([0.530874, 0.465606, 0.503344, 0.498025], - 0)), LabeledSample(([0.568502, 0.492452, 0.524331, 0.399215], - 0)), LabeledSample(([0.511768, 0.53197, 0.46875, 0.485156], - 0)), LabeledSample(([0.555756, 0.420141, 0.553663, 0.456152], - 0)), LabeledSample(([0.584546, 0.562276, 0.439516, 0.385976], - 1)), LabeledSample(([0.608485, 0.577022, 0.427781, 0.337336], - 1)), LabeledSample(([0.546234, 0.59768, 0.46082, 0.36339], - 1)), LabeledSample(([0.596632, 0.510739, 0.482188, 0.388162], - 1)), LabeledSample(([0.512997, 0.525043, 0.460839, 0.49879], - 0)), LabeledSample(([0.477408, 0.488846, 0.465015, 0.562914], - 0)), LabeledSample(([0.553381, 0.457028, 0.546788, 0.431182], - 0)), LabeledSample(([0.543981, 0.555533, 0.491698, 0.392047], - 1)), LabeledSample(([0.532066, 0.497762, 0.5178, 0.448354], - 1)), LabeledSample(([0.505981, 0.460209, 0.506897, 0.524639], - 0)), LabeledSample(([0.44959, 0.489591, 0.490236, 0.563772], - 0)), LabeledSample(([0.498647, 0.482584, 0.502011, 0.516187], - 0)), LabeledSample(([0.552142, 0.553439, 0.474121, 0.405035], - 1)), LabeledSample(([0.495714, 0.452003, 0.497858, 0.549635], - 0)), LabeledSample(([0.523342, 0.480002, 0.484639, 0.510722], - 0)), LabeledSample(([0.493365, 0.473391, 0.504036, 0.527673], - 0)), LabeledSample(([0.552146, 0.542635, 0.505733, 0.380679], - 1)), LabeledSample(([0.578287, 0.517882, 0.46856, 0.421704], - 1)), LabeledSample(([0.588389, 0.569435, 0.47621, 0.320571], - 1)), LabeledSample(([0.572852, 0.583312, 0.441711, 0.369431], - 1)), LabeledSample(([0.540173, 0.571013, 0.440259, 0.43397], - 1)), LabeledSample(([0.588118, 0.554021, 0.452409, 0.377498], - 1)), LabeledSample(([0.499325, 0.454156, 0.500229, 0.542391], - 0)), LabeledSample(([0.541172, 0.446455, 0.491748, 0.515746], - 0)), LabeledSample(([0.501365, 0.513378, 0.488352, 0.496577], - 0)), LabeledSample(([0.519525, 0.498491, 0.475854, 0.505137], - 0)), LabeledSample(([0.549086, 0.561405, 0.474075, 0.398223], - 1)), LabeledSample(([0.504199, 0.486123, 0.476877, 0.53109], - 0)), LabeledSample(([0.530715, 0.466196, 0.504931, 0.496032], - 0)), LabeledSample(([0.515663, 0.527232, 0.474253, 0.480835], - 0)), LabeledSample(([0.498647, 0.482584, 0.502011, 0.516187], - 0)), LabeledSample(([0.591455, 0.54028, 0.471969, 0.368136], - 1)), LabeledSample(([0.459772, 0.46144, 0.462874, 0.601191], - 0)), LabeledSample(([0.527031, 0.492257, 0.472236, 0.506867], - 0)), LabeledSample(([0.534498, 0.534498, 0.495766, 0.427598], - 0)), LabeledSample(([0.561849, 0.441966, 0.530269, 0.455857], - 0)), LabeledSample(([0.483984, 0.503088, 0.458885, 0.549624], - 0)), LabeledSample(([0.525126, 0.566848, 0.450923, 0.446761], - 1)), LabeledSample(([0.576674, 0.501348, 0.480182, 0.430723], - 1)), LabeledSample(([0.58787, 0.558697, 0.451917, 0.371534], - 1)), LabeledSample(([0.584716, 0.552543, 0.446305, 0.391937], - 1)), LabeledSample(([0.604866, 0.502993, 0.484769, 0.382275], - 1)), LabeledSample(([0.576834, 0.538774, 0.469003, 0.39626], - 1)), LabeledSample(([0.588747, 0.563029, 0.444888, 0.372089], - 1)), LabeledSample(([0.575899, 0.560012, 0.4573, 0.38158], - 1)), LabeledSample(([0.552402, 0.574207, 0.444699, 0.409123], - 1)), LabeledSample(([0.589006, 0.546658, 0.46965, 0.365605], - 1)), LabeledSample(([0.540387, 0.443462, 0.537296, 0.471843], - 0)), LabeledSample(([0.570654, 0.548912, 0.458326, 0.403716], - 1)), LabeledSample(([0.544644, 0.547271, 0.467682, 0.430268], - 1)), LabeledSample(([0.525228, 0.503964, 0.508832, 0.459615], - 0)), LabeledSample(([0.462827, 0.527655, 0.461528, 0.542553], - 0)), LabeledSample(([0.50897, 0.522189, 0.507054, 0.459527], - 0)), LabeledSample(([0.546369, 0.577899, 0.460934, 0.393768], - 1)), LabeledSample(([0.615382, 0.467063, 0.492079, 0.401268], - 1)), LabeledSample(([0.573572, 0.473185, 0.510765, 0.431544], - 1)), LabeledSample(([0.510624, 0.60155, 0.43847, 0.430285], - 1)), LabeledSample(([0.563956, 0.532924, 0.469591, 0.421223], - 1)), LabeledSample(([0.581565, 0.592669, 0.391677, 0.396376], - 1)), LabeledSample(([0.533848, 0.501219, 0.4732, 0.489762], - 0)), LabeledSample(([0.530036, 0.577194, 0.452731, 0.425375], - 1)), LabeledSample(([0.595573, 0.439349, 0.494919, 0.455325], - 1)), LabeledSample(([0.584424, 0.557699, 0.438769, 0.393576], - 1)), LabeledSample(([0.544759, 0.441244, 0.494108, 0.514196], - 0)), LabeledSample(([0.552072, 0.545641, 0.487013, 0.400388], 1)) - ]; - return LabeledSampleContainer(ret); - } + operation IrisTrainingData() : LabeledSample[] { + return [LabeledSample(([0.581557, 0.562824, 0.447721, 0.380219], 1)), + LabeledSample(([0.570241, 0.544165, 0.503041, 0.354484], + 1)), LabeledSample(([0.510784, 0.475476, 0.453884, 0.554087], + 0)), LabeledSample(([0.492527, 0.473762, 0.471326, 0.557511], + 0)), LabeledSample(([0.543273, 0.501972, 0.518341, 0.429186], + 0)), LabeledSample(([0.520013, 0.485702, 0.440061, 0.547747], + 0)), LabeledSample(([0.585261, 0.545431, 0.462126, 0.382641], + 1)), LabeledSample(([0.541059, 0.479438, 0.568697, 0.392401], + 0)), LabeledSample(([0.555604, 0.517196, 0.474722, 0.445479], + 1)), LabeledSample(([0.592542, 0.537541, 0.468725, 0.374486], + 1)), LabeledSample(([0.552254, 0.51027, 0.511855, 0.415505], + 0)), LabeledSample(([0.530874, 0.465606, 0.503344, 0.498025], + 0)), LabeledSample(([0.568502, 0.492452, 0.524331, 0.399215], + 0)), LabeledSample(([0.511768, 0.53197, 0.46875, 0.485156], + 0)), LabeledSample(([0.555756, 0.420141, 0.553663, 0.456152], + 0)), LabeledSample(([0.584546, 0.562276, 0.439516, 0.385976], + 1)), LabeledSample(([0.608485, 0.577022, 0.427781, 0.337336], + 1)), LabeledSample(([0.546234, 0.59768, 0.46082, 0.36339], + 1)), LabeledSample(([0.596632, 0.510739, 0.482188, 0.388162], + 1)), LabeledSample(([0.512997, 0.525043, 0.460839, 0.49879], + 0)), LabeledSample(([0.477408, 0.488846, 0.465015, 0.562914], + 0)), LabeledSample(([0.553381, 0.457028, 0.546788, 0.431182], + 0)), LabeledSample(([0.543981, 0.555533, 0.491698, 0.392047], + 1)), LabeledSample(([0.532066, 0.497762, 0.5178, 0.448354], + 1)), LabeledSample(([0.505981, 0.460209, 0.506897, 0.524639], + 0)), LabeledSample(([0.44959, 0.489591, 0.490236, 0.563772], + 0)), LabeledSample(([0.498647, 0.482584, 0.502011, 0.516187], + 0)), LabeledSample(([0.552142, 0.553439, 0.474121, 0.405035], + 1)), LabeledSample(([0.495714, 0.452003, 0.497858, 0.549635], + 0)), LabeledSample(([0.523342, 0.480002, 0.484639, 0.510722], + 0)), LabeledSample(([0.493365, 0.473391, 0.504036, 0.527673], + 0)), LabeledSample(([0.552146, 0.542635, 0.505733, 0.380679], + 1)), LabeledSample(([0.578287, 0.517882, 0.46856, 0.421704], + 1)), LabeledSample(([0.588389, 0.569435, 0.47621, 0.320571], + 1)), LabeledSample(([0.572852, 0.583312, 0.441711, 0.369431], + 1)), LabeledSample(([0.540173, 0.571013, 0.440259, 0.43397], + 1)), LabeledSample(([0.588118, 0.554021, 0.452409, 0.377498], + 1)), LabeledSample(([0.499325, 0.454156, 0.500229, 0.542391], + 0)), LabeledSample(([0.541172, 0.446455, 0.491748, 0.515746], + 0)), LabeledSample(([0.501365, 0.513378, 0.488352, 0.496577], + 0)), LabeledSample(([0.519525, 0.498491, 0.475854, 0.505137], + 0)), LabeledSample(([0.549086, 0.561405, 0.474075, 0.398223], + 1)), LabeledSample(([0.504199, 0.486123, 0.476877, 0.53109], + 0)), LabeledSample(([0.530715, 0.466196, 0.504931, 0.496032], + 0)), LabeledSample(([0.515663, 0.527232, 0.474253, 0.480835], + 0)), LabeledSample(([0.498647, 0.482584, 0.502011, 0.516187], + 0)), LabeledSample(([0.591455, 0.54028, 0.471969, 0.368136], + 1)), LabeledSample(([0.459772, 0.46144, 0.462874, 0.601191], + 0)), LabeledSample(([0.527031, 0.492257, 0.472236, 0.506867], + 0)), LabeledSample(([0.534498, 0.534498, 0.495766, 0.427598], + 0)), LabeledSample(([0.561849, 0.441966, 0.530269, 0.455857], + 0)), LabeledSample(([0.483984, 0.503088, 0.458885, 0.549624], + 0)), LabeledSample(([0.525126, 0.566848, 0.450923, 0.446761], + 1)), LabeledSample(([0.576674, 0.501348, 0.480182, 0.430723], + 1)), LabeledSample(([0.58787, 0.558697, 0.451917, 0.371534], + 1)), LabeledSample(([0.584716, 0.552543, 0.446305, 0.391937], + 1)), LabeledSample(([0.604866, 0.502993, 0.484769, 0.382275], + 1)), LabeledSample(([0.576834, 0.538774, 0.469003, 0.39626], + 1)), LabeledSample(([0.588747, 0.563029, 0.444888, 0.372089], + 1)), LabeledSample(([0.575899, 0.560012, 0.4573, 0.38158], + 1)), LabeledSample(([0.552402, 0.574207, 0.444699, 0.409123], + 1)), LabeledSample(([0.589006, 0.546658, 0.46965, 0.365605], + 1)), LabeledSample(([0.540387, 0.443462, 0.537296, 0.471843], + 0)), LabeledSample(([0.570654, 0.548912, 0.458326, 0.403716], + 1)), LabeledSample(([0.544644, 0.547271, 0.467682, 0.430268], + 1)), LabeledSample(([0.525228, 0.503964, 0.508832, 0.459615], + 0)), LabeledSample(([0.462827, 0.527655, 0.461528, 0.542553], + 0)), LabeledSample(([0.50897, 0.522189, 0.507054, 0.459527], + 0)), LabeledSample(([0.546369, 0.577899, 0.460934, 0.393768], + 1)), LabeledSample(([0.615382, 0.467063, 0.492079, 0.401268], + 1)), LabeledSample(([0.573572, 0.473185, 0.510765, 0.431544], + 1)), LabeledSample(([0.510624, 0.60155, 0.43847, 0.430285], + 1)), LabeledSample(([0.563956, 0.532924, 0.469591, 0.421223], + 1)), LabeledSample(([0.581565, 0.592669, 0.391677, 0.396376], + 1)), LabeledSample(([0.533848, 0.501219, 0.4732, 0.489762], + 0)), LabeledSample(([0.530036, 0.577194, 0.452731, 0.425375], + 1)), LabeledSample(([0.595573, 0.439349, 0.494919, 0.455325], + 1)), LabeledSample(([0.584424, 0.557699, 0.438769, 0.393576], + 1)), LabeledSample(([0.544759, 0.441244, 0.494108, 0.514196], + 0)), LabeledSample(([0.552072, 0.545641, 0.487013, 0.400388], 1)) + ]; + } - operation Examples () : Unit + operation Examples () : Unit { - + } } From 99541d53b69aac171927cd7ebbe2d0dfca63fce1 Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 26 Dec 2019 14:12:43 -0800 Subject: [PATCH 19/21] More progress removing private fns and ops from public API. --- MachineLearning/src/Runtime/Classification.qs | 16 ++++- .../{Circuits.qs => GradientEstimation.qs} | 63 +++---------------- MachineLearning/src/Runtime/InputEncoding.qs | 29 ++++++--- MachineLearning/src/Runtime/Utils.qs | 5 ++ 4 files changed, 49 insertions(+), 64 deletions(-) rename MachineLearning/src/Runtime/{Circuits.qs => GradientEstimation.qs} (71%) diff --git a/MachineLearning/src/Runtime/Classification.qs b/MachineLearning/src/Runtime/Classification.qs index 40c86a40e3c..aa3d9fae7e2 100644 --- a/MachineLearning/src/Runtime/Classification.qs +++ b/MachineLearning/src/Runtime/Classification.qs @@ -7,6 +7,18 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Convert; + + operation _PrepareClassification( + encoder : (LittleEndian => Unit is Adj + Ctl), + parameters : Double[], + gates : GateSequence, + target : Qubit[] + ) + : Unit is Adj { + encoder(LittleEndian(target)); + _ApplyGates(parameters, gates, target); + } + operation EstimateClassificationProbability( tolerance: Double, parameters : Double[], @@ -19,8 +31,8 @@ namespace Microsoft.Quantum.MachineLearning { let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); let encodedSample = StateGenerator(nQubits, circEnc); return 1.0 - EstimateFrequencyA( - endToEndPreparation(encodedSample::Apply, parameters,gates), - measureLastQubit(encodedSample::NQubits), + _PrepareClassification(encodedSample::Apply, parameters, gates, _), + _TailMeasurement(encodedSample::NQubits), encodedSample::NQubits, nMeasurements ); diff --git a/MachineLearning/src/Runtime/Circuits.qs b/MachineLearning/src/Runtime/GradientEstimation.qs similarity index 71% rename from MachineLearning/src/Runtime/Circuits.qs rename to MachineLearning/src/Runtime/GradientEstimation.qs index 049ba51211c..fc77a14a306 100644 --- a/MachineLearning/src/Runtime/Circuits.qs +++ b/MachineLearning/src/Runtime/GradientEstimation.qs @@ -12,47 +12,11 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Characterization; - /// WARNING: the downstream EstimateFrequencyA counts the frequency of Zero - - operation measureLastQubit(nQubits : Int): (Qubit[] => Result) { - let paulis = ConstantArray(nQubits, PauliI) w/ (nQubits - 1) <- PauliZ; - return Measure(paulis, _); - } - - operation _endToEndPreparation(enc: (LittleEndian => Unit is Adj + Ctl), parameters: Double[], gates: GateSequence, reg: Qubit[]): Unit is Adj - { - enc(LittleEndian(reg)); - _ApplyGates(parameters, gates, reg); - } - - operation endToEndPreparation(enc: (LittleEndian => Unit is Adj + Ctl), parameters: Double[], gates: GateSequence) : (Qubit[] => Unit is Adj) - { - return _endToEndPreparation(enc,parameters, gates, _); - } - - function collectNegativeLocs(cNegative: Int, coefficients : ComplexPolar[]) : Int[] - { - mutable negLocs = ConstantArray(cNegative, -1); - mutable nlx = 0; - for (idx in 0 .. Length(coefficients) - 1) - { - let (r,a) = (coefficients[idx])!; - if (AbsD(a - PI()) < 1E-9) { - if (nlx < cNegative) - { - set negLocs w/= nlx <- idx; - set nlx = nlx+1; - } - } - } - return negLocs; - } //collectNegativeLocs - // NOTE: the last qubit of 'reg' in this context is the auxillary qubit used in the Hadamard test. operation _endToEndHTcircuit(enc: (LittleEndian => Unit is Adj + Ctl), param1 : Double[], gates1: GateSequence, param2 : Double[], gates2: GateSequence, reg: Qubit[]): Unit is Adj + Ctl { let L = Length(reg) - 1; - let g1 = _ApplyGates(param1,gates1,_); - let g2 = _ApplyGates(param2,gates2,_); + let g1 = _ApplyGates(param1, gates1, _); + let g2 = _ApplyGates(param2, gates2, _); enc(LittleEndian(reg[0..(L-1)])); within { @@ -72,9 +36,13 @@ namespace Microsoft.Quantum.MachineLearning { return _endToEndHTcircuit(enc,param1, gates1, param2, gates2, _); } - operation HardamardTestPhysical(enc2: (LittleEndian => Unit is Adj + Ctl), param1 : Double[], gates1: GateSequence, param2 : Double[], gates2: GateSequence, nQubits: Int, nMeasurements : Int): Double - { - return 1.0-EstimateFrequencyA(endToEndHTcircuit(enc2,param1,gates1,param2,gates2),measureLastQubit(nQubits), nQubits, nMeasurements); + operation HardamardTestPhysical(enc2: (LittleEndian => Unit is Adj + Ctl), param1 : Double[], gates1: GateSequence, param2 : Double[], gates2: GateSequence, nQubits: Int, nMeasurements : Int): Double { + return 1.0 - EstimateFrequencyA( + endToEndHTcircuit(enc2,param1,gates1,param2,gates2), + _TailMeasurement(nQubits), + nQubits, + nMeasurements + ); } @@ -172,17 +140,4 @@ namespace Microsoft.Quantum.MachineLearning { return EstimateGradient(param, gates, sg, nMeasurements); } - //Csharp-frendly adapter for gradient estimation - //'gates' is a array of "flattened" controlled rotation defitions - //each such definition is Int[no.controls+3] in the format [parameter index, Pauli index, target index <,control qubit indices>] - //Pauli index is: 0 for I, 1 for X, 2 for y, 3 for Z - //target index is the index of the target qubit of the rotation - //Sequence of can be empty for uncontroled - operation GradientClassicalSimulationAdapter(tolerance: Double, param : Double[], gates: Int[][], sample: Double[]) : (Double[]) - { - - return EstimateGradientFromClassicalSample(tolerance, param,unFlattenGateSequence(gates),sample,0); - - } - } diff --git a/MachineLearning/src/Runtime/InputEncoding.qs b/MachineLearning/src/Runtime/InputEncoding.qs index 201b1d5e3be..730fa2d98e8 100644 --- a/MachineLearning/src/Runtime/InputEncoding.qs +++ b/MachineLearning/src/Runtime/InputEncoding.qs @@ -2,6 +2,7 @@ // Licensed under the MIT License. namespace Microsoft.Quantum.MachineLearning { + open Microsoft.Quantum.Arrays; open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Convert; open Microsoft.Quantum.Math; @@ -35,19 +36,31 @@ namespace Microsoft.Quantum.MachineLearning { return ret; } + function _NegativeLocations(cNegative: Int, coefficients : ComplexPolar[]) : Int[] { + mutable negLocs = new Int[0]; + for ((idx, coefficient) in Enumerated(coefficients)) { + if (AbsD(coefficient::Argument - PI()) < 1E-9) { + set negLocs += [idx]; + } + } + return Length(negLocs) > cNegative ? negLocs[...cNegative - 1] | negLocs; + } + /// Do special processing on the first cNegative entries - operation _EncodeSparseNegativeInput(cNegative: Int, tolerance: Double,coefficients : ComplexPolar[], reg: LittleEndian): Unit is Adj + Ctl - { - let negLocs = collectNegativeLocs(cNegative, coefficients); + operation _EncodeSparseNegativeInput( + cNegative: Int, + tolerance: Double, + 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 (ineg in 0..(cNegative - 1)) { - let jx = negLocs[ineg]; - if (jx > -1) { - ReflectAboutInteger(jx, reg); //TODO:REVIEW: this assumes that 2^Length(reg) is the minimal pad to Length(coefficients) - } + for (idxNegative in negLocs) { + ReflectAboutInteger(idxNegative, reg); //TODO:REVIEW: this assumes that 2^Length(reg) is the minimal pad to Length(coefficients) } } diff --git a/MachineLearning/src/Runtime/Utils.qs b/MachineLearning/src/Runtime/Utils.qs index 6a97180404d..c412f05461d 100644 --- a/MachineLearning/src/Runtime/Utils.qs +++ b/MachineLearning/src/Runtime/Utils.qs @@ -9,4 +9,9 @@ namespace Microsoft.Quantum.MachineLearning { return Length(v1) == Length(v2) and All(NearlyEqualD, Zip(v1, v2)); } + operation _TailMeasurement(nQubits : Int) : (Qubit[] => Result) { + let paulis = ConstantArray(nQubits, PauliI) w/ (nQubits - 1) <- PauliZ; + return Measure(paulis, _); + } + } From 9cf2a6d54d99c89bcd1caf89069c6dfb5e895a6e Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Fri, 27 Dec 2019 09:29:44 -0800 Subject: [PATCH 20/21] Further consolidated API surface. --- .../src/Runtime/GradientEstimation.qs | 53 ++++++------------- MachineLearning/src/Runtime/Training.qs | 31 ++++++----- MachineLearning/src/Runtime/Types.qs | 5 +- 3 files changed, 37 insertions(+), 52 deletions(-) diff --git a/MachineLearning/src/Runtime/GradientEstimation.qs b/MachineLearning/src/Runtime/GradientEstimation.qs index fc77a14a306..92b340a6e19 100644 --- a/MachineLearning/src/Runtime/GradientEstimation.qs +++ b/MachineLearning/src/Runtime/GradientEstimation.qs @@ -67,7 +67,13 @@ namespace Microsoft.Quantum.MachineLearning { /// # Output /// the gradient /// - operation EstimateGradient(param : Double[], gates: GateSequence, sg: StateGenerator, nMeasurements : Int) : (Double[]) { + operation EstimateGradient( + gates : GateSequence, + param : Double[], + sg : StateGenerator, + nMeasurements : Int + ) + : (Double[]) { //Synopsis: Suppose (param,gates) define Circ0 //Suppose (param1,gates1) define Circ1 that implements one-gate derivative of Circ0 //The expectation derivative is then 2 Re[] = @@ -81,13 +87,14 @@ namespace Microsoft.Quantum.MachineLearning { //Now, suppose a gate at which we differentiate is the (Controlled R(\theta))([k0,k1,...,kr],[target]) //and we want a unitary description of its \theta-derivative. It can be written as // 1/2 {(Controlled R(\theta'))([k0,k1,...,kr],[target]) - (Controlled Z)([k1,...,kr],[k0])(Controlled R(\theta'))([k0,k1,...,kr],[target])} - let pC = Length(param); - mutable grad = ConstantArray(pC, 0.0); - mutable paramShift = param + [0.0]; + mutable grad = ConstantArray(Length(param), 0.0); let nQubits = MaxI(NQubitsRequired(gates), sg::NQubits); for (gate in gates!) { - set paramShift w/= gate::Index <- (param[gate::Index] + PI()); //Shift the corresponding parameter + let paramShift = (param + [0.0]) + // Shift the corresponding parameter. + w/ gate::Index <- (param[gate::Index] + PI()); + // NB: This the *antiderivative* of the bracket let newDer = 2.0 * HardamardTestPhysical( sg::Apply, param, gates, paramShift, gates, nQubits + 1, nMeasurements @@ -97,47 +104,19 @@ namespace Microsoft.Quantum.MachineLearning { set grad w/= gate::Index <- grad[gate::Index] + newDer; } else { //controlled gate - set paramShift w/=gate::Index<-(param[gate::Index]+3.0 * PI()); + let controlledShift = paramShift + w/ gate::Index <- (param[gate::Index] + 3.0 * PI()); //Assumption: any rotation R has the property that R(\theta+2 Pi)=(-1).R(\theta) // NB: This the *antiderivative* of the bracket let newDer1 = 2.0 * HardamardTestPhysical( - sg::Apply, param, gates, paramShift, gates, nQubits + 1, + sg::Apply, param, gates, controlledShift, gates, nQubits + 1, nMeasurements ) - 1.0; - set grad w/= gate::Index <- (grad[gate::Index] + 0.5* (newDer - newDer1)); - set paramShift w/= gate::Index <-( param[gate::Index] + PI()); //unshift by 2 Pi (for debugging purposes) + set grad w/= gate::Index <- (grad[gate::Index] + 0.5 * (newDer - newDer1)); } - set paramShift w/= gate::Index <- param[gate::Index]; //unshift this parameter } return grad; - } //GradientHack - - - /// # Summary - /// computes stochastic gradient on one classical sample - /// - /// # Input - /// ## param - /// circuit parameters - /// - /// ## gates - /// sequence of gates in the circuits - /// - /// ## sample - /// sample vector as a raw array - /// - /// ## nMeasurements - /// number of true quantum measurements to estimate probabilities - /// - /// # Output - /// the gradient - /// - operation EstimateGradientFromClassicalSample(tolerance: Double, param : Double[], gates: GateSequence, sample: Double[], nMeasurements : Int) : (Double[]) { - let nQubits = MaxI(FeatureRegisterSize(sample), NQubitsRequired(gates)); - let circEnc = NoisyInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample); - let sg = StateGenerator(nQubits, circEnc); - return EstimateGradient(param, gates, sg, nMeasurements); } } diff --git a/MachineLearning/src/Runtime/Training.qs b/MachineLearning/src/Runtime/Training.qs index 4dfa1aaecaf..9acea0d7dc6 100644 --- a/MachineLearning/src/Runtime/Training.qs +++ b/MachineLearning/src/Runtime/Training.qs @@ -62,7 +62,7 @@ namespace Microsoft.Quantum.MachineLearning { for (idxStart in 0..(Length(parameterSource) - 1)) { Message($"Beginning training at start point #{idxStart}..."); - let proposedUpdate = StochasticTrainingLoop( + let proposedUpdate = TrainSequentialClassifierAtModel( gates, SequentialModel(parameterSource[idxStart], 0.0), samples, options, trainingSchedule, 1 ); @@ -119,14 +119,20 @@ namespace Microsoft.Quantum.MachineLearning { ) : (Double, Double[]) { mutable batchGradient = ConstantArray(Length(param), 0.0); + let nQubits = MaxI(FeatureRegisterSize(miniBatch[0]::Features), NQubitsRequired(gates)); + let effectiveTolerance = options::Tolerance / IntAsDouble(Length(gates!)); - for (samp in miniBatch) { - mutable err = IntAsDouble(samp::Label); + for (sample in 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 } - let grad = EstimateGradientFromClassicalSample( - options::Tolerance, param, gates, samp::Features, + let stateGenerator = StateGenerator( + nQubits, + NoisyInputEncoder(effectiveTolerance, sample::Features) + ); + let grad = EstimateGradient( + gates, param, stateGenerator, options::NMeasurements ); for (ip in 0..(Length(param) - 1)) { @@ -290,17 +296,16 @@ namespace Microsoft.Quantum.MachineLearning { /// # Output /// ((no.hits,no.misses),(opt.bias,opt.parameters)) /// - operation StochasticTrainingLoop( - gates: GateSequence, + operation TrainSequentialClassifierAtModel( + gates : GateSequence, model : SequentialModel, - samples: LabeledSample[], + samples : LabeledSample[], options : TrainingOptions, - schedule: SamplingSchedule, - periodScore: Int + schedule : SamplingSchedule, + periodScore : Int ) : SequentialModel { //const - let relFuzz = 0.01; let nSamples = Length(samples); let features = Mapped(_Features, samples); let actualLabels = Mapped(_Label, samples); @@ -367,8 +372,8 @@ namespace Microsoft.Quantum.MachineLearning { // and bias before updating. if (nStalls > options::MaxStalls / 2) { set current = SequentialModel( - ForEach(_RandomlyRescale(relFuzz, _), proposedUpdate::Parameters), - _RandomlyRescale(relFuzz, proposedUpdate::Bias) + ForEach(_RandomlyRescale(options::StochasticRescaleFactor, _), proposedUpdate::Parameters), + _RandomlyRescale(options::StochasticRescaleFactor, proposedUpdate::Bias) ); } } else { diff --git a/MachineLearning/src/Runtime/Types.qs b/MachineLearning/src/Runtime/Types.qs index 1d0f90b8a60..3d8ca3ab9a5 100644 --- a/MachineLearning/src/Runtime/Types.qs +++ b/MachineLearning/src/Runtime/Types.qs @@ -95,12 +95,13 @@ namespace Microsoft.Quantum.MachineLearning { MinibatchSize: Int, NMeasurements: Int, MaxEpochs: Int, - MaxStalls: Int + MaxStalls: Int, + StochasticRescaleFactor: Double ); function DefaultTrainingOptions() : TrainingOptions { return TrainingOptions( - 0.1, 0.005, 15, 10000, 16, 8 + 0.1, 0.005, 15, 10000, 16, 8, 0.01 ); } From 77ff4eb16335e753de4d3562623b4b23e69f715b Mon Sep 17 00:00:00 2001 From: Christopher Granade Date: Thu, 2 Jan 2020 11:45:24 -0800 Subject: [PATCH 21/21] Addressing feedback. --- MachineLearning/src/DataModel/Interop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MachineLearning/src/DataModel/Interop.cs b/MachineLearning/src/DataModel/Interop.cs index d1a9a04a692..a59d812d233 100644 --- a/MachineLearning/src/DataModel/Interop.cs +++ b/MachineLearning/src/DataModel/Interop.cs @@ -176,8 +176,8 @@ public static List PartialLocalLayer(long[] indices, char pauli) /// /// Creates a cyclic block of nQubits controlled rotations that starts - /// with contol qubit (nQubits-1), target qubit (cspan-1) % n , followed by the - /// ladder of entanglers with control qubit iq and target qubit (iq+cspan) % n + /// with control qubit (nQubits-1), target qubit (cspan-1) % nQubits , followed by a + /// ladder of entanglers with control qubits iq and target qubit (iq+cspan) % nQubits /// /// Number of qubits to entangle ///