Kernel code simulation #7

Open
opened 2021-03-10 08:39:10 +08:00 by lriesebos · 7 comments
Collaborator

We have developed a functional simulator for ARTIQ which can be found here https://gitlab.com/duke-artiq/dax . In this simulator, I leverage the fact that the ARTIQ kernels are a subset of Python. Obviously there are limitations in this type of simulator, but in practice we can work very well with it. We use simulation on a daily basis for testing and debugging.

Here I will list some notes regarding the ability to simulate the new kernel language in the same way as we do now.

Deep parallel

In this issue https://github.com/m-labs/artiq/issues/1555 there has been discussion about the semantics of the parallel statement. I am for deep parallel semantics because I think it makes the parallel statement more useful. But besides that, shallow parallel can not be simulated in the way we do and our current simulator can only simulate deep parallel semantics. For me that would be a potential other argument in favor of deep parallel semantics.

To deal with this obvious difference between ARTIQ and our simulator, we have developed flake8-artiq, which will flag code that behaves differently for deep and shallow parallel semantics.

Usage of global

Edit: I guess that #5 is giving some clarity on this topic.

Edit 2: Fully clarified in #8.

In the Referencing Python variables section, you mention the usage of the global keyword for host variables. I am a bit confused there. Do I need to use global if I want to refer to a (kernel invariant) attribute of self, or only for Python global variables?

Besides that we do not mind if Python global variables are always kernel invariant.

Type guards

Edit: Agreed upon in #8.

You propose using type() for type guards, and I expect this to simulate well. Though it might be challenging to distinct int/int32/int64 during simulation if an int constant defaults to type int32. I am not sure how to deal with that yet, but one option could be to allow type guards with else: raise TypeError() to not implicitly fallback on the last type in the type guard.

As mentioned in #6 , I am wondering how type guards will be used to differentiate list types. I wanted to note that syntax such as type(arg) == list[int32] can not be simulated correctly as this will only test the type of the container, not if the elements in the list are of type int32.

Dynamic dispatch

You discuss dynamic dispatch, but I think the use of virtual[] will be a problem during simulation as this is not an existing Python object. Usage of TypeVar('T', bound=Base), as already used by the Python typing lib, could maybe be an alternative.

Dynamic dispatch is not something we highly desire at this moment, but if it is available we will probably use it.

We have developed a functional simulator for ARTIQ which can be found here https://gitlab.com/duke-artiq/dax . In this simulator, I leverage the fact that the ARTIQ kernels are a subset of Python. Obviously there are limitations in this type of simulator, but in practice we can work very well with it. We use simulation on a daily basis for testing and debugging. Here I will list some notes regarding the ability to simulate the new kernel language in the same way as we do now. # Deep parallel In this issue https://github.com/m-labs/artiq/issues/1555 there has been discussion about the semantics of the `parallel` statement. I am for deep parallel semantics because I think it makes the parallel statement more useful. But besides that, shallow parallel can not be simulated in the way we do and our current simulator can only simulate deep parallel semantics. For me that would be a potential other argument in favor of deep parallel semantics. To deal with this obvious difference between ARTIQ and our simulator, we have developed [flake8-artiq](https://gitlab.com/duke-artiq/flake8-artiq), which will flag code that behaves differently for deep and shallow parallel semantics. # Usage of global *Edit: I guess that #5 is giving some clarity on this topic.* *Edit 2: Fully clarified in #8.* ~~In the [Referencing Python variables](https://git.m-labs.hk/M-Labs/nac3-spec#referencing-python-variables) section, you mention the usage of the `global` keyword for host variables. I am a bit confused there. Do I need to use `global` if I want to refer to a (kernel invariant) attribute of `self`, or only for Python global variables?~~ ~~Besides that we do not mind if Python global variables are always kernel invariant.~~ # Type guards *Edit: Agreed upon in #8.* You propose using `type()` for [type guards](https://git.m-labs.hk/M-Labs/nac3-spec/src/branch/master/toy-impl#generics), and I expect this to simulate well. Though it might be challenging to distinct `int`/`int32`/`int64` during simulation if an `int` constant defaults to type `int32`. I am not sure how to deal with that yet, but one option could be to allow type guards with `else: raise TypeError()` to not implicitly fallback on the last type in the type guard. As mentioned in #6 , I am wondering how type guards will be used to differentiate list types. I wanted to note that syntax such as `type(arg) == list[int32]` can not be simulated correctly as this will only test the type of the container, not if the elements in the list are of type `int32`. # Dynamic dispatch You discuss [dynamic dispatch](https://git.m-labs.hk/M-Labs/nac3-spec#dynamic-dispatch), but I think the use of `virtual[]` will be a problem during simulation as this is not an existing Python object. Usage of `TypeVar('T', bound=Base)`, as already used by the Python typing lib, could maybe be an alternative. Dynamic dispatch is not something we highly desire at this moment, but if it is available we will probably use it.
Collaborator

As mentioned in #6 , I am wondering how type guards will be used to differentiate list types. I wanted to note that syntax such as type(arg) == list[int32] can not be simulated correctly as this will only test the type of the container, not if the elements in the list are of type int32.

I don't think there is a way for doing this for Python, except for numpy arrays etc.

Dynamic dispatch is not something we highly desire at this moment, but if it is available we will probably use it.

I think maybe we would not focus on dynamic dispatch at the moment.

> As mentioned in #6 , I am wondering how type guards will be used to differentiate list types. I wanted to note that syntax such as type(arg) == list[int32] can not be simulated correctly as this will only test the type of the container, not if the elements in the list are of type int32. I don't think there is a way for doing this for Python, except for numpy arrays etc. > Dynamic dispatch is not something we highly desire at this moment, but if it is available we will probably use it. I think maybe we would not focus on dynamic dispatch at the moment.
Author
Collaborator

I don't think there is a way for doing this for Python, except for numpy arrays etc.

I also think so. This seems a bit of a tricky situation where extra complexity is required for simulation. What do you think about a type guard ending with an else: raise TypeError()? Is that something that should be allowed in the compiler?

> I don't think there is a way for doing this for Python, except for numpy arrays etc. I also think so. This seems a bit of a tricky situation where extra complexity is required for simulation. What do you think about a type guard ending with an `else: raise TypeError()`? Is that something that should be allowed in the compiler?
Collaborator

What do you think about a type guard ending with an else: raise TypeError()? Is that something that should be allowed in the compiler?

OK for me. Should we raise an error when we compile the code and found the branch would be taken? The type guard would be evaluated during compilation and we would know which branch could be taken (if we only have type guards in the condition).
If so, should we only check for TypeError, or should we check for other kinds of errors?

> What do you think about a type guard ending with an `else: raise TypeError()`? Is that something that should be allowed in the compiler? OK for me. Should we raise an error when we compile the code and found the branch would be taken? The type guard would be evaluated during compilation and we would know which branch could be taken (if we only have type guards in the condition). If so, should we only check for `TypeError`, or should we check for other kinds of errors?
Owner

Deep parallel

OK, let's go for deep parallel in NAC3.

But besides that, shallow parallel can not be simulated in the way we do

It can, but I agree that the required level of Python interpreter hacks is somewhat disgusting :-)

> Deep parallel OK, let's go for deep parallel in NAC3. > But besides that, shallow parallel can not be simulated in the way we do It can, but I agree that the required level of Python interpreter hacks is somewhat disgusting :-)
Author
Collaborator

@pca006132 I guess it would result in a runtime error instead of a compile-time error, and no specific modifications to the compiler are required. The function arguments are typed, so it is known what the possible input types are. If the if ... elif ... components cover all possible/used input types, then the else ... clause would never be used for further compilation. Only when a valid argument type is given but the type is not specified in the if ... elif ... part, the compiler would use the else ... component for compilation. The raise TypeError could then just be compiled normally, resulting in a runtime error.

What do you think about that?

@pca006132 I guess it would result in a runtime error instead of a compile-time error, and no specific modifications to the compiler are required. The function arguments are typed, so it is known what the possible input types are. If the `if ... elif ...` components cover all possible/used input types, then the `else ...` clause would never be used for further compilation. Only when a valid argument type is given but the type is not specified in the `if ... elif ...` part, the compiler would use the `else ...` component for compilation. The `raise TypeError` could then just be compiled normally, resulting in a runtime error. What do you think about that?
Collaborator

@pca006132 I guess it would result in a runtime error instead of a compile-time error, and no specific modifications to the compiler are required. The function arguments are typed, so it is known what the possible input types are. If the if ... elif ... components cover all possible/used input types, then the else ... clause would never be used for further compilation. Only when a valid argument type is given but the type is not specified in the if ... elif ... part, the compiler would use the else ... component for compilation. The raise TypeError could then just be compiled normally, resulting in a runtime error.

What do you think about that?

I'm OK with this, this is just normal code. But should we warn for dead code?

> @pca006132 I guess it would result in a runtime error instead of a compile-time error, and no specific modifications to the compiler are required. The function arguments are typed, so it is known what the possible input types are. If the `if ... elif ...` components cover all possible/used input types, then the `else ...` clause would never be used for further compilation. Only when a valid argument type is given but the type is not specified in the `if ... elif ...` part, the compiler would use the `else ...` component for compilation. The `raise TypeError` could then just be compiled normally, resulting in a runtime error. > > What do you think about that? I'm OK with this, this is just normal code. But should we warn for dead code?
Author
Collaborator

I do not think there should be a warning for dead code in this scenario. If you write a generic function but in one program you do not exhaust all possible input types, a warning would be misplaced in my opinion.

I do not think there should be a warning for dead code in this scenario. If you write a generic function but in one program you do not exhaust all possible input types, a warning would be misplaced in my opinion.
Sign in to join this conversation.
No Label
No Milestone
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: M-Labs/nac3-spec#7
No description provided.