xacc_internal_compiler.cpp 11.1 KB
Newer Older
1
#include "xacc_internal_compiler.hpp"
2
#include "Instruction.hpp"
3
#include "Utils.hpp"
4
#include "heterogeneous.hpp"
5
#include "config_file_parser.hpp"
6
#include "xacc.hpp"
7
#include "xacc_service.hpp"
8
#include "InstructionIterator.hpp"
9
10
#include <CompositeInstruction.hpp>
#include <stdlib.h>
11
12
13

namespace xacc {
namespace internal_compiler {
14
15
std::shared_ptr<Accelerator> qpu = nullptr;
std::shared_ptr<CompositeInstruction> lastCompiled = nullptr;
16
bool __execute = true;
17
std::vector<HeterogeneousMap> current_runtime_arguments = {};
18

19
void __set_verbose(bool v) { xacc::set_verbose(v); }
20
21
void compiler_InitializeXACC(const char *qpu_backend,
                             const std::vector<std::string> cmd_line_args) {
22
  if (!xacc::isInitialized()) {
23
    xacc::Initialize(cmd_line_args);
24
    xacc::external::load_external_language_plugins();
25
    auto at_exit = []() { xacc::Finalize(); };
26
    atexit(at_exit);
27
  }
28
  setAccelerator(qpu_backend);
29
30
}

31
void compiler_InitializeXACC(const char *qpu_backend, int shots) {
32
  if (!xacc::isInitialized()) {
33
    xacc::Initialize();
34
    xacc::external::load_external_language_plugins();
35
    auto at_exit = []() { xacc::Finalize(); };
36
37
    atexit(at_exit);
  }
38

39
  setAccelerator(qpu_backend, shots);
40
}
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
auto process_qpu_backend_str = [](const std::string &qpu_backend_str)
    -> std::pair<std::string, HeterogeneousMap> {
  bool has_extra_config = qpu_backend_str.find("[") != std::string::npos;
  auto qpu_name =
      (has_extra_config)
          ? qpu_backend_str.substr(0, qpu_backend_str.find_first_of("["))
          : qpu_backend_str;

  HeterogeneousMap options;
  if (has_extra_config) {
    auto first = qpu_backend_str.find_first_of("[");
    auto second = qpu_backend_str.find_first_of("]");
    auto qpu_config = qpu_backend_str.substr(first + 1, second - first - 1);

    auto key_values = split(qpu_config, ',');
    for (auto key_value : key_values) {
      auto tmp = split(key_value, ':');
      auto key = tmp[0];
      auto value = tmp[1];
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
      if (key == "qcor_qpu_config") {
        // If the config is provided in a file:
        // We could check for file extension here to
        // determine a parsing plugin.
        // Currently, we only support a simple key-value format (INI like).
        // e.g.
        // String like config:
        // name=XACC
        // Boolean configs
        // true_val=true
        // false_val=false
        // Array/vector configs
        // array=[1,2,3]
        // array_double=[1.0,2.0,3.0]
        auto parser = xacc::getService<ConfigFileParsingUtil>("ini");
        options = parser->parse(value);
      } else {
        // check if int first, then double,
        // finally just throw it in as a string
79
        try {
80
81
          auto i = std::stoi(value);
          options.insert(key, i);
82
        } catch (std::exception &e) {
83
84
85
86
87
88
          try {
            auto d = std::stod(value);
            options.insert(key, d);
          } catch (std::exception &e) {
            options.insert(key, value);
          }
89
90
91
92
93
94
95
        }
      }
    }
  }

  return std::make_pair(qpu_name, options);
};
96
97

void setAccelerator(const char *qpu_backend) {
98

99
100
  if (qpu) {
    if (qpu_backend != qpu->name()) {
101
102
      const auto [qpu_name, config] = process_qpu_backend_str(qpu_backend);
      qpu = xacc::getAccelerator(qpu_name, config);
103
104
    }
  } else {
105
106
    const auto [qpu_name, config] = process_qpu_backend_str(qpu_backend);
    qpu = xacc::getAccelerator(qpu_name, config);
107
108
  }
}
109

110
void setAccelerator(const char *qpu_backend, int shots) {
111
112
  if (qpu) {
    if (qpu_backend != qpu->name()) {
113
114
115
      auto [qpu_name, config] = process_qpu_backend_str(qpu_backend);
      config.insert("shots", shots);
      qpu = xacc::getAccelerator(qpu_backend, config);
116
117
    }
  } else {
118
119
    auto [qpu_name, config] = process_qpu_backend_str(qpu_backend);
    config.insert("shots", shots);
120
    qpu = xacc::getAccelerator(qpu_backend, {std::make_pair("shots", shots)});
121
  }
122
123
}

124
std::shared_ptr<Accelerator> get_qpu() { return qpu; }
125

126
std::shared_ptr<CompositeInstruction> getLastCompiled() { return lastCompiled; }
127

128
129
// Map kernel source string representing a single
// kernel function to a single CompositeInstruction (src to IR)
130
131
std::shared_ptr<CompositeInstruction> compile(const char *compiler_name,
                                              const char *kernel_src) {
132
  auto compiler = xacc::getCompiler(compiler_name);
133
  auto IR = compiler->compile(kernel_src, qpu);
134
  auto program = IR->getComposites()[0];
135
136
  lastCompiled = program;
  return program;
137
138
}

139
140
std::shared_ptr<CompositeInstruction> getCompiled(const char *kernel_name) {
  return xacc::hasCompiled(kernel_name) ? xacc::getCompiled(kernel_name)
141
                                        : nullptr;
Mccaskey, Alex's avatar
Mccaskey, Alex committed
142
143
}

144
// Run quantum compilation routines on IR
145
146
void optimize(std::shared_ptr<CompositeInstruction> program,
              const OptLevel opt) {
147

148
149
  xacc::info("[InternalCompiler] Pre-optimization, circuit has " +
             std::to_string(program->nInstructions()) + " instructions.");
150

151
152
  if (opt == DEFAULT) {
    auto optimizer = xacc::getIRTransformation("circuit-optimizer");
153
    optimizer->apply(program, qpu);
154
155
156
  } else {
    xacc::error("Other Optimization Levels not yet supported.");
  }
157

158
159
  xacc::info("[InternalCompiler] Post-optimization, circuit has " +
             std::to_string(program->nInstructions()) + " instructions.");
160
161
}

162
void execute(AcceleratorBuffer *buffer,
163
             std::vector<std::shared_ptr<CompositeInstruction>> programs) {
164

165
  qpu->execute(xacc::as_shared_ptr(buffer), programs);
166
167
}

168
169
// Execute on the specified QPU, persisting results to
// the provided buffer.
170
171
void execute(AcceleratorBuffer *buffer,
             std::shared_ptr<CompositeInstruction> program,
172
             double *parameters) {
173

Mccaskey, Alex's avatar
Mccaskey, Alex committed
174
175
  std::shared_ptr<CompositeInstruction> program_as_shared;
  if (parameters) {
176
    std::vector<double> values(parameters, parameters + program->nVariables());
177
    program = program->operator()(values);
Mccaskey, Alex's avatar
Mccaskey, Alex committed
178
  }
179
  auto buffer_as_shared = std::shared_ptr<AcceleratorBuffer>(
180
181
      buffer, xacc::empty_delete<AcceleratorBuffer>());

182
  qpu->execute(buffer_as_shared, program);
183
}
184

185
void execute(AcceleratorBuffer **buffers, const int nBuffers,
186
187
             std::shared_ptr<CompositeInstruction> program,
             double *parameters) {
188

189
190
191
  //  Should take vector of buffers, and we collapse them
  //  into a single unified buffer for execution, then set the
  //  measurement counts accordingly in postprocessing.
192

193
  std::vector<AcceleratorBuffer *> bvec(buffers, buffers + nBuffers);
194
195
196
  std::vector<std::string> buffer_names;
  for (auto &a : bvec)
    buffer_names.push_back(a->name());
197

198
199
200
  // Do we have any unknown ancilla bits?
  std::vector<std::string> possible_extra_buffers;
  int possible_size = -1;
201
  InstructionIterator it(program);
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  while (it.hasNext()) {
    auto &next = *it.next();
    auto bnames = next.getBufferNames();
    for (auto &bb : bnames) {
      if (!xacc::container::contains(buffer_names, bb) &&
          !xacc::container::contains(possible_extra_buffers, bb)) {
        // we have an unknown buffer with name bb, need to figure out its size
        // too
        possible_extra_buffers.push_back(bb);
      }
    }
  }

  for (auto &possible_buffer : possible_extra_buffers) {
    std::set<std::size_t> sizes;
217
    InstructionIterator it2(program);
218
219
220
221
222
223
    while (it2.hasNext()) {
      auto &next = *it2.next();
      for (auto &bit : next.bits()) {
        sizes.insert(bit);
      }
    }
224
    auto size = *std::max_element(sizes.begin(), sizes.end());
225
226
    auto extra = qalloc(size);
    extra->setName(possible_buffer);
227
228
    xacc::debug("[xacc_internal_compiler] Adding extra buffer " +
                possible_buffer + " of size " + std::to_string(size));
229
230
231
    bvec.push_back(extra.get());
  }

232
233
234
235
236
237
  // Merge the buffers. Keep track of buffer_name to shift in
  // all bit indices operating on that buffer_name, a map of
  // buffer names to the buffer ptr, and start a map to collect
  // buffer names to measurement counts
  int global_reg_size = 0;
  std::map<std::string, int> shift_map;
238
239
  std::vector<std::string> shift_map_names;
  std::vector<int> shift_map_shifts;
240
  std::map<std::string, AcceleratorBuffer *> buf_map;
241
242
243
  std::map<std::string, std::map<std::string, int>> buf_counts;
  for (auto &b : bvec) {
    shift_map.insert({b->name(), global_reg_size});
244
245
    shift_map_names.push_back(b->name());
    shift_map_shifts.push_back(global_reg_size);
246
247
248
249
    buf_map.insert({b->name(), b});
    buf_counts.insert({b->name(), {}});
    global_reg_size += b->size();
  }
250

251
252
  xacc::debug("[xacc_internal_compiler] Creating register of size " +
              std::to_string(global_reg_size));
253
254
255
256
  auto tmp = xacc::qalloc(global_reg_size);

  // Update Program bit indices based on new global
  // qubit register
257
  InstructionIterator iter(program);
258
259
260
  while (iter.hasNext()) {
    auto &next = *iter.next();
    std::vector<std::size_t> newBits;
261
    int counter = 0;
262
    for (auto &b : next.bits()) {
263
      auto buffer_name = next.getBufferName(counter);
264
265
      auto shift = shift_map[buffer_name];
      newBits.push_back(b + shift);
266
      counter++;
267
268
269
    }
    next.setBits(newBits);
    // FIXME Update buffer_names here too
270
271
    std::vector<std::string> unified_buf_names;
    for (int j = 0; j < next.nRequiredBits(); j++) {
272
      unified_buf_names.push_back("q");
273
274
    }
    next.setBufferNames(unified_buf_names);
275
276
  }

277
  std::vector<std::size_t> measure_idxs;
278
  InstructionIterator iter2(program);
279
  while (iter2.hasNext()) {
280
281
282
283
    auto &next = *iter2.next();
    if (next.name() == "Measure") {
      measure_idxs.push_back(next.bits()[0]);
    }
284
285
  }

286
  // Now execute using the global merged register
287
  execute(tmp.get(), program, parameters);
288
289
290
291

  // Take bit strings and map to buffer individual bit strings
  for (auto &kv : tmp->getMeasurementCounts()) {
    auto bitstring = kv.first;
292
293
294
295

    // Some backends return bitstring of size = number of measures
    // instead of size = global_reg_size, adjust if so
    if (bitstring.size() == measure_idxs.size()) {
296
297
298
      std::string tmps = "";
      for (int j = 0; j < global_reg_size; j++)
        tmps += "0";
299

300
301
302
      for (int j = 0; j < measure_idxs.size(); j++) {
        tmps[measure_idxs[j]] = bitstring[j];
      }
303

304
      bitstring = tmps;
305
306
307
    }

    // The following processing the bit string assuming LSB
308
    if (qpu->getBitOrder() == Accelerator::BitOrder::MSB) {
309
      std::reverse(bitstring.begin(), bitstring.end());
310
311
312
    }

    for (int j = 0; j < shift_map_names.size(); j++) {
313

314
315
      auto shift = shift_map_shifts[j];
      auto buff_name = shift_map_names[j];
316
      auto buffer = buf_map[buff_name];
317

318
      auto buffer_bitstring = bitstring.substr(shift, buffer->size());
319
320

      if (qpu->getBitOrder() == Accelerator::BitOrder::MSB) {
321
        std::reverse(buffer_bitstring.begin(), buffer_bitstring.end());
322
      }
323

324
325
326
327
328
      if (buf_counts[buff_name].count(buffer_bitstring)) {
        buf_counts[buff_name][buffer_bitstring] += kv.second;
      } else {
        buf_counts[buff_name].insert({buffer_bitstring, kv.second});
      }
329
330
331
    }
  }

332
333
  for (auto &b : bvec) {
    b->setMeasurements(buf_counts[b->name()]);
334
335
336
    b->addExtraInfo("endianness",
                    qpu->getBitOrder() == Accelerator::BitOrder::LSB ? "lsb"
                                                                     : "msb");
337
338
339
  }
}

340
341
} // namespace internal_compiler
} // namespace xacc