From 621554300e852c95ce1b3c5c2e66b074d69cb753 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Thu, 10 Mar 2022 17:27:53 +0100 Subject: [PATCH 1/2] Improve code quality. --- MachineLearning/src/Classification.qs | 6 ++-- MachineLearning/src/GradientEstimation.qs | 17 ++++-------- MachineLearning/src/InputEncoding.qs | 34 +++++++++++------------ MachineLearning/src/Private.qs | 6 ++-- MachineLearning/src/Structure.qs | 27 +++++------------- MachineLearning/src/Training.qs | 34 +++++++++++------------ MachineLearning/tests/StructureTests.qs | 2 +- 7 files changed, 54 insertions(+), 72 deletions(-) diff --git a/MachineLearning/src/Classification.qs b/MachineLearning/src/Classification.qs index f9968cc4c40..2b696de553b 100644 --- a/MachineLearning/src/Classification.qs +++ b/MachineLearning/src/Classification.qs @@ -10,7 +10,7 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Convert; - internal operation _PrepareClassification( + internal operation PrepareClassification( encoder : (LittleEndian => Unit is Adj + Ctl), model : SequentialModel, target : Qubit[] @@ -48,8 +48,8 @@ namespace Microsoft.Quantum.MachineLearning { : Double { let encodedSample = ApproximateInputEncoder(tolerance / IntAsDouble(Length(model::Structure)), sample); return 1.0 - EstimateFrequencyA( - _PrepareClassification(encodedSample::Prepare, model, _), - _TailMeasurement(encodedSample::NQubits), + PrepareClassification(encodedSample::Prepare, model, _), + TailMeasurement(encodedSample::NQubits), encodedSample::NQubits, nMeasurements ); diff --git a/MachineLearning/src/GradientEstimation.qs b/MachineLearning/src/GradientEstimation.qs index b3a56242684..b99f6f31aaf 100644 --- a/MachineLearning/src/GradientEstimation.qs +++ b/MachineLearning/src/GradientEstimation.qs @@ -12,12 +12,7 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Preparation; open Microsoft.Quantum.Characterization; - // NOTE: the last qubit of 'reg' in this context is the auxiliary qubit used in the Hadamard test. - internal operation _ApplyLEOperationToRawRegister(op : (LittleEndian => Unit is Adj), target : Qubit[]) : Unit is Adj { - op(LittleEndian(target)); - } - - internal operation _EstimateDerivativeWithParameterShift( + internal operation EstimateDerivativeWithParameterShift( inputEncoder : StateGenerator, model : SequentialModel, parameters : (Double[], Double[]), @@ -25,7 +20,7 @@ namespace Microsoft.Quantum.MachineLearning { nMeasurements : Int ) : Double { return EstimateRealOverlapBetweenStates( - _ApplyLEOperationToRawRegister(inputEncoder::Prepare, _), + target => inputEncoder::Prepare(LittleEndian(target)), ApplySequentialClassifier(model w/ Parameters <- Fst(parameters), _), ApplySequentialClassifier(model w/ Parameters <- Snd(parameters), _), nQubits, nMeasurements @@ -71,7 +66,7 @@ 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])} - mutable grad = ConstantArray(Length(model::Parameters), 0.0); + mutable grad = [0.0, size = Length(model::Parameters)]; let nQubits = MaxI(NQubitsRequired(model), encodedInput::NQubits); for gate in model::Structure { @@ -80,10 +75,10 @@ namespace Microsoft.Quantum.MachineLearning { w/ gate::ParameterIndex <- (model::Parameters[gate::ParameterIndex] + PI()); // NB: This the *antiderivative* of the bracket - let newDer = _EstimateDerivativeWithParameterShift( + let newDer = EstimateDerivativeWithParameterShift( encodedInput, model, (model::Parameters, paramShift), nQubits, nMeasurements ); - if (IsEmpty(gate::ControlIndices)) { + if IsEmpty(gate::ControlIndices) { //uncontrolled gate set grad w/= gate::ParameterIndex <- grad[gate::ParameterIndex] + newDer; } else { @@ -92,7 +87,7 @@ namespace Microsoft.Quantum.MachineLearning { w/ gate::ParameterIndex <- (model::Parameters[gate::ParameterIndex] + 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 = _EstimateDerivativeWithParameterShift( + let newDer1 = EstimateDerivativeWithParameterShift( encodedInput, model, (model::Parameters, controlledShift), nQubits, nMeasurements ); set grad w/= gate::ParameterIndex <- (grad[gate::ParameterIndex] + 0.5 * (newDer - newDer1)); diff --git a/MachineLearning/src/InputEncoding.qs b/MachineLearning/src/InputEncoding.qs index 0f9294cbd8e..c00e85afd3d 100644 --- a/MachineLearning/src/InputEncoding.qs +++ b/MachineLearning/src/InputEncoding.qs @@ -10,11 +10,11 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Intrinsic; open Microsoft.Quantum.Canon; - internal function _CanApplyTwoQubitCase(datum: Double[]) : Bool { - return((Length(datum)==4) and (Microsoft.Quantum.Math.AbsD(datum[0]*datum[3]-datum[1]*datum[2])< 1E-12) and (Microsoft.Quantum.Math.AbsD(datum[0])> 1E-4)); + internal function CanApplyTwoQubitCase(datum: Double[]) : Bool { + return ((Length(datum)==4) and (Microsoft.Quantum.Math.AbsD(datum[0]*datum[3]-datum[1]*datum[2])< 1E-12) and (Microsoft.Quantum.Math.AbsD(datum[0])> 1E-4)); } - internal operation _ApplyTwoQubitCase(datum: Double[], reg: LittleEndian) : Unit is Adj + Ctl { + internal operation ApplyTwoQubitCase(datum: Double[], reg: LittleEndian) : Unit is Adj + Ctl { let x = datum[1]/datum[0]; let y = datum[2]/datum[0]; // we now encoding [1,x,y,x*y] @@ -24,7 +24,7 @@ namespace Microsoft.Quantum.MachineLearning { R(PauliY, ax, (reg!)[0]); } - internal function _Unnegate(negLocs: Int[], coefficients : ComplexPolar[]) : ComplexPolar[] { + internal function Unnegate(negLocs: Int[], coefficients : ComplexPolar[]) : ComplexPolar[] { mutable ret = coefficients; for idxNegative in negLocs { if idxNegative >= Length(coefficients) { @@ -36,10 +36,10 @@ namespace Microsoft.Quantum.MachineLearning { return ret; } - internal function _NegativeLocations(cNegative: Int, coefficients : ComplexPolar[]) : Int[] { + internal function NegativeLocations(cNegative: Int, coefficients : ComplexPolar[]) : Int[] { mutable negLocs = []; for (idx, coefficient) in Enumerated(coefficients) { - if (AbsD(coefficient::Argument - PI()) < 1E-9) { + if AbsD(coefficient::Argument - PI()) < 1E-9 { set negLocs += [idx]; } } @@ -47,7 +47,7 @@ namespace Microsoft.Quantum.MachineLearning { } /// Do special processing on the first cNegative entries - internal operation _ReflectAboutNegativeCoefficients( + internal operation ReflectAboutNegativeCoefficients( negLocs : Int[], coefficients : ComplexPolar[], reg: LittleEndian @@ -96,11 +96,11 @@ namespace Microsoft.Quantum.MachineLearning { mutable cNegative = 0; for (idx, coef) in Enumerated(coefficients) { mutable magnitude = coef; - if (tolerance > 1E-9) { + if tolerance > 1E-9 { set magnitude = tolerance * IntAsDouble(Round(coefficients[idx] / tolerance)); //quantization } mutable ang = 0.0; - if (magnitude < 0.0) { + if magnitude < 0.0 { set cNegative += 1; set magnitude = -magnitude; set ang = PI(); @@ -109,8 +109,8 @@ namespace Microsoft.Quantum.MachineLearning { } // Check if we can apply the explicit two-qubit case. - if (_CanApplyTwoQubitCase(coefficients)) { - return StateGenerator(2, _ApplyTwoQubitCase(coefficients, _)); + if CanApplyTwoQubitCase(coefficients) { + return StateGenerator(2, ApplyTwoQubitCase(coefficients, _)); } let nQubits = FeatureRegisterSize(coefficients); @@ -119,18 +119,18 @@ namespace Microsoft.Quantum.MachineLearning { // there are only a few negative coefficients. // Here, by a "few," we mean fewer than the number of qubits required // to encode features. - if ((cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0)) { - let negLocs = _NegativeLocations(cNegative, complexCoefficients); + if (cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0) { + let negLocs = NegativeLocations(cNegative, complexCoefficients); return StateGenerator( nQubits, BoundCA([ // Prepare the state disregarding the sign of negative components. _CompileApproximateArbitraryStatePreparation( - tolerance, _Unnegate(negLocs, complexCoefficients), nQubits + tolerance, Unnegate(negLocs, complexCoefficients), nQubits ), // Reflect about the negative coefficients to apply the negative signs // at the end. - _ReflectAboutNegativeCoefficients(negLocs, complexCoefficients, _) + ReflectAboutNegativeCoefficients(negLocs, complexCoefficients, _) ]) ); } @@ -165,8 +165,8 @@ namespace Microsoft.Quantum.MachineLearning { | (-coefficient, PI()) ); } - if (_CanApplyTwoQubitCase(coefficients)) { - return StateGenerator(2, _ApplyTwoQubitCase(coefficients, _)); + if CanApplyTwoQubitCase(coefficients) { + return StateGenerator(2, ApplyTwoQubitCase(coefficients, _)); } //this is preparing the state almost exactly so far return StateGenerator( diff --git a/MachineLearning/src/Private.qs b/MachineLearning/src/Private.qs index 4feba8ae094..79f77a085e7 100644 --- a/MachineLearning/src/Private.qs +++ b/MachineLearning/src/Private.qs @@ -8,12 +8,12 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Math; - internal function _AllNearlyEqualD(v1 : Double[], v2 : Double[]) : Bool { + internal function AllNearlyEqualD(v1 : Double[], v2 : Double[]) : Bool { return Length(v1) == Length(v2) and All(NearlyEqualD, Zipped(v1, v2)); } - internal function _TailMeasurement(nQubits : Int) : (Qubit[] => Result) { - let paulis = ConstantArray(nQubits, PauliI) w/ (nQubits - 1) <- PauliZ; + internal function TailMeasurement(nQubits : Int) : (Qubit[] => Result) { + let paulis = [PauliI, size = nQubits] w/ (nQubits - 1) <- PauliZ; return Measure(paulis, _); } diff --git a/MachineLearning/src/Structure.qs b/MachineLearning/src/Structure.qs index b6bf2503339..06d874c63dc 100644 --- a/MachineLearning/src/Structure.qs +++ b/MachineLearning/src/Structure.qs @@ -44,7 +44,7 @@ namespace Microsoft.Quantum.MachineLearning { model : SequentialModel, qubits : Qubit[] ) - : (Unit) is Adj + Ctl { + : Unit is Adj + Ctl { for gate in model::Structure { if gate::ParameterIndex < Length(model::Parameters) { let input = (gate::Axis, model::Parameters[gate::ParameterIndex], qubits[gate::TargetIndex]); @@ -58,26 +58,13 @@ namespace Microsoft.Quantum.MachineLearning { } } - function _UncontrolledSpanSequence(idxsQubits : Int[]) : (Int, Int[])[] { + internal function UncontrolledSpanSequence(idxsQubits : Int[]) : (Int, Int[])[] { return Zipped( idxsQubits, - ConstantArray(Length(idxsQubits), []) + [[], size = Length(idxsQubits)] ); } - function _CallFlipped<'TInput1, 'TInput2, 'TOutput>( - fn : (('TInput1, 'TInput2) -> 'TOutput), - y : 'TInput2, x : 'TInput1 - ) : 'TOutput { - return fn(x, y); - } - - function _Flipped<'TInput1, 'TInput2, 'TOutput>( - fn : (('TInput1, 'TInput2) -> 'TOutput) - ) : (('TInput2, 'TInput1) -> 'TOutput) { - return _CallFlipped(fn, _, _); - } - /// # Summary /// Returns an array of uncontrolled (single-qubit) rotations along a given /// axis, with one rotation for each qubit in a register, parameterized by @@ -95,9 +82,9 @@ namespace Microsoft.Quantum.MachineLearning { function LocalRotationsLayer(nQubits : Int, axis : Pauli) : ControlledRotation[] { // [parameterIndex, pauliCode, targetQubit\,sequence of control qubits\] return Mapped( - _Flipped(ControlledRotation(_, axis, _)), + (idx, seq) -> ControlledRotation(seq, axis, idx), Enumerated( - _UncontrolledSpanSequence(SequenceI(0, nQubits - 1)) + UncontrolledSpanSequence(SequenceI(0, nQubits - 1)) ) ); } @@ -118,9 +105,9 @@ namespace Microsoft.Quantum.MachineLearning { /// `nQubits` qubits. function PartialRotationsLayer(idxsQubits : Int[], axis : Pauli) : ControlledRotation[] { return Mapped( - _Flipped(ControlledRotation(_, axis, _)), + (idx, seq) -> ControlledRotation(seq, axis, idx), Enumerated( - _UncontrolledSpanSequence(idxsQubits) + UncontrolledSpanSequence(idxsQubits) ) ); } diff --git a/MachineLearning/src/Training.qs b/MachineLearning/src/Training.qs index 0d6fe44488a..807f27f4d62 100644 --- a/MachineLearning/src/Training.qs +++ b/MachineLearning/src/Training.qs @@ -11,14 +11,14 @@ namespace Microsoft.Quantum.MachineLearning { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Optimization; - internal function _MisclassificationRate(probabilities : Double[], labels : Int[], bias : Double) : Double { + internal function MisclassificationRate(probabilities : Double[], labels : Int[], bias : Double) : Double { let proposedLabels = InferredLabels(bias, probabilities); return IntAsDouble(NMisclassifications(proposedLabels, labels)) / IntAsDouble(Length(probabilities)); } /// # Summary /// Returns a bias value that leads to near-minimum misclassification score. - internal function _UpdatedBias(labeledProbabilities: (Double, Int)[], bias: Double, tolerance: Double) : Double { + internal function UpdatedBias(labeledProbabilities: (Double, Int)[], bias: Double, tolerance: Double) : Double { mutable (min1, max0) = (1.0, 0.0); // Find the range of classification probabilities for each class. @@ -42,7 +42,7 @@ namespace Microsoft.Quantum.MachineLearning { // If we can't find a perfect classification, minimize to find // the best feasible bias. let optimum = LocalUnivariateMinimum( - _MisclassificationRate(Mapped(Fst, labeledProbabilities), Mapped(Snd, labeledProbabilities), _), + MisclassificationRate(Mapped(Fst, labeledProbabilities), Mapped(Snd, labeledProbabilities), _), (0.5 - max0, 0.5 - min1), tolerance ); @@ -122,13 +122,13 @@ namespace Microsoft.Quantum.MachineLearning { /// /// # Output /// (utility, (new)parameters) pair - internal operation _RunSingleTrainingStep( + internal operation RunSingleTrainingStep( miniBatch : (LabeledSample, StateGenerator)[], options : TrainingOptions, model : SequentialModel ) : (Double, SequentialModel) { - mutable batchGradient = ConstantArray(Length(model::Parameters), 0.0); + mutable batchGradient = [0.0, size = Length(model::Parameters)]; for (idxSample, (sample, stateGenerator)) in Enumerated(miniBatch) { mutable err = IntAsDouble(sample::Label); @@ -179,7 +179,7 @@ namespace Microsoft.Quantum.MachineLearning { /// - The smallest number of misclassifications observed through to this /// epoch. /// - The new best sequential model found. - internal operation _RunSingleTrainingEpoch( + internal operation RunSingleTrainingEpoch( encodedSamples : (LabeledSample, StateGenerator)[], schedule : SamplingSchedule, periodScore: Int, options : TrainingOptions, @@ -212,7 +212,7 @@ namespace Microsoft.Quantum.MachineLearning { ); for (idxMinibatch, minibatch) in Enumerated(minibatches) { options::VerboseMessage($" Beginning minibatch {idxMinibatch} of {Length(minibatches)}."); - let (utility, updatedModel) = _RunSingleTrainingStep( + let (utility, updatedModel) = RunSingleTrainingStep( minibatch, options, bestSoFar ); if utility > 1e-7 { @@ -224,7 +224,7 @@ namespace Microsoft.Quantum.MachineLearning { options::Tolerance, updatedModel, features, options::NMeasurements ); - let updatedBias = _UpdatedBias( + let updatedBias = UpdatedBias( Zipped(probabilities, actualLabels), model::Bias, options::Tolerance ); let updatedLabels = InferredLabels( @@ -246,13 +246,13 @@ namespace Microsoft.Quantum.MachineLearning { /// # Summary /// Randomly rescales an input to either grow or shrink by a given factor. - internal operation _RandomlyRescale(scale : Double, value : Double) : Double { + internal operation RandomlyRescale(scale : Double, value : Double) : Double { return value * ( 1.0 + scale * (DrawRandomBool(0.5) ? 1.0 | -1.0) ); } - internal function _EncodeSample(effectiveTolerance : Double, nQubits : Int, sample : LabeledSample) + internal function EncodeSample(effectiveTolerance : Double, nQubits : Int, sample : LabeledSample) : (LabeledSample, StateGenerator) { return ( sample, @@ -313,7 +313,7 @@ namespace Microsoft.Quantum.MachineLearning { options::NMeasurements ); // Find the best bias for the new classification parameters. - let localBias = _UpdatedBias( + let localBias = UpdatedBias( Zipped(probabilities, Sampled(validationSchedule, labels)), 0.0, options::Tolerance @@ -342,7 +342,7 @@ namespace Microsoft.Quantum.MachineLearning { features, options::NMeasurements ); mutable bestSoFar = model - w/ Bias <- _UpdatedBias( + w/ Bias <- UpdatedBias( Zipped(probabilities, actualLabels), model::Bias, options::Tolerance ); @@ -358,7 +358,7 @@ namespace Microsoft.Quantum.MachineLearning { options::VerboseMessage(" Pre-encoding samples..."); let effectiveTolerance = options::Tolerance / IntAsDouble(Length(model::Structure)); let nQubits = MaxI(FeatureRegisterSize(samples[0]::Features), NQubitsRequired(model)); - let encodedSamples = Mapped(_EncodeSample(effectiveTolerance, nQubits, _), samples); + let encodedSamples = Mapped(EncodeSample(effectiveTolerance, nQubits, _), samples); //reintroducing learning rate heuristics mutable lrate = options::LearningRate; @@ -369,7 +369,7 @@ namespace Microsoft.Quantum.MachineLearning { for ep in 1..options::MaxEpochs { options::VerboseMessage($" Beginning epoch {ep}."); - let (nMisses, proposedUpdate) = _RunSingleTrainingEpoch( + let (nMisses, proposedUpdate) = RunSingleTrainingEpoch( encodedSamples, schedule, options::ScoringPeriod, options w/ LearningRate <- lrate w/ MinibatchSize <- batchSize, @@ -389,7 +389,7 @@ namespace Microsoft.Quantum.MachineLearning { if NearlyEqualD(current::Bias, proposedUpdate::Bias) and - _AllNearlyEqualD(current::Parameters, proposedUpdate::Parameters) + AllNearlyEqualD(current::Parameters, proposedUpdate::Parameters) { set nStalls += 1; // If we're more than halfway through our maximum allowed number of stalls, @@ -408,8 +408,8 @@ namespace Microsoft.Quantum.MachineLearning { if nStalls > options::MaxStalls / 2 { set current = SequentialModel( model::Structure, - ForEach(_RandomlyRescale(options::StochasticRescaleFactor, _), proposedUpdate::Parameters), - _RandomlyRescale(options::StochasticRescaleFactor, proposedUpdate::Bias) + ForEach(RandomlyRescale(options::StochasticRescaleFactor, _), proposedUpdate::Parameters), + RandomlyRescale(options::StochasticRescaleFactor, proposedUpdate::Bias) ); } } else { diff --git a/MachineLearning/tests/StructureTests.qs b/MachineLearning/tests/StructureTests.qs index 7187e8da9db..58db9cec8d7 100644 --- a/MachineLearning/tests/StructureTests.qs +++ b/MachineLearning/tests/StructureTests.qs @@ -13,7 +13,7 @@ namespace Microsoft.Quantum.MachineLearning.Tests { let model = Default() w/ Structure <- [ ML.ControlledRotation((3, [7, 9]), PauliX, 0), - ML.ControlledRotation((8, new Int[0]), PauliY, 1) + ML.ControlledRotation((8, [0]), PauliY, 1) ]; let actual = ML.NQubitsRequired(model); EqualityFactI(actual, 10, "Wrong output from NQubitsRequired."); From 99700f20f88a1fe1e0dacc55ab87829c5b864034 Mon Sep 17 00:00:00 2001 From: Mathias Soeken Date: Fri, 11 Mar 2022 00:23:31 -0800 Subject: [PATCH 2/2] Update MachineLearning/tests/StructureTests.qs Co-authored-by: Cassandra Granade --- MachineLearning/tests/StructureTests.qs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MachineLearning/tests/StructureTests.qs b/MachineLearning/tests/StructureTests.qs index 58db9cec8d7..f62c2c2726f 100644 --- a/MachineLearning/tests/StructureTests.qs +++ b/MachineLearning/tests/StructureTests.qs @@ -13,7 +13,7 @@ namespace Microsoft.Quantum.MachineLearning.Tests { let model = Default() w/ Structure <- [ ML.ControlledRotation((3, [7, 9]), PauliX, 0), - ML.ControlledRotation((8, [0]), PauliY, 1) + ML.ControlledRotation((8, []), PauliY, 1) ]; let actual = ML.NQubitsRequired(model); EqualityFactI(actual, 10, "Wrong output from NQubitsRequired.");