4.13 Group Inputs and Default Path
The introduction to application of stress-flow to electrical circuit emulation has described the problem of paring or grouping inputs when emulating analog circuits digitally. So far, the discussion has avoided the issue by declaring two parameters for the entry point to the routine. Prior art avoided this problem altogether by either using a special control input to trigger calculations or by requiring that all parameters must arrive at inputs for the calculations to begin – which is a serious constraint. Our temporary solution of requiring an entry point with two parameters would actually be worse than the unnatural prior art methods, because we would be unable to connect inputs of our instrument to separate outputs of other instrument(s).
If we separated the input to the instrument into two inputs, “in_a” and “in_b” triggering sq1 and sq2 separately, we still would be able to trigger the pair together, by either the compounded expression, or by adding them both to the same “connector” construct. An example of a compound expression call to our instrument would look like this:
This would naturally express the problem at hand, but it would also introduce a problem of semantically allowed firings of just one input:
This would be totally incorrect because the “add” atom expects calculations on a pair of inputs to arrive before it can proceed. Calling a single input like that would result in completely erroneous results being outputted. A call like that is not conceptually wrong. An analog circuit that we were trying to emulate naturally allows being able to change one input without changing the other. To emulate such operation correctly in a digital model, we would, however, have to be able to feed previous value of input to an unmatched firing. For example, if firing of our instrument had the form of “in_a(10),in_b(12);”, then the consecutive, unmatched firing of “in_a(20);” should be internally translated to equivalent of “in_a(20),in_b(12);”.
FIG. 18: Inferior way to implement grouped inputs
To accomplish this goal, stress-flow introduces the concept of “group stress-flow atoms.” Group stress-flow atoms simply list several atoms that must be triggered together. To describe action to take in place of a missing member of a group, some special keyword (like “default”) could be used together with name of function to provide the default action for as shown on FIG. 18. The default functions would use copies “a_cp” and “b_cp” of previous values of the parameters to calls of in_a and in_b to issue proper calls to sq1 and sq2.
This solution will work, but is not optimal. There is really no need to recalculate sq1 or sq2 for default path operations. To further optimize the example, we could provide default paths for sq1 and sq2, again storing the results of square operations. In general case, however, we should also provide default path for “add_a/add_b” collector and “dsqrt” atom because it would be possible that both in_a and in_b were following the default paths. First problem with such a solution would be that it would require a lot of typing resulting in essentially the same operation for every atom (store parameters/results in regular operation code and use it in default operation code). Second problem would be that this would not work with the collector, which has two or more inputs, possibly generating combinations of regular/default calls, which would require separate function for each combination, which would not make any sense.
FIG. 19: Improved/proper form of code from FIG. 18
The best way to implement the default path functions is to have the compiler internally generate default path functions for all stress flow atoms that have “ordered” and “last-only” calls. A default path function for all other atoms could also be generated, but the issue is strictly philosophical since such function would do nothing. Each stress flow atom making “ordered” and “last-only” calls already possesses the “order reserve list” of “ordered” and “last-only” atoms that it calls from its body. Therefore, all that default path function has to do is call the default path functions of all the atoms on the “order reserve list”. Code written for a compiler that uses this idea became much simpler and is shown on FIG. 19. Default path functions of stress-flow atoms are actions to take, “by default” if the normal path is missing. Therefore, the default path function is called when a place for an ordered/last-only call was reserved but unused. This now completes the explanation of the ordered/last-only atoms operation.
FIG. 19A: Modification of code from FIG. 19 that disables default path function
For this code to work properly, the default path function for a “collector” must only be triggered when all arriving calls in the set are default-path calls. One or more regular call in the collector set forces “regular” collector function to be processed. For this reason, the compiler must store the previous values of parameters for each collector entry point here. Similar need occurs for some other situations and this is why the compiler must often or always internally store last parameters for each ordered/last-only stress-flow atom. Suppose we have a situation where the “result” function in “dsqrt” is not an ordered/last-only atom call, but a non-stress flow atom function that simply outputs the calculated value to some hardware. In such a case, the functioning of the default function of “dsqrt” might be incorrect in case we actually wanted to output the repeated value anyway. The best solution for such a situation is to be able to force default path execution onto the regular path and be able to test inside what path it is. The “dsqrt” function redefined this way is shown on FIG. 19A. The “nodef” keyword informs the compiler that compiler generated default-path function isn’t wanted. The default-path calls still can happen, they just will go to the same function. Expressions with the name of the atom are used to check whether or not the currently executing atom was triggered as regular or default-path call. The name evaluates to zero for default-path calls. This method is better than using some “isdefault” keyword/built-in function due to construction of the collector that can have some paths default and some not. Using the path names for checking allows access to default-path/regular info for each path. For example, in our collector we could write: if (add_a && add_b) to test if both entry paths are “regular”. Since the result in FIG. 19A modified example is a function that writes directly to some hardware, not overlapping it among different atoms is most appropriate and this is why it is moved to the stressed section. This example shows how large interconnected blocks of ordered/last-only operations can naturally interface with non-ordered stress-flow atoms or hardware. The “if” statement isn’t needed for anything in this case, it was just placed there to demonstrate the default-path/regular test. It would be possible to design stress-flow language in such a way that there would be no compiler-generated default-paths and the needed functionality would be performed by a single if statement testing for regular/default-path condition. This would save one keyword, but would not be as elegant since decisive majority of ordered/last-only stress-flow atoms need nothing original in the default-path section. (Other than calling default-path function for the order reserve list atoms, that always needs to be generated internally by the compiler).
FIG. 19B: Default-path group definition and invocation
In some cases that use the group construct, it may be required to be able to trigger the default path for entire group construct explicitly. This is best done by naming the group and using the group name for the all default-path triggering as shown on FIG. 19B. It would also be possible to do similar thing by creating a fake expression like: “if(false) aaa.in_a()”. In case the group is defined as ordered/last-only, this will reserve the space in the in_a queue and never use it, which will trigger the default path. Problem with this solution is that the good compiler will optimize the whole expression out if the “if” statement evaluates to a constant. Depending on method chosen for creating the “order reserve list” of each atom, specifically on counteraction of it with the optimization process, this method might be a good or a bad idea.
FIG. 19C: Example of “required” group path
In some cases of the group construct use, it is necessary to declare one or more grouped atoms as always needed to be called explicitly. This would only be a compiler information feature included in declaration and has no other bearing on the functionality. Suppose we used some “required” keyword to inform the compiler that “in_a” function is always required to be used explicitly. This would only mean that “in_a(10),in_b(20);” and “in_a(20);” expressions would still be valid, while “in_b(10);” would generate a compiler error. As before, rather than reserve another keyword, “void !in_a()” declaration could be used to accomplish the same thing.
Code implemented using all concepts of stress-flow removes all the imperfections and limitations that were hounding prior art systems, it naturally runs on multi-processor system without any centralized supervisor, and it optimizes itself to the highest extend possible. Consider the FIG. 19 example with FIG. 19A improvements. If both “in_a” and “in_b” calls are “regular”, all operations overlap themselves in the highest theoretically possible way as shown on FIG. 14A. If one of the paths is a “default call”, the square for that path is not recalculated. If both paths are “default calls,” all atoms in the model including the “add” collector and “dsqrt” are default calls which almost instantly outputs the previously calculated result.
Send mail to
email@example.com with questions or