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
+ ]
+ }
+}