@@ -23,7 +23,7 @@ use num_complex::{Complex64, ComplexFloat};
2323
2424use qiskit_circuit:: bit:: { ClassicalRegister , QuantumRegister } ;
2525use qiskit_circuit:: bit:: { ShareableClbit , ShareableQubit } ;
26- use qiskit_circuit:: circuit_data:: CircuitData ;
26+ use qiskit_circuit:: circuit_data:: { CircuitData , CircuitDataError } ;
2727use qiskit_circuit:: dag_circuit:: DAGCircuit ;
2828use qiskit_circuit:: instruction:: Parameters ;
2929use qiskit_circuit:: interner:: Interner ;
@@ -32,6 +32,7 @@ use qiskit_circuit::operations::{
3232 PauliProductRotation , StandardGate , StandardInstruction , UnitaryGate ,
3333} ;
3434use qiskit_circuit:: packed_instruction:: { PackedInstruction , PackedOperation } ;
35+ use qiskit_circuit:: parameter_table:: ParameterTableError ;
3536use qiskit_circuit:: { BlocksMode , Clbit , Qubit , VarsMode } ;
3637use smallvec:: smallvec;
3738
@@ -339,6 +340,46 @@ pub unsafe extern "C" fn qk_circuit_num_clbits(circuit: *const CircuitData) -> u
339340 circuit. num_clbits ( ) as u32
340341}
341342
343+ /// @ingroup QkCircuit
344+ /// Get the number of unbound symbols in ``QkParam`` objects in the circuit.
345+ ///
346+ /// @param circuit A pointer to the circuit.
347+ ///
348+ /// @return The number of unbound ``QkParam`` symbols in the circuit.
349+ ///
350+ /// # Example
351+ ///
352+ /// ```c
353+ /// QkCircuit *qc = qk_circuit_new(2, 0);
354+ /// QkParam *x = qk_param_new_symbol("x");
355+ /// QkParam *y = qk_param_new_symbol("y");
356+ ///
357+ /// uint32_t q0[1] = {0};
358+ /// const QkParam *rx_param[1] = {x};
359+ /// const QkParam *ry_param[1] = {y};
360+ ///
361+ /// qk_circuit_parameterized_gate(qc, QkGate_RX, q0, rx_param);
362+ /// qk_circuit_parameterized_gate(qc, QkGate_RY, q0, ry_param);
363+ ///
364+ /// // check the number of QkParam symbols
365+ /// size_t num_symbols = qk_circuit_num_param_symbols(qc); // == 2
366+ ///
367+ /// qk_param_free(x);
368+ /// qk_param_free(y);
369+ /// qk_circuit_free(qc);
370+ /// ```
371+ ///
372+ /// # Safety
373+ ///
374+ /// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``.
375+ #[ unsafe( no_mangle) ]
376+ pub unsafe extern "C" fn qk_circuit_num_param_symbols ( circuit : * const CircuitData ) -> usize {
377+ // SAFETY: Per documentation, the pointer is non-null and aligned.
378+ let circuit = unsafe { const_ptr_as_ref ( circuit) } ;
379+
380+ circuit. num_parameters ( )
381+ }
382+
342383/// @ingroup QkCircuit
343384/// Free the circuit.
344385///
@@ -457,6 +498,95 @@ pub unsafe extern "C" fn qk_circuit_gate(
457498 ExitCode :: Success
458499}
459500
501+ /// @ingroup QkCircuit
502+ /// Append a ``QkGate`` with ``QkParam*`` parameters to the circuit.
503+ ///
504+ /// @param circuit A pointer to the circuit to add the gate to.
505+ /// @param gate The ``QkGate`` to add to the circuit.
506+ /// @param qubits The pointer to the array of ``uint32_t`` qubit indices to add the gate on. This
507+ /// can be a null pointer if there are no qubits for ``gate`` (e.g. ``QkGate_GlobalPhase``).
508+ /// @param params The pointer to the array of ``QkParam*`` parameters to use for the gate parameters.
509+ /// This can be a null pointer if there are no parameters for ``gate`` (e.g. ``QkGate_H``).
510+ ///
511+ /// @return ``QkExitCode_Success`` upon successful append. Upon failure,
512+ /// ``QkExitCode_ParameterNameConflict`` indicates that a new parameter symbol has a name
513+ /// conflict with an existing one. ``QkExitCode_ParameterError`` describes other generic
514+ /// failures when attempting to track the parameter symbols.
515+ ///
516+ /// # Example
517+ ///
518+ /// ```c
519+ /// QkCircuit *qc = qk_circuit_new(100, 0);
520+ /// QkParam *theta = qk_param_new_symbol("theta");
521+ /// uint32_t qubit[1] = {0};
522+ /// const QkParam* params[1] = {theta};
523+ /// qk_circuit_parameterized_gate(qc, QkGate_RX, qubit, params); // add RX(theta) to the circuit
524+ /// ```
525+ ///
526+ /// # Safety
527+ ///
528+ /// The ``qubits`` and ``params`` types are expected to be a pointer to an array of ``uint32_t``
529+ /// and ``QkParam*`` respectively where the length is matching the expectations for the standard
530+ /// gate. If the array is insufficiently long the behavior of this function is undefined as this
531+ /// will read outside the bounds of the array. It can be a null pointer if there are no qubits
532+ /// or params for a given gate. You can check ``qk_gate_num_qubits`` and ``qk_gate_num_params`` to
533+ /// determine how many qubits and params are required for a given gate.
534+ ///
535+ /// Behavior is undefined if ``circuit`` is not a valid, non-null pointer to a ``QkCircuit``,
536+ /// or if any of the elements in the ``params`` array is not a valid, non-null pointer to a
537+ /// ``QkParam``.
538+ #[ unsafe( no_mangle) ]
539+ pub unsafe extern "C" fn qk_circuit_parameterized_gate (
540+ circuit : * mut CircuitData ,
541+ gate : StandardGate ,
542+ qubits : * const u32 ,
543+ params : * const * const Param ,
544+ ) -> ExitCode {
545+ // SAFETY: Per documentation, the pointer is non-null and aligned.
546+ let circuit = unsafe { mut_ptr_as_ref ( circuit) } ;
547+
548+ // SAFETY: Per the documentation the qubits pointer is an array of num_qubits() elements.
549+ let qargs: & [ Qubit ] = unsafe {
550+ match gate. num_qubits ( ) {
551+ 0 => & [ ] ,
552+ 1 => & [ Qubit ( * qubits. wrapping_add ( 0 ) ) ] ,
553+ 2 => & [
554+ Qubit ( * qubits. wrapping_add ( 0 ) ) ,
555+ Qubit ( * qubits. wrapping_add ( 1 ) ) ,
556+ ] ,
557+ 3 => & [
558+ Qubit ( * qubits. wrapping_add ( 0 ) ) ,
559+ Qubit ( * qubits. wrapping_add ( 1 ) ) ,
560+ Qubit ( * qubits. wrapping_add ( 2 ) ) ,
561+ ] ,
562+ 4 => & [
563+ Qubit ( * qubits. wrapping_add ( 0 ) ) ,
564+ Qubit ( * qubits. wrapping_add ( 1 ) ) ,
565+ Qubit ( * qubits. wrapping_add ( 2 ) ) ,
566+ Qubit ( * qubits. wrapping_add ( 3 ) ) ,
567+ ] ,
568+ // There are no ``QkGate``s > 4 qubits
569+ _ => unreachable ! ( ) ,
570+ }
571+ } ;
572+
573+ // SAFETY: Per documentation, the params pointer is an array of num_params() elements, and
574+ // each element is a valid, non-null pointer to a Param.
575+ let params: Vec < Param > =
576+ unsafe { :: std:: slice:: from_raw_parts ( params, gate. num_params ( ) as usize ) }
577+ . iter ( )
578+ . map ( |& ptr| unsafe { const_ptr_as_ref ( ptr) } . clone ( ) )
579+ . collect ( ) ;
580+
581+ match circuit. push_standard_gate ( gate, & params, qargs) {
582+ Ok ( ( ) ) => ExitCode :: Success ,
583+ Err ( CircuitDataError :: ParameterTableError ( ParameterTableError :: NameConflict ( _) ) ) => {
584+ ExitCode :: ParameterNameConflict
585+ }
586+ Err ( _) => ExitCode :: ParameterError ,
587+ }
588+ }
589+
460590/// @ingroup QkCircuit
461591/// Get the number of qubits for a ``QkGate``.
462592///
@@ -941,7 +1071,7 @@ pub struct CInstruction {
9411071 /// A pointer to an array of clbit indices this instruction operates on.
9421072 clbits : * mut u32 ,
9431073 /// A pointer to an array of parameter values for this instruction.
944- params : * mut f64 ,
1074+ params : * mut * mut Param ,
9451075 /// The number of qubits for this instruction.
9461076 num_qubits : u32 ,
9471077 /// The number of clbits for this instruction.
@@ -956,8 +1086,9 @@ impl CInstruction {
9561086 /// This must be cleared by a call to `qk_circuit_instruction_clear` to avoid leaking its
9571087 /// allocations.
9581088 ///
959- /// Panics if the operation name contains a nul, or if the instruction has non-float parameters.
960- pub ( crate ) fn from_packed_instruction_with_floats (
1089+ /// Panics if the operation name contains a nul, or if the instruction has non-numeric
1090+ /// parameters (not [Param::Float] or [Param::ParameterExpression]).
1091+ pub ( crate ) fn from_packed_instruction_with_numeric (
9611092 packed : & PackedInstruction ,
9621093 qargs_interner : & Interner < [ Qubit ] > ,
9631094 cargs_interner : & Interner < [ Clbit ] > ,
@@ -971,11 +1102,13 @@ impl CInstruction {
9711102 . params_view ( )
9721103 . iter ( )
9731104 . map ( |p| match p {
974- Param :: Float ( p) => Some ( * p) ,
1105+ Param :: Float ( _) | Param :: ParameterExpression ( _) => {
1106+ Some ( Box :: into_raw ( Box :: new ( p. clone ( ) ) ) )
1107+ }
9751108 _ => None ,
9761109 } )
977- . collect :: < Option < Box < [ f64 ] > > > ( )
978- . expect ( "caller is responsible for ensuring all parameters are floats " ) ;
1110+ . collect :: < Option < Box < [ * mut Param ] > > > ( )
1111+ . expect ( "caller is responsible for ensuring all parameters are numeric " ) ;
9791112 Self {
9801113 name,
9811114 num_qubits : qargs. len ( ) as u32 ,
@@ -1027,7 +1160,7 @@ pub unsafe extern "C" fn qk_circuit_get_instruction(
10271160) {
10281161 // SAFETY: Per documentation, `circuit` is a pointer to valid data.
10291162 let circuit = unsafe { const_ptr_as_ref ( circuit) } ;
1030- let inst = CInstruction :: from_packed_instruction_with_floats (
1163+ let inst = CInstruction :: from_packed_instruction_with_numeric (
10311164 & circuit. data ( ) [ index] ,
10321165 circuit. qargs_interner ( ) ,
10331166 circuit. cargs_interner ( ) ,
@@ -1325,7 +1458,10 @@ pub unsafe extern "C" fn qk_circuit_instruction_clear(inst: *mut CInstruction) {
13251458 inst. num_clbits = 0 ;
13261459 if inst. num_params > 0 && !inst. params . is_null ( ) {
13271460 let params = std:: slice:: from_raw_parts_mut ( inst. params , inst. num_params as usize ) ;
1328- let _ = Box :: from_raw ( params as * mut [ f64 ] ) ;
1461+ for param in params. iter ( ) {
1462+ let _ = Box :: from_raw ( * param) ;
1463+ }
1464+ let _ = Box :: from_raw ( params) ;
13291465 inst. params = std:: ptr:: null_mut ( ) ;
13301466 }
13311467 inst. num_params = 0 ;
0 commit comments