Circuits dynamiques
Les circuits dynamiques permettent de mesurer les qubits pendant l'exécution du circuit et d'utiliser les résultats de ces mesures pour contrôler les opérations suivantes. Qiskit offre une prise en charge complète pour la conception et l'analyse de circuits dynamiques. Pour plus d'informations, consultez le guide « Feedforward classique et flux de contrôle ».
L'API C de Qiskit ne prend actuellement en charge que l'analyse des instructions de flux de contrôle et des expressions classiques. La prise en charge de la création d'expressions classiques et de l'ajout d'instructions de contrôle de flux sera intégrée dans les prochaines versions de Qiskit.
Lorsque vous utilisez cette API, gardez à l'esprit les hypothèses et les limitations suivantes :
- La plupart des objets renvoyés par l'API de flux de contrôle et d'expressions classiques sont des pointeurs empruntés en lecture seule (renvoyés sous la forme
const *). Celles-ci ne restent valides que tant que l'objet parent — par exemple, le circuit auquel appartient une instructionIfElse— est actif. Par conséquent, les appelants ne doivent pas libérer les pointeurs empruntés et doivent s'assurer que les objets parents survivent à toute utilisation de ces pointeurs. - Cette API n'utilise pas de codes d'erreur. Lorsqu'elles sont appelées correctement, ces fonctions sont infaillibles. Cependant, les fonctions spécifiques à une variante (par exemple,
qk_control_flow_box_duration_kind()qui attend une instructionBox) entrera en état de panique et interrompra le processus utilisé avec un objet de type incorrect. Pour éviter cela, un ensemble de fonctions de requête est fourni afin de vérifier le type ou la nature d'un objet avant d'appeler la fonction spécifique à la variante appropriée. - Qiskit utilise des entiers de grande taille pour représenter certaines structures de contrôle de flux et d'expression classiques, telles que les valeurs de condition des registres classiques et les étiquettes « switch case ». La prise en charge complète des grands entiers sera ajoutée à l'API C à l'avenir. D'ici là, les valeurs numériques de cette API sont limitées à celles pouvant tenir dans un
uint64_t.
Le programme d'exemple suivant présente toutes les fonctions et tous les types de l'API permettant d'analyser les instructions de contrôle de flux et les expressions classiques, ainsi qu'une sélection de fonctions classiques d'interrogation des registres. Le point d'entrée se trouve en inspect_circuit bas; il appelle les fonctions d'aide définies au-dessus.
// Forward declarations for recursive circuit inspection
void inspect_circuit(const QkCircuit *, const QkControlFlowInstruction *, int);
void inspect_register(const QkClassicalRegister *creg, int indent) {
size_t num_bits = qk_classical_register_num_bits(creg);
char *reg_name = qk_classical_register_name(creg);
printf("%*sClassical Register: name='%s', num_bits=%zu\n", indent, "",
reg_name ? reg_name : "<unnamed>", num_bits);
qk_str_free(reg_name);
}
void inspect_expr(const QkExprNode *expr_node, int indent) {
QkExprNodeKind kind = qk_expr_kind(expr_node);
printf("%*sExpression kind: %d\n", indent, "", kind);
switch (kind) {
case QkExprNodeKind_Unary: {
QkUnaryExprInfo unary = qk_expr_unary_info(expr_node);
printf("%*sUnary operation: op=%d, type=%d\n", indent, "", unary.op, unary.ty.ty);
printf("%*sOperand:\n", indent, "");
inspect_expr(unary.operand, indent + 2);
break;
}
case QkExprNodeKind_Binary: {
QkBinaryExprInfo binary = qk_expr_binary_info(expr_node);
printf("%*sBinary operation: op=%d, type=%d\n", indent, "", binary.op, binary.ty.ty);
printf("%*sLeft operand:\n", indent, "");
inspect_expr(binary.left, indent + 2);
printf("%*sRight operand:\n", indent, "");
inspect_expr(binary.right, indent + 2);
break;
}
case QkExprNodeKind_Cast: {
QkCastExprInfo cast = qk_expr_cast_info(expr_node);
printf("%*sCast to type: %d", indent, "", cast.ty.ty);
if (cast.ty.ty == QkExprType_Uint) {
printf(" (width=%u)", cast.ty.width);
}
printf("\n%*sOperand:\n", indent, "");
inspect_expr(cast.operand, indent + 2);
break;
}
case QkExprNodeKind_Index: {
QkIndexExprInfo index = qk_expr_index_info(expr_node);
printf("%*sIndex operation, type=%d\n", indent, "", index.ty.ty);
printf("%*sTarget:\n", indent, "");
inspect_expr(index.target, indent + 2);
printf("%*sIndex:\n", indent, "");
inspect_expr(index.index, indent + 2);
break;
}
case QkExprNodeKind_Value: {
const QkValue *value = qk_expr_as_value(expr_node);
QkExprTypeInfo value_type = qk_value_type_info(value);
printf("%*sValue type: %d", indent, "", value_type.ty);
switch (value_type.ty) {
case QkExprType_Duration: {
QkDurationInfo duration_info = qk_value_duration_info(value);
if (duration_info.ty == QkDurationType_Dt) {
printf(", value=%ld dt\n", duration_info.value.dt);
} else {
printf(", value=%f (unit: %d)\n", duration_info.value.time, duration_info.ty);
}
break;
}
case QkExprType_Float: {
double float_val = qk_value_float(value);
printf(", value=%f\n", float_val);
break;
}
case QkExprType_Uint: {
uint64_t val = qk_value_uint(value);
printf(" (width=%u), value=%lu\n", value_type.width, val);
break;
}
case QkExprType_Bool: {
bool bool_val = qk_value_bool(value);
printf(", value=%s\n", bool_val ? "true" : "false");
break;
}
}
break;
}
case QkExprNodeKind_Var: {
const QkVar *var = qk_expr_as_var(expr_node);
char *name = qk_var_name(var);
QkExprTypeInfo type_info = qk_var_type_info(var);
printf("%*sVariable: name='%s', type=%d", indent, "", name ? name : "<unnamed>",
type_info.ty);
if (type_info.ty == QkExprType_Uint) {
printf(" (width=%u)", type_info.width);
}
printf("\n");
if (name != NULL) {
qk_str_free(name);
}
break;
}
case QkExprNodeKind_Stretch: {
const QkStretch *stretch = qk_expr_as_stretch(expr_node);
char *name = qk_stretch_name(stretch);
printf("%*sStretch: name='%s'\n", indent, "", name);
qk_str_free(name);
break;
}
}
}
void inspect_condition(const QkControlFlowInstruction *cf_inst, int indent) {
QkConditionType condition_type = qk_control_flow_condition_type(cf_inst);
printf("%*sCondition type: %d\n", indent, "", condition_type);
switch (condition_type) {
case QkConditionType_ClBit: {
QkConditionBitInfo cond_bit_info = qk_control_flow_condition_bit_info(cf_inst);
printf("%*sCondition on classical bit: clbit=%u, value=%s\n", indent, "",
cond_bit_info.clbit, cond_bit_info.condition ? "true" : "false");
break;
}
case QkConditionType_ClReg: {
uint64_t cond_width = qk_control_flow_condition_reg_cond_bit_width(cf_inst);
printf("%*sCondition on classical register (width=%lu bits)\n", indent, "", cond_width);
if (cond_width <= 64) {
uint64_t condition = qk_control_flow_condition_reg_cond_uint(cf_inst);
printf("%*sCondition value: %lu\n", indent, "", condition);
} else {
printf("%*sCondition value too large (>64 bits) for direct display\n", indent, "");
}
const QkClassicalRegister *creg = qk_control_flow_condition_reg(cf_inst);
inspect_register(creg, indent + 2);
break;
}
case QkConditionType_Expr: {
printf("%*sCondition based on expression:\n", indent, "");
const QkExprNode *expr = qk_control_flow_condition_expr(cf_inst);
inspect_expr(expr, indent + 2);
break;
}
}
}
void inspect_box(const QkControlFlowInstruction *cf_inst, int indent) {
printf("%*sInspecting Box instruction\n", indent, "");
QkBoxDurationKind duration_type = qk_control_flow_box_duration_kind(cf_inst);
switch (duration_type) {
case QkBoxDurationKind_NoDuration:
printf("%*sNo duration specified\n", indent, "");
break;
case QkBoxDurationKind_Duration: {
QkDurationInfo duration_info = qk_control_flow_box_duration_val_info(cf_inst);
printf("%*sDuration: ", indent, "");
if (duration_info.ty == QkDurationType_Dt) {
printf("%ld dt\n", duration_info.value.dt);
} else {
printf("%f (unit: %d)\n", duration_info.value.time, duration_info.ty);
}
break;
}
case QkBoxDurationKind_Expr: {
printf("%*sDuration specified by expression:\n", indent, "");
const QkExprNode *expr = qk_control_flow_box_duration_expr(cf_inst);
inspect_expr(expr, indent + 2);
break;
}
}
}
void inspect_for_loop(const QkControlFlowInstruction *cf_inst, int indent) {
printf("%*sInspecting ForLoop instruction\n", indent, "");
QkLoopCollectionType collection_type = qk_control_flow_loop_collection_type(cf_inst);
printf("%*sCollection type: %s\n", indent, "",
collection_type == QkLoopCollectionType_List ? "List" : "Range");
switch (collection_type) {
case QkLoopCollectionType_List: {
QkLoopElements loop_elements = qk_control_flow_loop_elements(cf_inst);
printf("%*sLoop elements (%zu items): [", indent, "", loop_elements.len);
for (size_t i = 0; i < loop_elements.len; i++) {
printf("%zu%s", loop_elements.elements[i], i < loop_elements.len - 1 ? ", " : "");
}
printf("]\n");
break;
}
case QkLoopCollectionType_Range: {
int64_t start, stop, step;
qk_control_flow_loop_range(cf_inst, &start, &stop, &step);
printf("%*sLoop range: start=%ld, stop=%ld, step=%ld\n", indent, "", start, stop, step);
break;
}
}
// Inspect the loop parameter, if it exists
QkLoopParamKind param_kind = qk_control_flow_loop_param_kind(cf_inst);
switch (param_kind) {
case QkLoopParamKind_NoLoopParam:
printf("%*sNo loop parameter\n", indent, "");
break;
case QkLoopParamKind_Parameter: {
QkSymbolInfo symbol_info = qk_control_flow_loop_symbol_info(cf_inst);
printf("%*sLoop parameter (Symbol): ", indent, "");
if (symbol_info.ty == QkSymbolType_Standalone) {
printf("name='%s'\n", symbol_info.name ? symbol_info.name : "<unnamed>");
} else if (symbol_info.ty == QkSymbolType_Element) {
printf("element index=%zu\n", symbol_info.index);
}
qk_str_free(symbol_info.name);
break;
}
case QkLoopParamKind_Variable: {
const QkVar *var = qk_control_flow_loop_variable(cf_inst);
char *name = qk_var_name(var);
QkExprTypeInfo type_info = qk_var_type_info(var);
printf("%*sLoop parameter (Variable): name='%s', type=%d", indent, "",
name ? name : "<unnamed>", type_info.ty);
if (type_info.ty == QkExprType_Uint) {
printf(" (width=%u)", type_info.width);
}
printf("\n");
if (name != NULL) {
qk_str_free(name);
}
break;
}
}
}
void inspect_switch(const QkControlFlowInstruction *cf_inst, int indent) {
printf("%*sInspecting Switch instruction\n", indent, "");
// Inspect the Switch instruction target
QkConditionType target_type = qk_control_flow_switch_target_type(cf_inst);
printf("%*sTarget type: %d\n", indent, "", target_type);
switch (target_type) {
case QkConditionType_ClBit: {
uint32_t bit = qk_control_flow_switch_target_bit(cf_inst);
printf("%*sTarget bit: %u\n", indent, "", bit);
break;
}
case QkConditionType_ClReg: {
printf("%*sTarget register:\n", indent, "");
const QkClassicalRegister *creg = qk_control_flow_switch_target_register(cf_inst);
inspect_register(creg, indent + 2);
break;
}
case QkConditionType_Expr: {
printf("%*sTarget expression:\n", indent, "");
const QkExprNode *expr = qk_control_flow_switch_target_expr(cf_inst);
inspect_expr(expr, indent + 2);
break;
}
}
// Inspect the Switch instruction cases
size_t num_cases = qk_control_flow_switch_num_cases(cf_inst);
printf("%*sNumber of cases: %zu\n", indent, "", num_cases);
for (size_t case_idx = 0; case_idx < num_cases; case_idx++) {
printf("%*sCase %zu:\n", indent, "", case_idx);
uint64_t bit_width = qk_control_flow_switch_case_labels_bit_width(cf_inst, case_idx);
if (bit_width <= 64) {
QkSwitchCaseLabels labels = qk_control_flow_switch_case_labels_uint(cf_inst, case_idx);
printf("%*sLabels (%zu): [", indent + 2, "", labels.num_labels);
for (size_t label = 0; label < labels.num_labels; label++) {
printf("%lu%s", labels.labels[label], label < labels.num_labels - 1 ? ", " : "");
}
printf("]\n");
if (labels.num_labels > 0) {
qk_control_flow_switch_case_labels_clear(&labels);
}
} else {
printf("%*sLabel width (%lu bits) too large for direct display\n", indent + 2, "",
bit_width);
}
if (qk_control_flow_switch_is_case_default(cf_inst, case_idx)) {
printf("%*sThis is the DEFAULT case\n", indent + 2, "");
}
}
}
void inspect_control_flow_instruction(const QkControlFlowInstruction *cf_inst, int indent) {
QkControlFlowKind cf_type = qk_control_flow_kind(cf_inst);
printf("%*s=== Control Flow: kind - %d ===\n", indent, "", cf_type);
switch (cf_type) {
case QkControlFlowKind_Box:
inspect_box(cf_inst, indent + 2);
break;
case QkControlFlowKind_BreakLoop:
printf("%*sBreak loop instruction\n", indent + 2, "");
break;
case QkControlFlowKind_ContinueLoop:
printf("%*sContinue loop instruction\n", indent + 2, "");
break;
case QkControlFlowKind_ForLoop:
inspect_for_loop(cf_inst, indent + 2);
break;
case QkControlFlowKind_IfElse:
printf("%*sInspecting IfElse instruction\n", indent + 2, "");
inspect_condition(cf_inst, indent + 2);
break;
case QkControlFlowKind_While:
printf("%*sInspecting While instruction\n", indent + 2, "");
inspect_condition(cf_inst, indent + 2);
break;
case QkControlFlowKind_Switch:
inspect_switch(cf_inst, indent + 2);
break;
}
size_t num_blocks = qk_control_flow_num_blocks(cf_inst);
printf("%*sNumber of blocks: %zu\n", indent, "", num_blocks);
for (size_t block = 0; block < num_blocks; block++) {
printf("%*s--- Block %zu ---\n", indent, "", block);
const QkCircuit *block_circuit = qk_control_flow_block_circuit(cf_inst, block);
// Go deeper in the hierarchy
inspect_circuit(block_circuit, cf_inst, indent + 2);
}
}
void inspect_circuit(const QkCircuit *circuit, const QkControlFlowInstruction *parent_cf,
int indent) {
size_t num_instructions = qk_circuit_num_instructions(circuit);
printf("%*sCircuit has %zu instructions\n", indent, "", num_instructions);
for (size_t inst_idx = 0; inst_idx < num_instructions; inst_idx++) {
QkCircuitInstruction inst;
qk_circuit_get_instruction(circuit, inst_idx, &inst);
QkOperationKind kind = qk_circuit_instruction_kind(circuit, inst_idx);
if (kind == QkOperationKind_ControlFlow) {
QkControlFlowInstruction *cf_inst =
qk_circuit_get_control_flow_instruction(circuit, inst_idx, parent_cf);
inspect_control_flow_instruction(cf_inst, indent);
qk_control_flow_instruction_free(cf_inst);
} else {
printf("%*sInstruction %zu: Standard gate/operation\n", indent, "", inst_idx);
// Inspect qubit mapping, if one exists
const uint32_t *qubit_mapping = parent_cf ? qk_control_flow_qubit_map(parent_cf) : NULL;
if (inst.num_qubits > 0) {
printf("%*s Qubits: [", indent, "");
for (uint32_t qubit = 0; qubit < inst.num_qubits; qubit++) {
uint32_t mapped_qubit =
qubit_mapping ? qubit_mapping[inst.qubits[qubit]] : inst.qubits[qubit];
printf("%u%s", mapped_qubit, qubit < inst.num_qubits - 1 ? ", " : "");
}
printf("]\n");
}
// Inspect clbit mapping, if one exists
const uint32_t *clbit_mapping = parent_cf ? qk_control_flow_clbit_map(parent_cf) : NULL;
if (inst.num_clbits > 0) {
printf("%*s Clbits: [", indent, "");
for (uint32_t clbit = 0; clbit < inst.num_clbits; clbit++) {
uint32_t mapped_clbit =
clbit_mapping ? clbit_mapping[inst.clbits[clbit]] : inst.clbits[clbit];
printf("%u%s", mapped_clbit, clbit < inst.num_clbits - 1 ? ", " : "");
}
printf("]\n");
}
}
qk_circuit_instruction_clear(&inst);
}
}Pour plus d'informations sur les fonctions et les types de l'API C, consultez les pages de documentation consacrées au flux de contrôle et aux expressions classiques.