@@ -15,6 +15,7 @@ use qiskit_circuit::imports;
1515use qiskit_circuit:: operations:: Param ;
1616use qiskit_circuit:: parameter:: parameter_expression:: {
1717 OPReplay , OpCode , ParameterExpression , ParameterValueType , PyParameter ,
18+ PyParameterVectorElement ,
1819} ;
1920use qiskit_circuit:: parameter:: symbol_expr:: { Symbol , SymbolExpr , Value } ;
2021use std:: sync:: Arc ;
@@ -224,10 +225,61 @@ fn pack_symbol_table_element(
224225 }
225226}
226227
228+ // In case the parameter expression reduces to a constant value/symbol
229+ // we still want to pack it as an expression to save the symbol table data,
230+ // even though the symbols are not used. So we turn the constant to an equivalent expression.
231+ fn pack_parameter_expression_with_empty_replay (
232+ exp : & ParameterExpression ,
233+ ) -> Result < Vec < formats:: ParameterExpressionElementPack > , QpyError > {
234+ // Try constant value first
235+ if let Ok ( value) = exp. try_to_value ( false ) {
236+ let synthetic_op = OPReplay {
237+ op : OpCode :: ADD ,
238+ lhs : Some ( value. into ( ) ) ,
239+ rhs : Some ( ParameterValueType :: Int ( 0 ) ) ,
240+ } ;
241+ return pack_parameter_expression_element ( & synthetic_op) ;
242+ }
243+
244+ // Check if it's a bare parameter or parameter vector element
245+ if let Ok ( symbol) = exp. try_to_symbol_ref ( ) {
246+ let param_value = match symbol. index {
247+ None => ParameterValueType :: Parameter ( PyParameter {
248+ symbol : symbol. clone ( ) ,
249+ } ) ,
250+ Some ( _) => ParameterValueType :: VectorElement ( PyParameterVectorElement {
251+ symbol : symbol. clone ( ) ,
252+ } ) ,
253+ } ;
254+ let synthetic_op = OPReplay {
255+ op : OpCode :: ADD ,
256+ lhs : Some ( param_value) ,
257+ rhs : Some ( ParameterValueType :: Int ( 0 ) ) ,
258+ } ;
259+ return pack_parameter_expression_element ( & synthetic_op) ;
260+ }
261+ Err ( QpyError :: InvalidParameter ( format ! (
262+ "Cannot encode parameter expression {:?}" ,
263+ exp
264+ ) ) )
265+ }
266+
267+ // fn pack_parameter_expression_with_empty_replay(exp: &ParameterExpression) -> Result<Vec<formats::ParameterExpressionElementPack>, QpyError> {
268+ // if let Ok(value) = exp.try_to_value(false) {
269+ // return Ok(pack_parameter_expression_element(&constant_value_op_replay(value))?;);
270+ // } else {
271+ // return Err(QpyError::InvalidParameter(format!("Cannot encode parameter expression {:?}", exp)));
272+ // }
273+ // }
274+
227275fn pack_parameter_expression_elements (
228276 exp : & ParameterExpression ,
229277) -> Result < Vec < formats:: ParameterExpressionElementPack > , QpyError > {
230- let mut result = Vec :: new ( ) ;
278+ let replay = exp. qpy_replay ( ) ;
279+ if replay. is_empty ( ) {
280+ return pack_parameter_expression_with_empty_replay ( exp) ;
281+ }
282+ let mut result: Vec < formats:: ParameterExpressionElementPack > = Vec :: new ( ) ;
231283 for replay_obj in exp. qpy_replay ( ) . iter ( ) {
232284 let packed_parameter = pack_parameter_expression_element ( replay_obj) ?;
233285 result. extend ( packed_parameter) ;
@@ -470,9 +522,15 @@ pub(crate) fn unpack_parameter_expression(
470522 replay. push ( OPReplay { op, lhs, rhs } ) ;
471523 } ;
472524 }
473- ParameterExpression :: from_qpy ( & replay, Some ( sub_operations) ) . map_err ( |_| {
525+ let mut exp = ParameterExpression :: from_qpy ( & replay, Some ( sub_operations) ) . map_err ( |_| {
474526 QpyError :: ConversionError ( "Failure while loading parameter expression" . to_string ( ) )
475- } )
527+ } ) ?;
528+ // add parameters not present in the replay but part of the original expression (in case they were optimized away)
529+ exp. extend_symbols ( param_uuid_map. values ( ) . filter_map ( |v| match v {
530+ GenericValue :: ParameterExpressionSymbol ( s) => Some ( s. clone ( ) ) ,
531+ _ => None ,
532+ } ) ) ;
533+ Ok ( exp)
476534}
477535
478536pub ( crate ) fn pack_symbol ( symbol : & Symbol ) -> formats:: ParameterSymbolPack {
@@ -585,13 +643,29 @@ pub(crate) fn unpack_parameter_vector(
585643 } )
586644}
587645
646+ // exp should be a symbol (for parameter/parameter vector element)
647+ // but more than that: it should no contain other symbols in its symbol table
648+ // since if, e.g. exp was `0*x+y` and got simplified to `y` we still need
649+ // to store `x`, so we must treat exp as an expression
650+ fn expression_as_single_symbol ( exp : & ParameterExpression ) -> Option < Symbol > {
651+ if let Ok ( symbol) = exp. try_to_symbol ( ) {
652+ if exp. num_of_symbols ( ) == 1 {
653+ Some ( symbol)
654+ } else {
655+ None
656+ }
657+ } else {
658+ None
659+ }
660+ }
661+
588662pub ( crate ) fn pack_param_expression (
589663 exp : & ParameterExpression ,
590664 qpy_data : & QPYWriteData ,
591665) -> Result < formats:: GenericDataPack , QpyError > {
592666 // if the parameter expression is a single symbol, we should treat it like a parameter
593667 // or a parameter vector, depending on whether the `vector` field exists
594- if let Ok ( symbol) = exp . try_to_symbol ( ) {
668+ if let Some ( symbol) = expression_as_single_symbol ( exp ) {
595669 match symbol. vector {
596670 None => pack_generic_value ( & GenericValue :: ParameterExpressionSymbol ( symbol) , qpy_data) ,
597671 Some ( _) => pack_generic_value (
0 commit comments