Skip to content

Conversation

@yaelbh
Copy link
Collaborator

@yaelbh yaelbh commented Dec 11, 2025

Part of #2479.

While testing NoiseLearnerV3ResultDecoder.decode, I think I saw a bug. I'm not sure about the origin of the input raw_string, but I think it's a json string, in which case the post selection part of it looks like this:

{"fraction_kept":{"0":1.0,"4":1.0}}

The dictionary keys 0 and 4 were originally integers, but with json keys are always strings. One needs the model in order to know that these are integers and therefore decode them as such.

To fix the bug, I modified NoiseLearnerV3ResultDecoder.decode to be more similar to the decoder of the executor, which addresses the above concern.

In addition, noise_learner_v3_result_from_0_1 was implemented as if model was a dictionary, although it's a NoiseLearnerV3ResultsModel, I fixed it.

This PR is still in progress because several tests are still missing.

@yaelbh yaelbh added the executor-preview Issues related to the `executor_preview` branch label Dec 11, 2025
@yaelbh yaelbh changed the base branch from main to executor_preview December 11, 2025 11:46
Comment on lines 63 to 70
def test_valid_model(self):
"""Tests the decoder."""
decoded = NoiseLearnerV3ResultDecoder.decode(self.encoded.model_dump_json())
for datum_in, datum_out in zip(self.results.data, decoded.data):
assert datum_in._generators == datum_out._generators
assert np.allclose(datum_in._rates, datum_out._rates)
assert np.allclose(datum_in._rates_std, datum_out._rates_std)
assert datum_in.metadata == datum_out.metadata
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way to avoid accessing private attributes here is to implement an __eq__ method, which will be useful per se' and testable

def decode(cls, raw_result: str) -> NoiseLearnerV3Results: # type: ignore[no-untyped-def]
"""Decode raw json to result type."""
decoded: Dict = super().decode(raw_result)
decoded = json.loads(raw_result)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to revert this change?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes please

Copy link
Collaborator

@SamFerracin SamFerracin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just revert that json.loads and it's good to go in

Would you mind testing one more time before merging

  • that new jobs go through
  • that old jobs (e.g. jobs that you run on the executor_preview branch) can be loaded
    ?
    Better safe than sorry

@yaelbh
Copy link
Collaborator Author

yaelbh commented Jan 8, 2026

Would you mind testing one more time before merging

I ran NLV3 with the decoder branch (this PR), printed the results (generators, rates, metadata), and saved.
I loaded with the decoder branch, and verified that all the results are identical to those that were saved.

Then:
I ran NLV3 with the executor-preview branch, printed the results and saved them.
I loaded with the decoder branch, again it's all ok.

@yaelbh yaelbh requested a review from SamFerracin January 8, 2026 10:27
Copy link
Collaborator

@SamFerracin SamFerracin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Could you please

  1. Fix the conflict
  2. Confirm that new jobs work correctly, and we can load old jobs (which I think you did already)

After that, I am happy to approve :)

Copy link
Collaborator

@SamFerracin SamFerracin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another one of those PRs that I would test e2e by running a job. If that job passes, feel free to merge 😄

def decode(cls, raw_result: str) -> NoiseLearnerV3Results: # type: ignore[no-untyped-def]
"""Decode raw json to result type."""
decoded: dict[str, str] = super().decode(raw_result)
decoded: dict[str, Any] = super().decode(raw_result)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmmh are you sure of this change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is decoded:

{'schema_version': 'v0.1', 'data': [{'generators_sparse': [[['X', [0]]], [['Y', [0]]], [['Z', [0]]], [['X', [1]]], [['XX', [0, 1]]], [['YX', [0, 1]]], [['ZX', [0, 1]]], [['Y', [1]]], [['XY', [0, 1]]], [['YY', [0, 1]]], [['ZY', [0, 1]]], [['Z', [1]]], [['XZ', [0, 1]]], [['YZ', [0, 1]]], [['ZZ', [0, 1]]]], 'num_qubits': 2, 'rates': {'data': 'JCh+jLlr4z9N27+y0qT8PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE82jhiLec/AAAAAAAAAAAAAAAAAAAAACE82jhiLec/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCh+jLlr4z9N27+y0qT8PwAAAAAAAAAA', 'shape': [15], 'dtype': 'f64'}, 'rates_std': {'data': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'shape': [15], 'dtype': 'f64'}, 'metadata': {'learning_protocol': 'lindblad', 'post_selection': {'fraction_kept': {'0': 0.6299099392361112, '1': 0.6307237413194444, '2': 0.6239149305555556, '4': 0.6247829861111112, '16': 0.6248372395833334, '32': 0.626708984375}}}}, {'generators_sparse': [[['X', [0]]], [['Y', [0]]], [['Z', [0]]], [['X', [1]]], [['XX', [0, 1]]], [['YX', [0, 1]]], [['ZX', [0, 1]]], [['Y', [1]]], [['XY', [0, 1]]], [['YY', [0, 1]]], [['ZY', [0, 1]]], [['Z', [1]]], [['XZ', [0, 1]]], [['YZ', [0, 1]]], [['ZZ', [0, 1]]]], 'num_qubits': 2, 'rates': {'data': 'AAAAAAAAAABftTLhl/qpPwAAAAAAAAAAMc7fhEJEAUADPj+MEB71PwAAAAAAAAAAMc7fhEJEAUAAAAAAAAAAAAAAAAAAAAAAAz4/jBAe9T8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABftTLhl/qpPwAAAAAAAAAA', 'shape': [15], 'dtype': 'f64'}, 'rates_std': {'data': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'shape': [15], 'dtype': 'f64'}, 'metadata': {'learning_protocol': 'lindblad', 'post_selection': {'fraction_kept': {'0': 0.6240776909722222, '1': 0.6219618055555556, '2': 0.6254340277777778, '4': 0.6245659722222222, '16': 0.6256510416666666, '32': 0.6236979166666666}}}}]}

Output of

print(type(decoded["data"]))

is:

<class 'list'>

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking!

@yaelbh yaelbh merged commit 576e13f into Qiskit:executor_preview Jan 13, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

executor-preview Issues related to the `executor_preview` branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants