diff --git a/samples/machine-learning/half-moons/HalfMoons.csproj b/samples/machine-learning/half-moons/HalfMoons.csproj new file mode 100644 index 000000000000..1337adc30d47 --- /dev/null +++ b/samples/machine-learning/half-moons/HalfMoons.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.0 + x64 + + + + + + + + + diff --git a/samples/machine-learning/half-moons/Program.cs b/samples/machine-learning/half-moons/Program.cs new file mode 100644 index 000000000000..ffc3b254597c --- /dev/null +++ b/samples/machine-learning/half-moons/Program.cs @@ -0,0 +1,90 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; +using System; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Threading.Tasks; +using static System.Math; + +namespace Microsoft.Quantum.Samples +{ + using Microsoft.Quantum.MachineLearning; + using Microsoft.Quantum.MachineLearning.Interop; + + class Program + { + static async Task Main() + { + // We start by loading the training and validation data from our JSON + // data file. + var data = await LoadData("data.json"); + + // We then define the classifier parameters where we want to start our + // training iterations from. Since gradient descent is good at finding + // local optima, it's helpful to have a variety of different starting + // points. + var parameterStartingPoints = new [] + { + new [] {0.060057, 3.00522, 2.03083, 0.63527, 1.03771, 1.27881, 4.10186, 5.34396}, + new [] {0.586514, 3.371623, 0.860791, 2.92517, 1.14616, 2.99776, 2.26505, 5.62137}, + new [] {1.69704, 1.13912, 2.3595, 4.037552, 1.63698, 1.27549, 0.328671, 0.302282}, + new [] {5.21662, 6.04363, 0.224184, 1.53913, 1.64524, 4.79508, 1.49742, 1.5455} + }; + + // Next, we initialize a full state-vector simulator as our target machine. + using var targetMachine = new QuantumSimulator(); + + // Once we have the data loaded and have initialized our target machine, + // we can then use that target machine to train a QCC classifier. + var (optimizedParameters, optimizedBias) = await TrainHalfMoonModel.Run( + targetMachine, + new QArray>(data.TrainingData.Features.Select(vector => new QArray(vector))), + new QArray(data.TrainingData.Labels), + new QArray>(parameterStartingPoints.Select(parameterSet => new QArray(parameterSet))) + ); + + // After training, we can use the validation data to test the accuracy + // of our new classifier. + var testMisses = await ValidateHalfMoonModel.Run( + targetMachine, + new QArray>(data.ValidationData.Features.Select(vector => new QArray(vector))), + new QArray(data.ValidationData.Labels), + optimizedParameters, + optimizedBias + ); + System.Console.WriteLine($"Observed {testMisses} misclassifications out of {data.ValidationData.Labels.Count} validation samples."); + } + + class LabeledData + { + public List Features { get; set; } + public List Labels { get; set; } + } + + class DataSet + { + public LabeledData TrainingData { get; set; } + public LabeledData ValidationData { get; set; } + } + + static async Task LoadData(string dataPath, double offset = 0.0, double filler = 1.0) + { + using var dataReader = File.OpenRead(dataPath); + return await JsonSerializer.DeserializeAsync( + dataReader, + new JsonSerializerOptions + { + ReadCommentHandling = JsonCommentHandling.Skip + } + ); + } + + } +} diff --git a/samples/machine-learning/half-moons/Training.qs b/samples/machine-learning/half-moons/Training.qs new file mode 100644 index 000000000000..5c7839a95038 --- /dev/null +++ b/samples/machine-learning/half-moons/Training.qs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Samples { + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Arrays; + open Microsoft.Quantum.MachineLearning; + open Microsoft.Quantum.Math; + + function WithOffset(offset : Double, sample : Double[]) : Double[] { + return Mapped(TimesD(offset, _), sample); + } + + function WithProductKernel(scale : Double, sample : Double[]) : Double[] { + return sample + [scale * Fold(TimesD, 1.0, sample)]; + } + + function Preprocessed(samples : Double[][]) : Double[][] { + let offset = 0.75; + let scale = 1.0; + + return Mapped( + Compose( + WithOffset(offset, _), + WithProductKernel(scale, _) + ), + samples + ); + } + + function DefaultSchedule(samples : Double[][]) : SamplingSchedule { + return SamplingSchedule([ + 0..Length(samples) - 1 + ]); + } + + // FIXME: This needs to return a GateSequence value, but that requires adapting + // TrainQcccSequential. + function ClassifierStructure() : GateSequence { + let (x, y, z) = (1, 2, 3); + return GateSequence([ + ControlledRotation(GateSpan(0, new Int[0]), PauliX, 4), + ControlledRotation(GateSpan(0, new Int[0]), PauliZ, 5), + ControlledRotation(GateSpan(1, new Int[0]), PauliX, 6), + ControlledRotation(GateSpan(1, new Int[0]), PauliZ, 7), + ControlledRotation(GateSpan(0, [1]), PauliX, 0), + ControlledRotation(GateSpan(1, [0]), PauliX, 1), + ControlledRotation(GateSpan(1, new Int[0]), PauliZ, 2), + ControlledRotation(GateSpan(1, new Int[0]), PauliX, 3) + ]); + } + + operation TrainHalfMoonModel( + trainingVectors : Double[][], + trainingLabels : Int[], + initialParameters : Double[][] + ) : (Double[], Double) { + let samples = Mapped( + LabeledSample, + Zip(Preprocessed(trainingVectors), trainingLabels) + ); + let nQubits = 2; + let learningRate = 0.1; + let minibatchSize = 15; + let tolerance = 0.005; + let nMeasurements = 10000; + let maxEpochs = 16; + Message("Ready to train."); + let (optimizedParameters, optimialBias) = TrainSequentialClassifier( + nQubits, + ClassifierStructure(), + initialParameters, + samples, + DefaultSchedule(trainingVectors), + DefaultSchedule(trainingVectors), + learningRate, tolerance, minibatchSize, + maxEpochs, + nMeasurements + ); + Message($"Training complete, found optimal parameters: {optimizedParameters}"); + return (optimizedParameters, optimialBias); + } + + operation ValidateHalfMoonModel( + validationVectors : Double[][], + validationLabels : Int[], + parameters : Double[], + bias : Double + ) : Int { + let samples = Mapped( + LabeledSample, + Zip(Preprocessed(validationVectors), validationLabels) + ); + let nQubits = 2; + let tolerance = 0.005; + let nMeasurements = 10000; + let results = ValidateModel( + tolerance, + nQubits, + samples, + DefaultSchedule(validationVectors), + ClassifierStructure(), + parameters, + bias, + nMeasurements + ); + return results::NMisclassifications; + } + +} diff --git a/samples/machine-learning/half-moons/data.json b/samples/machine-learning/half-moons/data.json new file mode 100644 index 000000000000..8ab42ad57891 --- /dev/null +++ b/samples/machine-learning/half-moons/data.json @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +{ + "TrainingData": { + "Features": [ + [ + 0.883898709935946, + 0.0 + ], + [ + 1.1793893456320759, + 0.3748347849076317 + ], + [ + 0.5231831310352743, + 0.900818803609586 + ], + [ + 0.30081607582696224, + 0.9615554053041235 + ], + [ + -0.06340157706002579, + 1.224735863277014 + ], + [ + -0.5382643095868677, + 1.0133143141607128 + ], + [ + -0.5975208094029268, + 0.561438878658366 + ], + [ + -0.766231190604878, + 0.19526118857958627 + ], + [ + 0.14060021668138722, + 0.4 + ], + [ + 0.05239183271831849, + -0.008592101868396151 + ], + [ + 0.36087228857768316, + -0.3470962748964499 + ], + [ + 0.7371834528946137, + -0.3046817236779634 + ], + [ + 1.0706889634601606, + -0.7207038198913537 + ], + [ + 1.487876633223481, + -0.37571084836184543 + ], + [ + 1.8726657414770358, + -0.16558447370672003 + ] + ], + "Labels": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + }, + "ValidationData": { + "Features": [ + [ + 0.883898709935946, + 0.0 + ], + [ + 1.1793893456320759, + 0.3748347849076317 + ], + [ + 0.5231831310352743, + 0.900818803609586 + ], + [ + 0.30081607582696224, + 0.9615554053041235 + ], + [ + -0.06340157706002579, + 1.224735863277014 + ], + [ + -0.5382643095868677, + 1.0133143141607128 + ], + [ + -0.5975208094029268, + 0.561438878658366 + ], + [ + -0.766231190604878, + 0.19526118857958627 + ], + [ + 0.14060021668138722, + 0.4 + ], + [ + 0.05239183271831849, + -0.008592101868396151 + ], + [ + 0.36087228857768316, + -0.3470962748964499 + ], + [ + 0.7371834528946137, + -0.3046817236779634 + ], + [ + 1.0706889634601606, + -0.7207038198913537 + ], + [ + 1.487876633223481, + -0.37571084836184543 + ], + [ + 1.8726657414770358, + -0.16558447370672003 + ] + ], + "Labels": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + } +}