pulse backend string model parser#71
Conversation
…nts with operators
…nel is not specified in required format. Added tests for this and other others
…some string models
… as its no longer needed
… -> operator conversion
…n of what the function can be used to generate. Added test cases for this function
…checking well-formed strings
brosand
left a comment
There was a problem hiding this comment.
Looking good, mostly just left a couple small suggestions for testing and parsing, and a couple small typos.
|
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.""" |
There was a problem hiding this comment.
should be "test that too few vertical bars"
brosand
left a comment
There was a problem hiding this comment.
LGTM, thanks for doing those small changes
chriseclectic
left a comment
There was a problem hiding this comment.
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
| """ | ||
|
|
||
| from .pulse_to_signals import InstructionToSignals | ||
| from .string_model_parser.string_model_parser import parse_hamiltonian_dict |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
This function should have type hints and doc string to say what is expected for arg types, and the return
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Updated the function to have type hints.
|
|
||
| Token = namedtuple("Token", ("type", "name")) | ||
|
|
||
| str_elements = OrderedDict( |
There was a problem hiding this comment.
If this is only used by HamiltonianParser it should be class variable of that class, no need for global scope. Same for Token
There was a problem hiding this comment.
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 = [] |
There was a problem hiding this comment.
Why double underscore?
There was a problem hiding this comment.
No reason, it was in the original code. I've changed it to just td_hams.
| ) | ||
|
|
||
|
|
||
| class HamiltonianParser: |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Might also want to call this _HamiltonianParser just to add further indication this shouldn't be used and is internal only
There was a problem hiding this comment.
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} |
There was a problem hiding this comment.
What is the point of this?
| __funcdict = {"dag": dag} | ||
|
|
||
|
|
||
| def apply_func(name: str, op: np.ndarray) -> np.ndarray: |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
No need for this function, it could just be system = HarmiltonianParser(args).parse(other_args)
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
| def hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict): | |
| def _hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict): |
|
@chriseclectic I think I've addressed most comments with the latest changes. The remaining things to discuss are:
|
…ynamics into pulse_string_parser
|
Closing this PR as it is extremely out of date - will reopen a new one. |
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