XACC
mmd.hpp
1 /*******************************************************************************
2  * Copyright (c) 2019 UT-Battelle, LLC.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and Eclipse Distribution License v1.0 which accompanies this
6  * distribution. The Eclipse Public License is available at
7  * http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution
8  *License is available at https://eclipse.org/org/documents/edl-v10.php
9  *
10  * Contributors:
11  * Alexander J. McCaskey - initial API and implementation
12  *******************************************************************************/
13 #ifndef XACC_ALGORITHM_DDCL_STRATEGIES_MMD_LOSS_HPP_
14 #define XACC_ALGORITHM_DDCL_STRATEGIES_MMD_LOSS_HPP_
15 
16 #include "ddcl.hpp"
17 #include "xacc.hpp"
18 #include <cassert>
19 #include <set>
20 #include <Eigen/Dense>
21 #include "InstructionIterator.hpp"
22 
23 namespace xacc {
24 namespace algorithm {
25 
26 class MMDLossStrategy : public LossStrategy {
27 protected:
28  // Helper functions
29  Eigen::MatrixXd mix_rbf_kernel(std::vector<int> x,std::vector<int> y,
30  std::vector<double> sigma_list, int num_bit){
31  //it's always the case that x = y = [0, 1, ..., pow(2, num_bit)-1];
32  std::vector<int> temp_x(x.size());
33  std::vector<int> temp_y(y.size());
34  Eigen::MatrixXd dx2 = Eigen::MatrixXd::Zero(x.size(), x.size());
35  for(int i = 0; i < num_bit; i++){
36  for(int j = 0; j < x.size(); j++){
37  temp_x[j] = (x[j]>>i)&1;
38  temp_y[j] = (y[j] >>i)&1;
39  }
40  for(int k = 0; k < x.size(); k++){
41  for(int l = 0; l < x.size(); l++){
42  dx2(k,l) += (temp_x[k] != temp_y[l]);
43  }
44  }
45 
46  }
47  return _mix_rbf_kernel_d(dx2, sigma_list);
48 
49  }
50 
51 
52  Eigen::MatrixXd _mix_rbf_kernel_d(Eigen::MatrixXd dx2, std::vector<double> sigma_list){
53  Eigen::MatrixXd K = Eigen::MatrixXd::Zero(dx2.rows(), dx2.cols());
54  Eigen::MatrixXd _dx2 = dx2;
55  for(auto &sigma : sigma_list){
56  double gamma = 1.0/(2*sigma);
57  for(int i = 0; i < dx2.rows(); i++){
58  for(int j = 0; j < dx2.cols(); j++){
59  _dx2(i,j) = std::exp(-gamma*dx2(i,j));
60  }
61  }
62  K += _dx2;
63  }
64  return K;
65  }
66 
67  double kernel_expect(Eigen::MatrixXd K,
68  std::vector<double> px,
69  std::vector<double> py){
70  int len = px.size();
71  //This takes px and py and turns them into eigen::Vector objects
72  Eigen::VectorXd P = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(px.data(), px.size());
73  Eigen::VectorXd Q = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(py.data(), py.size());
74 
75  auto temp = K*Q;
76  double expectation = P.dot(temp);
77 
78 
79  return expectation;
80  }
81 
82 public:
83  std::pair<double, std::vector<double>>
84  compute(Counts &counts, const std::vector<double> &target, const HeterogeneousMap& = {}) override {
85  int shots = 0;
86  for (auto &x : counts) {
87  shots += x.second;
88  }
89 
90 
91  // Compute the probability distribution
92  std::vector<double> q(target.size()); // all zeros
93  for (auto &x : counts) {
94  int idx = std::stoi(x.first, nullptr, 2);
95  q[idx] = (double)x.second / shots;
96  }
97 
98 
99  std::vector<double> pxy(q.size());
100  for(int i = 0; i < q.size(); i++)
101  {
102  pxy[i] = std::abs(target[i] - q[i]);
103  }
104 
105 
106  //worried about edge cases, anywhere in here where I can access this information
107  //without type instability
109  //loss?
110  int num_bit = (int)log2(target.size());
111 
112  std::vector<int> basis(pow(2,num_bit));
113  for(int i = 0; i < pow(2,num_bit); i++){
114  basis[i] = i;
115  }
116 
117  std::vector<double> sigma_list = {0.25};
118  Eigen::MatrixXd K = mix_rbf_kernel(basis, basis, sigma_list, num_bit);
119  auto mmd = kernel_expect(K, pxy, pxy);
120 
121  return std::make_pair(mmd, q);
122  }
123 
124 
125  bool isValidGradientStrategy(const std::string &gradientStrategy) override {
126  // FIXME define what grad strategies this guy works with
127 
128  return true;
129  }
130 
131  const std::string name() const override { return "mmd"; }
132  const std::string description() const override { return ""; }
133 };
134 
135 
136  //Loss function implemetation complete
137 
138 
139 
141  protected:
142  std::vector<double> currentParameterSet;
143 
144  double kernel_expect(Eigen::MatrixXd K,
145  std::vector<double> px,
146  std::vector<double> py){
147  int len = px.size();
148  //This takes px and py and turns them into eigen::Vector objects
149  //YOU_CALLED_A_FIXED_SIZE_METHOD_ON_A_DYNAMIC_SIZE_MATRIX
150  Eigen::VectorXd P = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(px.data(), px.size());
151  Eigen::VectorXd Q = Eigen::Map<Eigen::VectorXd, Eigen::Unaligned>(py.data(), py.size());
152 
153  double expectation = P.dot(K*Q);
154 
155 
156  return expectation;
157  }
158 
159  Eigen::MatrixXd mix_rbf_kernel(std::vector<int> x,std::vector<int> y,
160  std::vector<double> sigma_list, int num_bit){
161  //it's always the case that x = y = [0, 1, ..., pow(2, num_bit)-1];
162  std::vector<int> temp_x(x.size());
163  std::vector<int> temp_y(y.size());
164  Eigen::MatrixXd dx2 = Eigen::MatrixXd::Zero(x.size(), x.size());
165  for(int i = 0; i < num_bit; i++){
166  for(int j = 0; j < x.size(); j++){
167  temp_x[j] = (x[j]>>i)&1;
168  temp_y[j] = (y[j] >>i)&1;
169  }
170  for(int k = 0; k < x.size(); k++){
171  for(int l = 0; l < x.size(); l++){
172  dx2(k,l) += (temp_x[k] != temp_y[l]);
173  }
174  }
175  }
176  return _mix_rbf_kernel_d(dx2, sigma_list);
177  }
178 
179  Eigen::MatrixXd _mix_rbf_kernel_d(Eigen::MatrixXd dx2, std::vector<double> sigma_list){
180  Eigen::MatrixXd K = Eigen::MatrixXd::Zero(dx2.rows(), dx2.cols());
181  Eigen::MatrixXd _dx2 = dx2;
182  for(auto &sigma : sigma_list){
183  double gamma = 1.0/(2*sigma);
184  for(int i = 0; i < dx2.rows(); i++){
185  for(int j = 0; j < dx2.cols(); j++){
186  _dx2(i,j) = std::exp(-gamma*dx2(i,j));
187  }
188  }
189  K += _dx2;
190  }
191  return K;
192  }
193 
194  public:
195  std::vector<Circuit>
196  getCircuitExecutions(Circuit circuit, const std::vector<double> &x) override {
197  currentParameterSet = x;
198  std::set<std::size_t> uniqueBits = circuit->uniqueBits();
199  std::vector<Circuit> grad_circuits;
200  auto provider = xacc::getIRProvider("quantum");
201  for (int i = 0; (i < x.size()); i++) {
202  //shifts params +/- pi/2
203  auto xplus = x[i] + xacc::constants::pi / 2.;
204  auto xminus = x[i] - xacc::constants::pi / 2.;
205  std::vector<double> tmpx_plus = x, tmpx_minus = x;
206  tmpx_plus[i] = xplus;
207  tmpx_minus[i] = xminus;
208  //builds circuits with those params
209  auto xplus_circuit = circuit->operator()(tmpx_plus);
210  auto xminus_circuit = circuit->operator()(tmpx_minus);
211  //run circuit for pos vals
212  for (std::size_t i = 0; i < xplus_circuit->nLogicalBits(); i++) {
213  auto m =
214  provider->createInstruction("Measure", std::vector<std::size_t>{i});
215  xplus_circuit->addInstruction(m);
216  }
217  //run circuit for minus vals
218  for (std::size_t i = 0; i < xminus_circuit->nLogicalBits(); i++) {
219  auto m =
220  provider->createInstruction("Measure", std::vector<std::size_t>{i});
221  xminus_circuit->addInstruction(m);
222  }
223  //save result
224 
225  grad_circuits.push_back(xplus_circuit);
226  grad_circuits.push_back(xminus_circuit);
227  }
228  return grad_circuits;
229  }
230  void compute(std::vector<double> &grad, std::vector<std::shared_ptr<AcceleratorBuffer>> results,
231  const std::vector<double> &q_dist,
232  const std::vector<double> &target_dist) override {
233  assert(2*grad.size() == results.size());
234 
235  //Get the number of shots
236  int shots = 0;
237  for (auto &x : results[0]->getMeasurementCounts()) {
238  shots += x.second;
239  }
240 
241  //q+ and q- vectors
242  int counter = 0;
243  std::vector<std::vector<double>> qplus_theta, qminus_theta;
244  for (int i = 0; i < results.size(); i += 2) {
245 
246  std::vector<double> qp(q_dist.size()), qm(q_dist.size());
247  for (auto &x : results[i]->getMeasurementCounts()) {
248  int idx = std::stoi(x.first, nullptr, 2);
249  qp[idx] = (double)x.second / shots;
250  }
251  for (auto &x : results[i + 1]->getMeasurementCounts()) {
252  int idx = std::stoi(x.first, nullptr, 2);
253  qm[idx] = (double)x.second / shots;
254  }
255  std::vector<double> shiftedp = currentParameterSet;
256  std::vector<double> shiftedm = currentParameterSet;
257  auto xplus = currentParameterSet[counter] + xacc::constants::pi / 2;
258  auto xminus = currentParameterSet[counter] - xacc::constants::pi / 2;
259 
260  shiftedp[counter] = xplus;
261  shiftedm[counter] = xminus;
262 
263 
264  results[i]->addExtraInfo("gradient-index", counter);
265  results[i+1]->addExtraInfo("gradient-index", counter);
266 
267  results[i]->addExtraInfo("shift-direction", "plus");
268  results[i+1]->addExtraInfo("shift-direction", "minus");
269 
270  results[i]->addExtraInfo("qdist", qp);
271  results[i+1]->addExtraInfo("qdist", qm);
272 
273  results[i]->addExtraInfo("gradient-parameters", shiftedp);
274  results[i+1]->addExtraInfo("gradient-parameters", shiftedm);
275 
276  qplus_theta.push_back(qp);
277  qminus_theta.push_back(qm);
278 
279  counter++;
280 
281  }
282 
283  std::vector<double> sigma_list = {0.1};
284  int num_bit = (int) log2(q_dist.size());
285  std::vector<int> basis(pow(2,num_bit));
286  for(int i = 0; i < pow(2,num_bit); i++){
287  basis[i] = i;
288  }
289  Eigen::MatrixXd K = mix_rbf_kernel(basis, basis, sigma_list, num_bit);
290  //compute gradient vector
291  std::vector<double> grad_pos(counter);
292  std::vector<double> grad_neg(counter);
293  std::vector<double> grad_pos_targ(counter);
294  std::vector<double> grad_neg_targ(counter);
295 
296  for(int i = 0; i < counter; i++){
297  grad_pos[i] = kernel_expect(K, qplus_theta[i], q_dist);
298  grad_neg[i] = kernel_expect(K, qminus_theta[i], q_dist);
299  grad_pos_targ[i] = kernel_expect(K, qplus_theta[i], target_dist);
300  grad_neg_targ[i] = kernel_expect(K, qminus_theta[i], target_dist);
301  grad[i] = grad_pos[i]-grad_pos_targ[i]-grad_neg[i]+grad_neg_targ[i];
302  }
303  return;
304  }
305  const std::string name() const override { return "mmd-parameter-shift"; }
306  const std::string description() const override { return ""; }
307  };
308 
309 } // namespace algorithm
310 } // namespace xacc
311 #endif
Definition: ddcl.hpp:24
const std::string description() const override
Definition: mmd.hpp:306
Definition: mmd.hpp:26
Definition: Accelerator.hpp:25
const std::string name() const override
Definition: mmd.hpp:305
const std::string name() const override
Definition: mmd.hpp:131
Definition: heterogeneous.hpp:45
std::pair< double, std::vector< double > > compute(Counts &counts, const std::vector< double > &target, const HeterogeneousMap &={}) override
Definition: mmd.hpp:84
Definition: ddcl.hpp:32
const std::string description() const override
Definition: mmd.hpp:132