Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions MachineLearning/src/Classification.qs
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,79 @@ namespace Microsoft.Quantum.MachineLearning {

operation _PrepareClassification(
encoder : (LittleEndian => Unit is Adj + Ctl),
structure : SequentialClassifierStructure,
parameters : Double[],
gates : GateSequence,
target : Qubit[]
)
: Unit is Adj {
encoder(LittleEndian(target));
_ApplyGates(parameters, gates, target);
ApplySequentialClassifier(structure, parameters, target);
}

/// # Summary
/// Given a sample and a sequential classifier, estimates the
/// classification probability for that sample by repeatedly measuring
/// the output of the classifier on the given sample.
///
/// # Input
/// ## tolerance
/// The tolerance to allow in encoding the sample into a state preparation
/// operation.
/// ## parameters
/// A parameterization of the given sequential classifier.
/// ## structure
/// The structure of the given sequential classifier.
/// ## sample
/// The feature vector for the sample to be classified.
/// ## nMeasurements
/// The number of measusrements to use in estimating the classification
/// probability.
/// # Output
/// An estimate of the classification probability for the given sample.
operation EstimateClassificationProbability(
tolerance: Double,
tolerance : Double,
parameters : Double[],
gates: GateSequence,
sample: Double[],
structure : SequentialClassifierStructure,
sample : Double[],
nMeasurements: Int
)
: Double {
let nQubits = FeatureRegisterSize(sample);
let circEnc = ApproximateInputEncoder(tolerance / IntAsDouble(Length(gates!)), sample);
let circEnc = ApproximateInputEncoder(tolerance / IntAsDouble(Length(structure!)), sample);
let encodedSample = StateGenerator(nQubits, circEnc);
return 1.0 - EstimateFrequencyA(
_PrepareClassification(encodedSample::Apply, parameters, gates, _),
_PrepareClassification(encodedSample::Apply, structure, parameters, _),
_TailMeasurement(encodedSample::NQubits),
encodedSample::NQubits,
nMeasurements
);
}

/// # Summary
/// Given a set of samples and a sequential classifier, estimates the
/// classification probability for those samples by repeatedly measuring
/// the output of the classifier on each sample.
///
/// # Input
/// ## tolerance
/// The tolerance to allow in encoding the sample into a state preparation
/// operation.
/// ## parameters
/// A parameterization of the given sequential classifier.
/// ## structure
/// The structure of the given sequential classifier.
/// ## samples
/// An array of feature vectors for each sample to be classified.
/// ## nMeasurements
/// The number of measusrements to use in estimating the classification
/// probability.
/// # Output
/// An array of estimates of the classification probability for each given
/// sample.
operation EstimateClassificationProbabilities(
tolerance : Double,
parameters : Double[],
structure : GateSequence,
structure : SequentialClassifierStructure,
samples : Double[][],
nMeasurements : Int
)
Expand Down
66 changes: 0 additions & 66 deletions MachineLearning/src/Convert.qs

This file was deleted.

9 changes: 0 additions & 9 deletions MachineLearning/src/Features.qs

This file was deleted.

65 changes: 34 additions & 31 deletions MachineLearning/src/GradientEstimation.qs
Original file line number Diff line number Diff line change
Expand Up @@ -19,70 +19,73 @@ namespace Microsoft.Quantum.MachineLearning {

operation _EstimateDerivativeWithParameterShift(
inputEncoder : StateGenerator,
gates : GateSequence,
structure : SequentialClassifierStructure,
parameters : (Double[], Double[]),
nQubits : Int,
nMeasurements : Int
) : Double {
return EstimateRealOverlapBetweenStates(
_ApplyLEOperationToRawRegister(inputEncoder::Apply, _),
_ApplyGates(Fst(parameters), gates, _),
_ApplyGates(Snd(parameters), gates, _),
ApplySequentialClassifier(structure, Fst(parameters), _),
ApplySequentialClassifier(structure, Snd(parameters), _),
nQubits, nMeasurements
);
}

/// # Summary
/// polymorphic classical/quantum gradient estimator
/// Estimates the training gradient for a sequential classifier at a
/// particular set of parameters and for a given encoded input.
///
/// # Input
/// ## structure
/// The structure of the sequential classifier as a sequence of quantum
/// operations.
/// ## param
/// circuit parameters
///
/// ## gates
/// sequence of gates in the circuits
///
/// A set of parameters for the given classifier structure.
/// ## sg
/// generates quantum encoding of a subject sample (either simulated or true)
///
/// ## measCount
/// number of true quantum measurements to estimate probabilities.
/// IMPORTANT: measCount==0 implies simulator deployment
/// An input to the sequential classifier, encoded into a state preparation
/// operation.
/// ## nMeasurements
/// The number of measurements to use in estimating the gradient.
///
/// # Output
/// the gradient
/// An estimate of the training gradient at the given input and model
/// parameters.
///
/// # Remarks
/// This operation uses a Hadamard test and the parameter shift technique
/// together to estimate the gradient.
operation EstimateGradient(
gates : GateSequence,
structure : SequentialClassifierStructure,
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[<Circ1 psi|\Pi_1|Circ0 psi>] =
// Re[<Circ1 psi|Id|Circ0 psi>] - Re[<Circ1 psi|Z \otimes Id|Circ0 psi>]
//We observe SEE THEORY that for (Circ1)=(Circ0)' , Re[<Circ1 psi|Circ0 psi>]==0
//Thus we are left to compute Re[<Circ1 psi|Z \otimes Id|Circ0 psi>] =
// 1 - 1/2 < (Z \otimes Id) Circ0 psi - Circ1 psi | (Z \otimes Id) Circ0 psi - Circ1 psi>
//i.e., 1 - HadamardTestResultHack(Circ1,[Z],Circ0)
// 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[<Circ1 psi|\Pi_1|Circ0 psi>] =
// Re[<Circ1 psi|Id|Circ0 psi>] - Re[<Circ1 psi|Z \otimes Id|Circ0 psi>]
// We observe SEE THEORY that for (Circ1)=(Circ0)' , Re[<Circ1 psi|Circ0 psi>]==0
// Thus we are left to compute Re[<Circ1 psi|Z \otimes Id|Circ0 psi>] =
// 1 - 1/2 < (Z \otimes Id) Circ0 psi - Circ1 psi | (Z \otimes Id) Circ0 psi - Circ1 psi>
// i.e., 1 - HadamardTestResultHack(Circ1,[Z],Circ0)


//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
// 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(param), 0.0);
let nQubits = MaxI(NQubitsRequired(gates), sg::NQubits);
let nQubits = MaxI(NQubitsRequired(structure), sg::NQubits);

for (gate in gates!) {
for (gate in structure!) {
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 = _EstimateDerivativeWithParameterShift(
sg, gates, (param, paramShift), nQubits, nMeasurements
sg, structure, (param, paramShift), nQubits, nMeasurements
);
if (IsEmpty(gate::Span::ControlIndices)) {
//uncontrolled gate
Expand All @@ -91,10 +94,10 @@ namespace Microsoft.Quantum.MachineLearning {
//controlled gate
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)
// 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(
sg, gates, (param, controlledShift), nQubits, nMeasurements
sg, structure, (param, controlledShift), nQubits, nMeasurements
);
set grad w/= gate::Index <- (grad[gate::Index] + 0.5 * (newDer - newDer1));
}
Expand Down
38 changes: 32 additions & 6 deletions MachineLearning/src/InputEncoding.qs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,38 @@ namespace Microsoft.Quantum.MachineLearning {
// Reflect about the negative coefficients to apply the negative signs
// at the end.
for (idxNegative in negLocs) {
ReflectAboutInteger(idxNegative, reg); //TODO:REVIEW: this assumes that 2^Length(reg) is the minimal pad to Length(coefficients)
ReflectAboutInteger(idxNegative, reg);
}
}

function ApproximateInputEncoder(tolerance : Double,coefficients : Double[])
/// # Summary
/// Returns the number of qubits required to encode a particular feature
/// vector.
///
/// # Input
/// ## sample
/// A sample feature vector to be encoded into a qubit register.
///
/// # Output
/// The size required to encode `sample` into a qubit register, expressed
/// as a number of qubits.
function FeatureRegisterSize(sample : Double[]) : Int {
return Ceiling(Lg(IntAsDouble(Length(sample))));
}

/// # Summary
/// Given a set of coefficients and a tolerance, returns a state preparation
/// operation that prepares each coefficient as the corresponding amplitude
/// of a computational basis state, up to the given tolerance.
///
/// # Input
/// ## tolerance
/// // TODO
/// ## coefficients
/// // TODO
/// # Output
/// // TODO
function ApproximateInputEncoder(tolerance : Double, coefficients : Double[])
: (LittleEndian => Unit is Adj + Ctl) {
//First quantize the coefficients: for a coef x find such y*tolerance, where y is integer and |x-y*tolerance| \neq tolerance/2
let nCoefficients = Length(coefficients);
Expand Down Expand Up @@ -93,18 +120,17 @@ namespace Microsoft.Quantum.MachineLearning {
// Here, by a "few," we mean fewer than the number of qubits required
// to encode features.
if ((cNegative > 0) and (IntAsDouble(cNegative) < Lg(IntAsDouble(Length(coefficients))) + 1.0)) {
return _EncodeSparseNegativeInput(cNegative, tolerance, complexCoefficients, _); //TODO:MORE:ACCEPTANCE ("Wines" passing soi far)
return _EncodeSparseNegativeInput(cNegative, tolerance, complexCoefficients, _);
}

// Finally, we fall back to arbitrary state preparation.
return ApproximatelyPrepareArbitraryState(tolerance, complexCoefficients, _);
} //EncodeNoisyInput

//TODO:REVIEW: Design consideration! The implicit qubit count must be read off from the state encoder, NOT from the gate sequence!

/// Create amplitude encoding of an array of real-valued coefficients
/// The vector of 'coefficients' does not have to be unitary
function InputEncoder(coefficients : Double[]): (LittleEndian => Unit is Adj + Ctl) {
function InputEncoder(coefficients : Double[])
: (LittleEndian => Unit is Adj + Ctl) {
//default implementation, does not respect sparcity
mutable complexCoefficients = new ComplexPolar[Length(coefficients)];
for ((idx, coefficient) in Enumerated(coefficients)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Microsoft.Quantum.MachineLearning {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Math;

function _AllNearlyEqualD(v1: Double[], v2: Double[]):Bool {
function _AllNearlyEqualD(v1 : Double[], v2 : Double[]) : Bool {
return Length(v1) == Length(v2) and All(NearlyEqualD, Zip(v1, v2));
}

Expand Down
Loading