Perf multiple circuit run for one job - #197
Conversation
|
transform jobs to single job but with multiple circuits |
…d execution Co-authored-by: Copilot <copilot@github.com>
- Updated Job class to accept both QCircuit and CircuitBinding types for circuits. - Enhanced compute_expectation_value to support CircuitBinding and handle multiple contexts. - Modified run_ibm to streamline job execution for both single and multiple jobs. - Adjusted measure handling in the runner to accommodate CircuitBinding. - Added comprehensive tests for CircuitBinding in various modes (PRODUCT, ZIP, recursive). - Improved error handling and validation for job compatibility with devices. - Refactored run_aer to support CircuitBinding and ensure proper execution flow.
…d improve broadcasting functionality
…mpqp into perf-multiple-circuit-run
…ages in PauliStringMonomial
…and improve measurement handling
Braket program set
| """Returns if the matrix is already set in the observable.""" | ||
| return not self._matrix is None | ||
|
|
||
| def is_pauli(self): |
There was a problem hiding this comment.
I would rename the function is_pauli or is_matrix which can be misleading. Maybe is_matrix_set / is_pauli_string_set can do the job?
| from qiskit.quantum_info import SparsePauliOp | ||
| from sympy import Basic, Expr | ||
|
|
||
| Coef = Union[Real, float, Expr, Basic] |
There was a problem hiding this comment.
Maybe move this to mpqp.tools.generics ?
Or at least not put it in TYPE_CHECKING so it can be used in isinstance
| from sympy import Expr | ||
|
|
||
| assert isinstance( | ||
| other, (int, float, complex, Expr) |
There was a problem hiding this comment.
We can't directly use Coef here? I remember we already had this discussion/issue for Matrix, but in principle if Coef is also a Union of types, it should be able to check it
| @property | ||
| def job_type(self) -> "JobType": | ||
| """ | ||
| Returns the type of job associated with the circuit based on its measurements. | ||
| """ | ||
| from mpqp.execution.job import JobType | ||
|
|
||
| for measurement in self.measurements: | ||
| if isinstance(measurement, BasisMeasure): | ||
| if measurement.shots <= 0: | ||
| return JobType.STATE_VECTOR | ||
| else: | ||
| return JobType.SAMPLE | ||
| elif isinstance(measurement, ExpectationMeasure): | ||
| return JobType.OBSERVABLE | ||
| return JobType.STATE_VECTOR |
There was a problem hiding this comment.
Note for me: double check that this is used in generate_jobs when we call run, to no do the same job twice
| class BindingMode(Enum): | ||
| PRODUCT = auto() | ||
| ZIP = auto() | ||
|
|
||
|
|
||
| class CircuitBinding: | ||
| def __init__( |
There was a problem hiding this comment.
I would have put it in another file than circuit.py, if it doesn't imply circular dependencies
| values: Optional[OneOrMany[dict[Expr | str, Complex | float]]] = None, | ||
| measurements: Optional[OneOrMany[Measure]] = None, | ||
| mode: BindingMode = BindingMode.PRODUCT, | ||
| noises: Optional[list[NoiseModel]] = None, |
There was a problem hiding this comment.
This also be combined with product/zip (different noise models for different circuits/values/measurements?) ? If not then maybe just use the one attached to the QCircuit, I don't see why would we need to put it as a separate parameter
| measurements: Optional[OneOrMany[Measure]] = None, | ||
| mode: BindingMode = BindingMode.PRODUCT, | ||
| noises: Optional[list[NoiseModel]] = None, | ||
| shots: Optional[int] = None, |
There was a problem hiding this comment.
Same remark for the shots, in principle they should be already informed in the Measure.
| ZIP = auto() | ||
|
|
||
|
|
||
| class CircuitBinding: |
There was a problem hiding this comment.
Documentation, with examples
| return params | ||
|
|
||
|
|
||
| class BindingMode(Enum): |
| Optional["Measure"], | ||
| ] | ||
| ]: | ||
| """Resolves the lazy execution graph and returns a flat list of |
There was a problem hiding this comment.
Better documentation
| def merge_vals(v_base, v_curr): | ||
| if v_base is None and v_curr is None: | ||
| return None | ||
| merged = dict(v_base) if v_base is not None else {} | ||
| if v_curr is not None: | ||
| merged.update(v_curr) | ||
| return merged |
There was a problem hiding this comment.
could be moved to tools? why defined inside the method ?
| ) -> "CircuitBinding": ... | ||
| def to_other_device( | ||
| self, device: AvailableDevice, programSet: bool = True | ||
| ) -> "CircuitBinding | tuple[ProgramSet, list[tuple[Any]]] | list[tuple[EstimatorPubLike, Job]]": |
There was a problem hiding this comment.
Maybe use same organization as we recently did. Move this to mpqp.translation and call it from here ?
| for j in range(len(grouping)): | ||
| result = task[index][0] | ||
| for name, eigenvalue in eigenvalues.items(): | ||
| for i in range(length): | ||
| binary_state = f"{bin(i)[2:].zfill(len(bin(length))- 3)}" | ||
| if binary_state in result.probabilities: | ||
| sorted_values[i] = result.probabilities[ | ||
| binary_state | ||
| ].real | ||
| else: | ||
| sorted_values[i] = 0 |
There was a problem hiding this comment.
All the grouping thing I would create another method to handle that, for circuit biding or not circuit biding, and even I would say braket or not braket, because it is a general process that can be reused, right ?
Otherwise we will have as much implementations of the pauli grouping as providers and as ways to run circuits with observables (run normal, run binded, etc)
To be discussed
| """Optional message associated with the current job status, especially | ||
| for execution errors.""" | ||
|
|
||
| """Store the measurement of a circuit.""" |
There was a problem hiding this comment.
I think this is more used for circuit biding right ?
| """Store the measurement of a circuit.""" | ||
| self.measurement = measurement | ||
|
|
||
| """Store parameters in case the original circuit was parametrized.""" |
There was a problem hiding this comment.
Or maybe say defined by symbolic parameters
| if isinstance(self.circuit, CircuitBinding): | ||
| return None |
There was a problem hiding this comment.
here you would return self.measurement in the case of a CircutBiding no ?
No description provided.