libqasm
library for handling cQASM files
qasm_new_to_old.hpp
Go to the documentation of this file.
1 
7 #pragma once
8 #ifndef QASM_NEW_TO_OLD_HPP
9 
10 #include "cqasm.hpp"
11 #include "qasm_ast.hpp"
12 
13 namespace compiler {
14 
19 namespace new_to_old {
20 
25 enum class ParameterType {
26  NoArg,
27  SingleBit,
31  TwoQubit,
34  ThreeQubit,
36  SingleInt,
38  // Confusingly and annoyingly, the not gate uses the
39  // control bits attribute instead of the bits_ attribute
40  // (like display does, for instance). So we have to
41  // special-case it for compatibility.
42  NotGate
43 };
44 
49 static NumericalIdentifiers convert_indices(const cqasm::tree::Many<cqasm::values::ConstInt> &indices) {
50  NumericalIdentifiers retval;
51  for (auto control_bit : indices) {
52  retval.addToVector(control_bit->value);
53  }
54  return retval;
55 }
56 
60 static std::string convert_axis(const cqasm::values::Value &value) {
61  switch (value->as_const_axis()->value) {
62  case cqasm::primitives::Axis::X: return "x";
63  case cqasm::primitives::Axis::Y: return "y";
64  case cqasm::primitives::Axis::Z: return "z";
65  }
66  throw std::runtime_error("unreachable code, corrupted axis enum");
67 }
68 
73 static Operation *convert_instruction(const cqasm::semantic::Instruction &instruction) {
74 
75  // Handle the normal arguments.
76  Operation *op = nullptr;
77  switch (instruction.instruction->get_annotation<ParameterType>()) {
79  op = new Operation(
80  instruction.instruction->name
81  );
82  break;
84  op = new Operation(
85  instruction.instruction->name,
86  Bits(convert_indices(instruction.operands[0]->as_bit_refs()->index))
87  );
88  break;
90  op = new Operation(
91  instruction.instruction->name,
92  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index))
93  );
94  break;
96  op = new Operation(
97  instruction.instruction->name,
98  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
99  instruction.operands[1]->as_const_real()->value
100  );
101  break;
103  op = new Operation(
104  instruction.instruction->name,
105  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index))
106  );
107  {
108  auto mat = instruction.operands[1]->as_const_complex_matrix()->value;
109  std::vector<double> mat_elements;
110  for (size_t row = 1; row <= mat.size_rows(); row++) {
111  for (size_t col = 1; col <= mat.size_cols(); col++) {
112  auto val = mat.at(row, col);
113  mat_elements.push_back(val.real());
114  mat_elements.push_back(val.imag());
115  }
116  }
117  op->setUMatrixElements(mat_elements);
118  }
119  break;
121  op = new Operation(
122  instruction.instruction->name,
123  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
124  Qubits(convert_indices(instruction.operands[1]->as_qubit_refs()->index))
125  );
126  break;
128  op = new Operation(
129  instruction.instruction->name,
130  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
131  Qubits(convert_indices(instruction.operands[1]->as_qubit_refs()->index)),
132  instruction.operands[2]->as_const_real()->value
133  );
134  break;
136  op = new Operation(
137  instruction.instruction->name,
138  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
139  Qubits(convert_indices(instruction.operands[1]->as_qubit_refs()->index)),
140  instruction.operands[2]->as_const_int()->value
141  );
142  break;
144  op = new Operation(
145  instruction.instruction->name,
146  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
147  Qubits(convert_indices(instruction.operands[1]->as_qubit_refs()->index)),
148  Qubits(convert_indices(instruction.operands[2]->as_qubit_refs()->index))
149  );
150  break;
152  op = new Operation(
153  instruction.instruction->name,
154  Qubits(convert_indices(instruction.operands[0]->as_qubit_refs()->index)),
155  convert_axis(instruction.operands[1]),
156  Qubits(convert_indices(instruction.operands[2]->as_qubit_refs()->index)),
157  convert_axis(instruction.operands[3])
158  );
159  break;
161  op = new Operation(
162  instruction.instruction->name,
163  instruction.operands[0]->as_const_int()->value
164  );
165  break;
167  op = new Operation(
168  instruction.instruction->name,
169  instruction.operands[0]->as_const_string()->value
170  );
171  break;
173  op = new Operation(
174  instruction.instruction->name
175  );
176  op->setControlBits(Bits(convert_indices(instruction.operands[0]->as_bit_refs()->index)));
177  break;
178  }
179  if (!op) {
181  "unsupported parameter signature for old API",
182  &instruction
183  );
184  }
185 
186  // Handle the condition code.
187  if (auto control_bits = instruction.condition->as_bit_refs()) {
188  op->setControlBits(Bits(convert_indices(control_bits->index)));
189  } else if (auto control_const = instruction.condition->as_const_bool()) {
190  if (!control_const->value) {
191  // Constant false condition. Is at the time
192  // of writing already optimized away
193  // earlier, but it can't hurt to
194  // double-check.
195  return nullptr;
196  }
197  } else {
199  "unsupported condition for old API",
200  &*instruction.condition
201  );
202  }
203 
204  return op;
205 }
206 
212 static void handle_parse_result(QasmRepresentation &qasm, cqasm::parser::ParseResult &&result) {
213 
214  // Throw an error if parsing failed.
215  if (!result.errors.empty()) {
216  throw std::runtime_error(result.errors[0]);
217  }
218 
219  // Create the semantic analyzer.
220  auto analyzer = cqasm::analyzer::Analyzer{"1.0"};
221  analyzer.register_default_functions_and_mappings();
222 
223  // The old library accepted the depolarizing_channel error model
224  // with *any* number of float arguments. I'm just randomly
225  // assuming 50 arguments is enough... Just increase this if more
226  // are needed.
227  std::ostringstream args;
228  for (int i = 0; i <= 50; i++) {
229  analyzer.register_error_model("depolarizing_channel", args.str());
230  args << "r";
231  }
232 
233  // Register the instructions that were baked into the old
234  // grammar.
235 #define REG(typ, ...) analyzer.register_instruction_with_annotation<ParameterType>(ParameterType::typ, __VA_ARGS__)
236  REG(NoArg, "measure_all", "", false, false);
237  REG(MeasureParity, "measure_parity", "QaQa", false, false, false, true);
238  REG(SingleQubit, "x", "Q");
239  REG(SingleQubit, "y", "Q");
240  REG(SingleQubit, "z", "Q");
241  REG(SingleQubit, "i", "Q");
242  REG(SingleQubit, "h", "Q");
243  REG(SingleQubit, "x90", "Q");
244  REG(SingleQubit, "y90", "Q");
245  REG(SingleQubit, "mx90", "Q");
246  REG(SingleQubit, "my90", "Q");
247  REG(SingleQubit, "s", "Q");
248  REG(SingleQubit, "sdag", "Q");
249  REG(SingleQubit, "t", "Q");
250  REG(SingleQubit, "tdag", "Q");
251  REG(SingleQubitMatrix, "u", "Qu");
252  REG(SingleQubit, "prep", "Q", false);
253  REG(SingleQubit, "prep_x", "Q", false);
254  REG(SingleQubit, "prep_y", "Q", false);
255  REG(SingleQubit, "prep_z", "Q", false);
256  REG(SingleQubit, "measure", "Q", false);
257  REG(SingleQubit, "measure_x", "Q", false);
258  REG(SingleQubit, "measure_y", "Q", false);
259  REG(SingleQubit, "measure_z", "Q", false);
260  REG(SingleQubitReal, "rx", "Qr");
261  REG(SingleQubitReal, "ry", "Qr");
262  REG(SingleQubitReal, "rz", "Qr");
263  REG(TwoQubit, "cnot", "QQ");
264  REG(TwoQubit, "cz", "QQ");
265  REG(TwoQubit, "swap", "QQ");
266  REG(TwoQubitReal, "cr", "QQr");
267  REG(TwoQubitInt, "crk", "QQi");
268  REG(ThreeQubit, "toffoli", "QQQ");
269  REG(NotGate, "not", "B");
270  REG(NoArg, "display", "", false, false);
271  REG(SingleBit, "display", "B", false, false);
272  REG(NoArg, "display_binary", "", false, false);
273  REG(SingleBit, "display_binary", "B", false, false);
274  REG(SingleInt, "skip", "i", false, false);
275  REG(SingleInt, "wait", "i", false, false);
276  REG(NoArg, "reset-averaging", "", false, false);
277  REG(SingleQubit, "reset-averaging", "Q", false, false);
278  REG(SingleString, "load_state", "s", false, false);
279 #undef REG
280 
281  // Run analysis.
282  auto analysis_result = analyzer.analyze(*result.root->as_program());
283  if (!analysis_result.errors.empty()) {
284  throw std::runtime_error(analysis_result.errors[0]);
285  }
286 
287  // Convert the new parse tree to the old AST classes.
288  // GENERAL NOTE: the new operators all over the place introduce
289  // memory leaks. This is legacy behavior intentionally kept
290  // for backward compatibility.
291 
292  // Handle storage locations.
293  if (analysis_result.root->num_qubits == 0) {
294  throw std::runtime_error("qubits statement is missing");
295  }
296  qasm.qubitRegister(analysis_result.root->num_qubits);
297  if (!analysis_result.root->variables.empty()) {
298  throw std::runtime_error("variables are not supported");
299  }
300 
301  // Copy version number (poorly).
302  double version = analysis_result.root->version->items[0];
303  if (analysis_result.root->version->items.size() > 1) {
304  int sub = analysis_result.root->version->items[1];
305  version += 0.1 * ((sub > 9) ? 9 : sub);
306  }
307  qasm.versionNumber(version);
308 
309  // Copy error model.
310  if (!analysis_result.root->error_model.empty()) {
311  std::vector<double> args;
312  for (auto arg : analysis_result.root->error_model->parameters) {
313  args.push_back(arg->as_const_real()->value);
314  }
315  qasm.setErrorModel(analysis_result.root->error_model->name, args);
316  }
317 
318  // Copy subcircuits.
319  auto &scs = qasm.getSubCircuits();
320  for (auto subcircuit : analysis_result.root->subcircuits) {
321 
322  // The old API adds a default subcircuit automatically in
323  // the QasmRepresentation constructor (so it always exists),
324  // whereas the new API only adds a default one when it would
325  // not be empty. The default subcircuit in the new API is
326  // represented with an (otherwise illegal) empty name. So,
327  // if we only add subcircuits with a nonempty name, and
328  // always add bundles to the last subcircuit, we end up
329  // doing the right thing.
330  if (!subcircuit->name.empty()) {
331  int line_number = 0;
332  if (auto loc = subcircuit->get_annotation_ptr<cqasm::parser::SourceLocation>()) {
333  line_number = loc->first_line;
334  }
335  SubCircuit sc {
336  subcircuit->name.c_str(),
337  (int)scs.numberOfSubCircuits(),
338  line_number
339  };
340  sc.numberIterations(subcircuit->iterations);
341  scs.addSubCircuit(sc);
342  }
343 
344  // Add bundles to the last subcircuit.
345  for (auto bundle : subcircuit->bundles) {
346 
347  // Construct the OperationsCluster for this bundle.
348  OperationsCluster *opclus = nullptr;
349  for (auto instruction : bundle->items) {
350 
351  // Convert the instruction.
352  Operation *op = convert_instruction(*instruction);
353  if (!op) {
354  continue;
355  }
356 
357  // Add the operation to an operation cluster. When
358  // this is the first instruction, we first construct
359  // the cluster. This makes it a "serial" cluster.
360  // When we receive another instruction, it's added
361  // with the addParallelOperation method, which marks
362  // the cluster as parallel.
363  if (!opclus) {
364  int line_number = 0;
365  if (auto loc = instruction->get_annotation_ptr<cqasm::parser::SourceLocation>()) {
366  line_number = loc->first_line;
367  }
368  opclus = new OperationsCluster(op, line_number);
369  } else {
370  opclus->addParallelOperation(op);
371  }
372  }
373 
374  // Add the cluster to the last subcircuit if the bundle
375  // was nonempty.
376  if (opclus) {
377  scs.lastSubCircuit().addOperationsCluster(opclus);
378  }
379  }
380  }
381 
382  // Copy mappings for as far as this is supported by the old API.
383  for (auto mapping : analysis_result.root->mappings) {
384  if (auto qubit_refs = mapping->value->as_qubit_refs()) {
385  qasm.addMappings(mapping->name, convert_indices(qubit_refs->index), true);
386  } else if (auto bit_refs = mapping->value->as_bit_refs()) {
387  qasm.addMappings(mapping->name, convert_indices(bit_refs->index), false);
388  }
389  }
390 
391 }
392 
393 } // namespace new_to_old
394 } // namespace compiler
395 
396 #endif
ParameterType
All instruction types supported by the old API based on the types of their parameters alone...
void qubitRegister(int participating_number)
Definition: qasm_ast.hpp:618
SubCircuits & getSubCircuits()
Definition: qasm_ast.hpp:638
void addMappings(std::string name_key, NumericalIdentifiers indices, bool isQubit)
Definition: qasm_ast.hpp:643
void setErrorModel(std::string error_model_type, std::vector< double > error_model_num_params)
Definition: qasm_ast.hpp:665
Main include file for libqasm; this is what you should be #includeing.
void addToVector(const int index)
Definition: qasm_ast.hpp:27
Namespace used for most of the original API.
Definition: qasm_ast.hpp:18
double versionNumber() const
Definition: qasm_ast.hpp:628
::tree::base::Many< T > Many
Definition: cqasm-tree.hpp:32
void addParallelOperation(Operation *valid_op)
Definition: qasm_ast.hpp:447
#define REG(typ,...)
void setUMatrixElements(const std::vector< double > input)
Definition: qasm_ast.hpp:312
void setControlBits(Bits control_bits)
Definition: qasm_ast.hpp:291
annotations::SourceLocation SourceLocation
int numberIterations() const
Definition: qasm_ast.hpp:514
Exception used for analysis errors.
Definition: cqasm-error.hpp:27