qrt.cpp 12.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*******************************************************************************
 * Copyright (c) 2018-, UT-Battelle, LLC.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the BSD 3-Clause License 
 * which accompanies this distribution. 
 *
 * Contributors:
 *   Alexander J. McCaskey - initial API and implementation
 *   Thien Nguyen - implementation
 *******************************************************************************/
11
#include "qrt.hpp"
12
13
14
15

#include <Eigen/Dense>
#include <Utils.hpp>

16
17
#include "Instruction.hpp"
#include "PauliOperator.hpp"
18
#include "pass_manager.hpp"
19
#include "qcor_config.hpp"
20
#include "xacc.hpp"
21
#include "xacc_config.hpp"
22
#include "xacc_internal_compiler.hpp"
23
#include "xacc_service.hpp"
24
#include "qcor_observable.hpp"
25

26
27
28
29
30
31
namespace qcor {
namespace __internal__ {
std::string DEFAULT_GRADIENT_METHOD = "central";
}
} // namespace qcor

32
33
namespace xacc {
namespace internal_compiler {
34
35
// Extern vars:
int __opt_level = 0;
36
bool __print_opt_stats = false;
37
std::string __user_opt_passes = "";
38
39
std::string __placement_name = "";
std::vector<int> __qubit_map = {};
40
std::string __qrt_env = "nisq";
41
bool __print_final_submission = false;
42
std::string __print_final_submission_filename = "";
43
bool __validate_nisq_execution = false;
44

45
void execute_pass_manager(
46
    std::shared_ptr<qcor::CompositeInstruction> optional_composite) {
47
48
  qcor::internal::PassManager passManager(__opt_level, __qubit_map,
                                          __placement_name);
49
50
51
  auto kernelToExecute = optional_composite
                             ? optional_composite
                             : ::quantum::qrt_impl->get_current_program();
52
  // auto kernelToExecute = std::dynamic_pointer_cast<xacc::CompositeInstruction>(_kernelToExecute->get_as_opaque());
53
  auto optData = passManager.optimize(kernelToExecute);
54

55
56
57
58
59
60
61
62
63
64
65
66
  std::vector<std::string> user_passes;
  if (!__user_opt_passes.empty()) {
    std::stringstream ss(__user_opt_passes);
    // Parses list of passes
    while (ss.good()) {
      std::string passName;
      std::getline(ss, passName, ',');
      user_passes.emplace_back(passName);
    }
  }

  // Runs user-specified passes
67
68
  for (const auto &user_pass : user_passes) {
    optData.emplace_back(
69
        qcor::internal::PassManager::runPass(user_pass, kernelToExecute));
70
71
  }

72
73
74
75
76
77
  if (__print_opt_stats) {
    // Prints out the Optimizer Stats if requested.
    for (const auto &passData : optData) {
      std::cout << passData.toString(false);
    }
  }
78

79
  passManager.applyPlacement(kernelToExecute);
80
81
}

82
83
84
85
86
87
88
89
90
std::vector<int> parse_qubit_map(const char *qubit_map_str) {
  std::vector<int> qubitMap;
  std::stringstream ss(qubit_map_str);
  while (ss.good()) {
    // Split by ',' delimiter
    try {
      std::string qubitId;
      std::getline(ss, qubitId, ',');
      qubitMap.emplace_back(std::stoi(qubitId));
91
    } catch (...) {
92
93
94
95
96
97
      // Cannot parse the integer.
      return {};
    }
  }
  return qubitMap;
}
98

99
100
101
102
void apply_decorators(const std::string &decorator_cmdline_string) {
  auto decorator =
      xacc::getAcceleratorDecorator(decorator_cmdline_string, get_qpu());
  xacc::internal_compiler::qpu = decorator;
103
}
104
105
106
107
108

std::string get_native_code(std::shared_ptr<qcor::CompositeInstruction> program,
                            xacc::HeterogeneousMap options) {
  return get_qpu()->getNativeCode(program->as_xacc(), options);
}
109

110
111
112
113
void set_autograd_method(const std::string &method_name) {
  qcor::__internal__::DEFAULT_GRADIENT_METHOD = method_name;
}

114
115
116
117
118
119
120
121
122
123
124
std::pair<bool, xacc::HeterogeneousMap>
validate_backend_execution(std::shared_ptr<qcor::CompositeInstruction> program,
                           xacc::HeterogeneousMap options) {
  // FWIW, we only have this method...
  static const std::string DEFAULT_METHOD = "mirror-rb";
  auto validate_program =
      program ? program : ::quantum::qrt_impl->get_current_program();
  auto validator = xacc::getService<qcor::BackendValidator>(DEFAULT_METHOD);
  return validator->validate(get_qpu(), validate_program, options);
}
} // namespace internal_compiler
125
}  // namespace xacc
126
namespace quantum {
127
int current_shots = 0;
128
129
std::shared_ptr<QuantumRuntime> qrt_impl = nullptr;
std::vector<std::string> kernels_in_translation_unit = {};
130
131
132
std::unordered_map<
    std::string, std::pair<std::vector<std::string>, std::vector<std::string>>>
    kernel_signatures_in_translation_unit = {};
133
134

void initialize(const std::string qpu_name, const std::string kernel_name) {
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  // if XACC_INSTALL_DIR != XACC_ROOT
  // then we need to pass --xacc-root-path XACC_ROOT
  //
  // Example - we are on Rigetti QCS and can't install via sudo
  // so we dpkg -x xacc to a user directory, but deb package
  // expects to be extracted to /usr/local/xacc, and xacc_config.hpp
  // points to that /usr/local/xacc. Therefore ServiceRegistry fails
  // to load plugins and libs, unless we change rootPath.
  std::string xacc_config_install_dir = std::string(XACC_INSTALL_DIR);
  std::string qcor_config_xacc_root = std::string(XACC_ROOT);
  if (xacc_config_install_dir != qcor_config_xacc_root) {
    std::vector<std::string> cmd_line{"--xacc-root-path",
                                      qcor_config_xacc_root};
    xacc::internal_compiler::compiler_InitializeXACC(qpu_name.c_str(),
                                                     cmd_line);
  } else {
    xacc::internal_compiler::compiler_InitializeXACC(qpu_name.c_str());
  }
153

154
  qrt_impl = xacc::getService<QuantumRuntime>(__qrt_env);
155
  qrt_impl->initialize(kernel_name);
156
157
}

158
159
160
161
162
163
void finalize() {
  // We should have called initialize
  assert(qrt_impl);
  qrt_impl->finalize();
}

164
165
166
167
168
void set_backend(std::string accelerator_name, const int shots) {
  xacc::internal_compiler::compiler_InitializeXACC(accelerator_name.c_str());
  set_shots(shots);
}

Mccaskey, Alex's avatar
Mccaskey, Alex committed
169
170
171
172
void set_backend(std::string accelerator_name) {
  xacc::internal_compiler::compiler_InitializeXACC(accelerator_name.c_str());
}

173
void set_shots(int shots) {
174
  current_shots = shots;
175
176
177
  xacc::internal_compiler::get_qpu()->updateConfiguration(
      {std::make_pair("shots", shots)});
}
178

179
180
int get_shots() { return current_shots; }

181
182
183
184
void set_qrt(const std::string &qrt_name) {
  xacc::internal_compiler::__qrt_env = qrt_name;
}

185
186
187
188
void h(const qubit &qidx) { qrt_impl->h(qidx); }
void x(const qubit &qidx) { qrt_impl->x(qidx); }
void y(const qubit &qidx) { qrt_impl->y(qidx); }
void z(const qubit &qidx) { qrt_impl->z(qidx); }
189

190
191
void s(const qubit &qidx) { qrt_impl->s(qidx); }
void sdg(const qubit &qidx) { qrt_impl->sdg(qidx); }
192

193
194
void t(const qubit &qidx) { qrt_impl->t(qidx); }
void tdg(const qubit &qidx) { qrt_impl->tdg(qidx); }
195

196
void rx(const qubit &qidx, const double theta) { qrt_impl->rx(qidx, theta); }
197

198
void ry(const qubit &qidx, const double theta) { qrt_impl->ry(qidx, theta); }
199

200
void rz(const qubit &qidx, const double theta) { qrt_impl->rz(qidx, theta); }
201

202
void u1(const qubit &qidx, const double theta) { qrt_impl->u1(qidx, theta); }
Nguyen, Thien Minh's avatar
Nguyen, Thien Minh committed
203

204
205
void u3(const qubit &qidx, const double theta, const double phi,
        const double lambda) {
206
  qrt_impl->u3(qidx, theta, phi, lambda);
207
208
}

209
210
void reset(const qubit &qidx) { qrt_impl->reset(qidx); }

211
212
213
bool mz(const qubit &qidx, std::pair<std::string, size_t> *optional_creg) {
  return qrt_impl->mz(qidx, optional_creg);
}
214

215
void cnot(const qubit &src_idx, const qubit &tgt_idx) {
216
  qrt_impl->cnot(src_idx, tgt_idx);
217
218
219
}

void cy(const qubit &src_idx, const qubit &tgt_idx) {
220
  qrt_impl->cy(src_idx, tgt_idx);
221
222
223
}

void cz(const qubit &src_idx, const qubit &tgt_idx) {
224
  qrt_impl->cz(src_idx, tgt_idx);
225
226
227
}

void ch(const qubit &src_idx, const qubit &tgt_idx) {
228
  qrt_impl->ch(src_idx, tgt_idx);
229
230
231
}

void swap(const qubit &src_idx, const qubit &tgt_idx) {
232
  qrt_impl->swap(src_idx, tgt_idx);
233
234
235
}

void cphase(const qubit &src_idx, const qubit &tgt_idx, const double theta) {
236
  qrt_impl->cphase(src_idx, tgt_idx, theta);
237
238
239
}

void crz(const qubit &src_idx, const qubit &tgt_idx, const double theta) {
240
  qrt_impl->crz(src_idx, tgt_idx, theta);
241
242
}

243
void exp(qreg q, const double theta, qcor::Operator &H) {
244
  qrt_impl->exp(q, theta, H);
245
246
}

247
248
249
// void exp(qreg q, const double theta, xacc::Observable *H) {
//   qrt_impl->exp(q, theta, H);
// }
250

251
252
253
// void exp(qreg q, const double theta, std::shared_ptr<xacc::Observable> H) {
//   qrt_impl->exp(q, theta, H);
// }
254

255
void submit(xacc::AcceleratorBuffer *buffer) { qrt_impl->submit(buffer); }
256

257
void submit(xacc::AcceleratorBuffer **buffers, const int nBuffers) {
258
  qrt_impl->submit(buffers, nBuffers);
259
}
260

261
void set_current_program(std::shared_ptr<qcor::CompositeInstruction> p) {
262
  qrt_impl->set_current_program(p);
263
264
}

265
void set_current_buffer(xacc::AcceleratorBuffer *buffer) {
Nguyen, Thien Minh's avatar
Nguyen, Thien Minh committed
266
267
268
  qrt_impl->set_current_buffer(buffer);
}

269
270
271
272
273
274
void persistBitstring(xacc::AcceleratorBuffer *buffer) {
  const auto bitstring = buffer->single_measurements_to_bitstring();
  if (!bitstring.empty()) {
    buffer->appendMeasurement(bitstring);
  }
}
275

276
void h(qreg q) {
277
278
279
280
281
  for (int i = 0; i < q.size(); i++) {
    h(q[i]);
  }
}

282
void x(qreg q) {
283
284
285
286
  for (int i = 0; i < q.size(); i++) {
    x(q[i]);
  }
}
287
void y(qreg q) {
288
289
290
291
  for (int i = 0; i < q.size(); i++) {
    y(q[i]);
  }
}
292
void z(qreg q) {
293
294
295
296
  for (int i = 0; i < q.size(); i++) {
    z(q[i]);
  }
}
297
void t(qreg q) {
298
299
300
301
  for (int i = 0; i < q.size(); i++) {
    t(q[i]);
  }
}
302
void tdg(qreg q) {
303
304
305
306
  for (int i = 0; i < q.size(); i++) {
    tdg(q[i]);
  }
}
307
void s(qreg q) {
308
309
310
311
  for (int i = 0; i < q.size(); i++) {
    s(q[i]);
  }
}
312
void sdg(qreg q) {
313
314
315
316
  for (int i = 0; i < q.size(); i++) {
    sdg(q[i]);
  }
}
317
void mz(qreg q) {
318
319
320
321
322
  for (int i = 0; i < q.size(); i++) {
    mz(q[i]);
  }
}

323
void rx(qreg q, const double theta) {
324
325
326
327
  for (int i = 0; i < q.size(); i++) {
    rx(q[i], theta);
  }
}
328
void ry(qreg q, const double theta) {
329
330
331
332
  for (int i = 0; i < q.size(); i++) {
    ry(q[i], theta);
  }
}
333
void rz(qreg q, const double theta) {
334
335
336
337
338
  for (int i = 0; i < q.size(); i++) {
    rz(q[i], theta);
  }
}
// U1(theta) gate
339
void u1(qreg q, const double theta) {
340
341
342
343
  for (int i = 0; i < q.size(); i++) {
    u1(q[i], theta);
  }
}
344
void u3(qreg q, const double theta, const double phi, const double lambda) {
345
346
347
348
  for (int i = 0; i < q.size(); i++) {
    u3(q[i], theta, phi, lambda);
  }
}
349
void reset(qreg q) {
350
351
352
353
  for (int i = 0; i < q.size(); i++) {
    reset(q[i]);
  }
}
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

void cnot(qreg src, qreg tgt) {
  assert(src.size() == tgt.size() &&
         "2-qubit broadcast must be across registers of same size.");

  for (int i = 0; i < src.size(); i++) {
    cnot(src[i], tgt[i]);
  }
}

void cy(qreg src, qreg tgt) {
  assert(src.size() == tgt.size() &&
         "2-qubit broadcast must be across registers of same size.");

  for (int i = 0; i < src.size(); i++) {
    cy(src[i], tgt[i]);
  }
}
void cz(qreg src, qreg tgt) {
  assert(src.size() == tgt.size() &&
         "2-qubit broadcast must be across registers of same size.");

  for (int i = 0; i < src.size(); i++) {
    cz(src[i], tgt[i]);
  }
}
void ch(qreg src, qreg tgt) {
  assert(src.size() == tgt.size() &&
         "2-qubit broadcast must be across registers of same size.");

  for (int i = 0; i < src.size(); i++) {
    ch(src[i], tgt[i]);
  }
}
388
389
390
391

QubitAllocator *getAncillaQubitAllocator() {
  return qrt_impl->get_anc_qubit_allocator();
}
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
}  // namespace quantum

namespace qcor {
void AncQubitAllocator::onDealloc(xacc::internal_compiler::qubit *in_qubit) {
  // std::cout << "Deallocate: " << (void *)in_qubit << "\n";
  // If this qubit was allocated from this pool:
  if (xacc::container::contains(m_allocatedQubits, in_qubit)) {
    const auto qIndex = std::find(m_allocatedQubits.begin(),
                                  m_allocatedQubits.end(), in_qubit) -
                        m_allocatedQubits.begin();
    // Strategy: create a storage copy of the returned qubit:
    // i.e. with the same index w.r.t. this global anc. buffer
    // but store it in the pool vector -> will stay alive
    // until giving out at the next allocate()
    qubit archive_qubit(ANC_BUFFER_NAME, qIndex, m_buffer.get());
    m_allocatedQubits[qIndex] = &archive_qubit;
    m_qubitPool.emplace_back(archive_qubit);
  }
}

xacc::internal_compiler::qubit AncQubitAllocator::allocate() {
  if (!m_qubitPool.empty()) {
    auto recycled_qubit = m_qubitPool.back();
    m_qubitPool.pop_back();
    return recycled_qubit;
  }
  if (!m_buffer) {
    // This must be the first call.
    assert(m_allocatedQubits.empty());
    m_buffer = xacc::qalloc(1);
    m_buffer->setName(ANC_BUFFER_NAME);
  }

  // Need to allocate new qubit:
  // Each new qubit will have an incrementing index.
  const auto newIdx = m_allocatedQubits.size();
  qubit new_qubit(ANC_BUFFER_NAME, newIdx, m_buffer.get());
  // Just track that we allocated this qubit
  m_allocatedQubits.emplace_back(&new_qubit);
  m_buffer->setSize(m_allocatedQubits.size());
  return new_qubit;
}
} // namespace qcor