forked from microsoft/Quantum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHost.cs
158 lines (143 loc) · 7.06 KB
/
Host.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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 System.Reflection;
using static System.Math;
namespace Microsoft.Quantum.Samples
{
using Microsoft.Quantum.MachineLearning;
class Program
{
static async Task Main()
{
// We start by loading the training and validation data from our JSON
// data file.
var data = await LoadData(Path.Join(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "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}
};
// Convert samples to Q# form.
var samples = new QArray<QArray<double>>(data.TrainingData.Features.Select(vector => new QArray<double>(vector)));
// 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, nMisses) = parameterStartingPoints
// We can use parallel LINQ (PLINQ) to convert the IEnumerable
// over starting points into a parallelized query.
.AsParallel()
// By default, PLINQ may or may not actually run our query in
// parallel, depending on the capabilities of your machine.
// We can force PLINQ to actually parallelize, however, by using
// the WithExecutionMode method.
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
// Many of the same LINQ methods are defined for PLINQ queries
// as well as IEnumerable objects, so we can go on and run
// the training loop in parallel by selecting on the start point.
.Select(
(startPoint, idxStartPoint) =>
{
// Since we want each start point to run on its own
// instance of the full-state simulator, we create a new
// instance here, using C# 8's "using var" syntax to
// ensure that the simulator is deallocated once
// training is complete for this start point.
using var targetMachine = new QuantumSimulator();
// We attach a tag to log output so that we can tell
// each training job's messages apart.
// To do so, we disable the default output to the console
// and attach our own event with the index of the
// starting point that generated each message.
targetMachine.DisableLogToConsole();
targetMachine.OnLog += message =>
Console.WriteLine($"[{idxStartPoint}] {message}");
// Finally, we can call the Q# entry point with the
// samples, their labels, and our given start point.
return TrainHalfMoonModelAtStartPoint.Run(
targetMachine,
samples,
new QArray<long>(data.TrainingData.Labels),
new QArray<double>(startPoint)
).Result;
}
)
// We can then gather the results back into a sequential
// (IEnumerable) collection.
.AsSequential()
// Finally, we want to minimize over the number of misses,
// returning the corresponding sequential classifier model.
// In this case, we use a handy extension method defined below
// to perform the minimization.
.MinBy(result => result.Item3);
// After training, we can use the validation data to test the accuracy
// of our new classifier.
using var targetMachine = new QuantumSimulator();
var missRate = await ValidateHalfMoonModel.Run(
targetMachine,
new QArray<QArray<double>>(data.ValidationData.Features.Select(vector => new QArray<double>(vector))),
new QArray<long>(data.ValidationData.Labels),
optimizedParameters,
optimizedBias
);
System.Console.WriteLine($"Observed {100 * missRate:F2}% misclassifications.");
}
class LabeledData
{
public List<double[]> Features { get; set; }
public List<long> Labels { get; set; }
}
class DataSet
{
public LabeledData TrainingData { get; set; }
public LabeledData ValidationData { get; set; }
}
static async Task<DataSet> LoadData(string dataPath)
{
using var dataReader = File.OpenRead(dataPath);
return await JsonSerializer.DeserializeAsync<DataSet>(
dataReader
);
}
}
public static class LinqExtensions
{
/// <summary>
/// Minimizes over the elements of an enumerable, using a given
/// projection function to define the relative ordering between
/// elements.
/// </summary>
/// <param name="source">A source of elements to be minimized over.</param>
/// <param name="by">
/// A projection function used to define comparisons between
/// elements
/// </param>
/// <returns>
/// The element <c>min</c> of <c>source</c> such that <c>by(min)</c>
/// is minimized. In the case that two or more elements share the
/// same value of <c>by</c>, the first element will be returned.
/// </returns>
public static TSource MinBy<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> by)
where TResult : IComparable<TResult> =>
source
.Aggregate(
(minimum, next) =>
by(minimum).CompareTo(by(next)) < 0
? minimum
: next
);
}
}