Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.

pulse backend string model parser#71

Closed
DanPuzzuoli wants to merge 44 commits into
qiskit-community:mainfrom
DanPuzzuoli:pulse_string_parser
Closed

pulse backend string model parser#71
DanPuzzuoli wants to merge 44 commits into
qiskit-community:mainfrom
DanPuzzuoli:pulse_string_parser

Conversation

@DanPuzzuoli
Copy link
Copy Markdown
Collaborator

Summary

Closes #53.

Bring string model parser from Aer into dynamics. Currently a work in progress as I attempt to reduce the original number of files and lines of code involved.

Details and comments

@DanPuzzuoli DanPuzzuoli added the Changelog: New Feature Include in the "Added" section of the changelog label Dec 11, 2021
@DanPuzzuoli DanPuzzuoli marked this pull request as ready for review January 4, 2022 21:41
@DanPuzzuoli DanPuzzuoli changed the title [WIP] String model parser pulse backend string model parser Jan 4, 2022
@brosand brosand self-requested a review February 1, 2022 19:53
Copy link
Copy Markdown
Contributor

@brosand brosand left a comment

Choose a reason for hiding this comment

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

Looking good, mostly just left a couple small suggestions for testing and parsing, and a couple small typos.

Comment thread qiskit_dynamics/pulse/string_model_parser/operator_from_string.py
Comment thread qiskit_dynamics/pulse/string_model_parser/operator_from_string.py Outdated
Comment thread qiskit_dynamics/pulse/string_model_parser/operator_from_string.py Outdated
Comment thread qiskit_dynamics/pulse/string_model_parser/string_model_parser.py Outdated
Comment thread qiskit_dynamics/pulse/string_model_parser/string_model_parser.py Outdated
Comment thread qiskit_dynamics/pulse/string_model_parser/string_model_parser.py Outdated
Comment thread qiskit_dynamics/pulse/string_model_parser/string_model_parser.py
Comment thread test/dynamics/pulse/test_string_model_parser.py
Comment thread test/dynamics/pulse/test_string_model_parser.py
Comment thread qiskit_dynamics/pulse/string_model_parser/string_model_parser.py
@DanPuzzuoli
Copy link
Copy Markdown
Collaborator Author

Not sure why I can't reply to the one comment about adding the output to one of the examples in the docs.

I'm kind of on the fence about this - two of the outputs of the function consist of arrays and so showing them isn't necessarily too informative. Given that this function is primarily for internal use anyway I'm leaning towards not adding this.

@DanPuzzuoli DanPuzzuoli requested a review from brosand February 8, 2022 21:42
@brosand
Copy link
Copy Markdown
Contributor

brosand commented Feb 8, 2022

Not sure why I can't reply to the one comment about adding the output to one of the examples in the docs.

I'm kind of on the fence about this - two of the outputs of the function consist of arrays and so showing them isn't necessarily too informative. Given that this function is primarily for internal use anyway I'm leaning towards not adding this.

Not sure why I can't reply to this one either haha, but this makes sense,

self.assertTrue("does not conform" in str(qe.exception))

def test_single_vertical_bars(self):
"""Test that too many vertical bars raises error."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should be "test that too few vertical bars"

@DanPuzzuoli DanPuzzuoli requested a review from brosand February 9, 2022 13:54
brosand
brosand previously approved these changes Feb 9, 2022
Copy link
Copy Markdown
Contributor

@brosand brosand left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for doing those small changes

Copy link
Copy Markdown
Collaborator

@chriseclectic chriseclectic left a comment

Choose a reason for hiding this comment

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

Since majority of this is intended as internal code hidden from users i would recommend adding some comments to that effect in the module docstrings, and renaming most of the classes and functions to start with an underscore to further indicate they are internal methods that should not be used directly

Comment thread qiskit_dynamics/pulse/__init__.py Outdated
"""

from .pulse_to_signals import InstructionToSignals
from .string_model_parser.string_model_parser import parse_hamiltonian_dict
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this function supposed to be directly called by a user? I thought it was more an internal function that is planned to be used by some yet-to-be-made interface for building models from ibm backend configs?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You're right, I'll remove this.

from .operator_from_string import operator_from_string, apply_func


def legacy_parser(operator_str, subsystem_dims, subsystem_list):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this called legacy_parser? To me legacy implies there is a newer version that that this is planned to be removed at some point

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function should have type hints and doc string to say what is expected for arg types, and the return

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm using "legacy" here to imply that it's a non-trivial piece of code that is being preserved but not "maintained" per se. I like the usage of "legacy" to imply this - I think the way you're interpreting it is basically synonymous with "expected to be deprecated".

That all being said I'll change this if it's a sticking point for you.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated the function to have type hints.


Token = namedtuple("Token", ("type", "name"))

str_elements = OrderedDict(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If this is only used by HamiltonianParser it should be class variable of that class, no need for global scope. Same for Token

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Moved this into the class.

"""
self.h_str = h_str
self.subsystem_dims = {int(label): int(dim) for label, dim in subsystem_dims.items()}
self.__td_hams = []
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why double underscore?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No reason, it was in the original code. I've changed it to just td_hams.

)


class HamiltonianParser:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is there any need for this to be a class instead of a giant function? If the only way you use it is via a legacy_parser call you could just merge the init + parse + compiled into a single function since there is no reason to save state in an objects variables, all those can just be local variables in the functions scope

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Might also want to call this _HamiltonianParser just to add further indication this shouldn't be used and is internal only

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No reason - this is part of the "legacy" aspect of it and why I wrote the legacy_parser function. Turning it directly into a function was non-trivial given the handling of internal state. The legacy_parser function was added to wrap this in the preferred standard function interface.



# pylint: disable=invalid-name
__funcdict = {"dag": dag}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What is the point of this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Removed.

__funcdict = {"dag": dag}


def apply_func(name: str, op: np.ndarray) -> np.ndarray:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function seems sus. You could just replace it in the parser code (which is the only spot it looks to be used) with something like

elif token.type in ["Func", "Ext"]:
     if token.name == "dag":
         stack.append(np.conjugate(np.transpose(stack.pop(-1)))
     else:
          stack.append(stack.pop(-1))

(possibly something even simpler if you know whats going on in that code block)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah you're right - I think maybe it was like this in the original pulse simulator to enable further expansion of functionality later, but for now it's pointless. I've implemented your suggestion and removed apply_func and related things, though changed the else statement to raise an error.

subsystem_dims = {int(qubit): qub_dict[int(qubit)] for qubit in subsystem_list}

# Parse the Hamiltonian
system = legacy_parser(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No need for this function, it could just be system = HarmiltonianParser(args).parse(other_args)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Unless you think otherwise I'd rather keep the legacy_parser function even though this is all it does as its the preferred interface (a function call).

return np.diag(np.sqrt(np.arange(1, dim, dtype=complex)), 1)


def adag(dim: int) -> np.ndarray:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same with all these functions, could call them _a, _X etc

return static_hamiltonian, list(hamiltonian_operators), list(reduced_channels), subsystem_dims


def hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
def hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict):
def _hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict):

@DanPuzzuoli
Copy link
Copy Markdown
Collaborator Author

@chriseclectic I think I've addressed most comments with the latest changes. The remaining things to discuss are:

  • Usage of the word "legacy". Maybe we can change the file to regex_parser.py and this function to regex_parser - this will drop the problematic terminology and may be a more informative name (indicating this is where the true "parsing" happens).
  • Using underscores for functions/classes that are meant to be internal: I'd prefer not to do this as I feel like it is unnecessary and I don't really like the way it looks. I think the fact that these things aren't being imported into the standard packaging structure/API, along with the warnings in the files, should be sufficient to imply that they are internal?

@DanPuzzuoli
Copy link
Copy Markdown
Collaborator Author

Closing this PR as it is extremely out of date - will reopen a new one.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Changelog: New Feature Include in the "Added" section of the changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model string representation parsing functionality from qiskit aer

3 participants