Circuitos dinâmicos
Os circuitos dinâmicos permitem medir qubits durante a execução do circuito e utilizar os resultados das medições para controlar as operações subsequentes. O Qiskit oferece amplo suporte para a construção e a análise de circuitos dinâmicos. Para obter mais detalhes, consulte o guia sobre alimentação direta clássica e fluxo de controle.
Atualmente, a API C do Qiskit suporta apenas a inspeção de instruções de fluxo de controle e expressões clássicas. O suporte à construção de expressões clássicas e à adição de instruções de fluxo de controle será incluído em futuras versões do Qiskit.
Ao trabalhar com essa API, tenha em mente os seguintes pressupostos e limitações:
- A maioria dos objetos retornados pela API de fluxo de controle e pela API de expressões clássicas são ponteiros emprestados somente para leitura (retornados como
const *). Elas permanecem válidas apenas enquanto o objeto pai — por exemplo, o circuito ao qual pertence uma instruçãoIfElse— estiver ativo. Portanto, os chamadores não devem liberar ponteiros emprestados e devem garantir que os objetos pais permaneçam em memória por mais tempo do que qualquer uso desses ponteiros. - Esta API não utiliza códigos de erro. Quando chamadas corretamente, as funções são infalíveis. No entanto, funções específicas para variantes (por exemplo,
qk_control_flow_box_duration_kind()que espera uma instruçãoBox) entrará em pânico e abortará o processo ao ser usado com um objeto do tipo incorreto. Para evitar isso, é fornecido um conjunto de funções de consulta para verificar o tipo ou a classe de um objeto antes de chamar a função específica da variante apropriada. - O Qiskit utiliza inteiros grandes para representar algumas construções de fluxo de controle e expressões clássicas, como valores clássicos de condições de registros e rótulos de switch case. No futuro, será adicionado suporte completo a inteiros grandes à API C. Até lá, os valores numéricos nesta API estão limitados ao que cabe em um
uint64_t.
O programa de exemplo a seguir demonstra todas as funções e tipos da API para a análise de instruções de fluxo de controle e expressões clássicas, juntamente com algumas funções clássicas de consulta de registros. O ponto de entrada fica inspect_circuit na parte inferior; ele chama as funções auxiliares definidas acima dele.
// 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);
}
}Consulte as páginas de documentação “Fluxo de controle” e “Expressões clássicas” para obter mais informações sobre as funções e os tipos da API C.