To refactor a program, I took a complex process I want to abstract and placed it within a macro.
%macro BlackBox();
data _null_;
put "This represents a complex process I want to abstract.";
run;
%mend;
The process needs to occur multiple times in succession, so the obvious solution is to place it within a loop.
data _null_;
do i = 1 to 3;
%BlackBox();
end;
run;
This, however, produces the following error.
ERROR 117-185: There was 1 unclosed DO block.
What is happening?
My best guess is that SAS is trying to run a data step within a data step.
I find that I can avoid this error by enclosing my loop within a macro and then immediately calling the macro.
%macro PerformDoLoop();
%do i = 1 %to 3;
%BlackBox();
%end;
%mend;
%PerformDoLoop;
All this seems like an roundabout way of handling a fundamental programming task. I'm hoping that learning more about why the data step approach fails will give me insight into how to complete this task more elegantly.
Please understand that this is a simplified example used to illustrate the error I am encountering. A real instance of the macro may take in arguments or return values.
Your assumption is precisely correct; SAS is trying to execute a data step within a data step, and that's not going to go anywhere of course (well, it's possible, but only ... complexly).
The macro loop method is entirely reasonable, and I would argue for other programming languages that's basically what you'd do. You write a method draw_box to display a box on the screen in C#, then you write a method draw_three_boxes that displays three boxes on the screen by calling draw_box three times.
Now, the reason it seems silly is that you have bad programming design insomuch as the draw_three_boxes method is very limited: it's only capable of doing one thing, drawing three boxes, so why didn't you just make the original draw_box method do that in the first place?
Presumably, what you should want to do is write draw_box and then write draw_boxes(int count, int xpos, int ypos) or something like that, right? Same thing here. You shouldn't write the PerformDoLoop() macro as you did because you're hardcoding the number of times to perform the loop.
Instead get at why you are running it three times. If it's really something you just know and isn't a piece of data, well, write %PerformDoLoop(count=) and then call %PerformDoLoop(count=3). Or include the %do loop in the original macro, with a parameter for count, default it to one.
More likely, there is a data driven reason for doing it 3 times. You have 3 states. You have 3 classes of students. Whatever. Use that to generate the calls to %BlackBox. That will give you the best results, because then you aren't doing it in the program - your data changes, you instantly get 2 or 4 or whatever calls.
You can see my recently presented paper, Writing Code With Your Data from SESUG 2016 for more information on how to do that.
The macro language is a pre-processor. It generates SAS code, and it executes before the DATA step code is even compiled. With your code:
data _null_;
do i = 1 to 3;
%BlackBox();
end;
run;
The macro %BlackBox() will execute once (not three times, because it executes before the DO loop executes, conceptually outside of the DO loop). And the data step code becomes:
data _null_;
do i = 1 to 3;
data _null_;
put "This represents a complex process I want to abstract.";
run;
end;
run;
As you say, it is not possible in SAS to execute a data step inside another data step. The data _null_ on line 3 ends the first data step, leaving it within an unclosed do block.
I agree with #Joe's points. If you want to generate a number of macro calls, using a macro %DO loop to do so is a fine approach. His paper gives a nice approach for using data to generate macro calls, by building macro variables that resolve to a list of macro calls.
Another useful approach to learn is CALL EXECUTE. This allows you to use a data step to generate macro calls. CALL EXECUTE generates the macro calls when the data step executes, and the macros will execute outside of the data step (when you use %NRSTR as below). For example:
data _null_;
do i = 1 to 3;
call execute ('%nrstr(%BlackBox())');
end;
run;
Will generate three macro calls:
NOTE: CALL EXECUTE generated line.
1 + %BlackBox()
MPRINT(BLACKBOX): data _null_;
MPRINT(BLACKBOX): put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX): run;
This represents a complex process I want to abstract.
2 + %BlackBox()
MPRINT(BLACKBOX): data _null_;
MPRINT(BLACKBOX): put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX): run;
This represents a complex process I want to abstract.
3 + %BlackBox()
MPRINT(BLACKBOX): data _null_;
MPRINT(BLACKBOX): put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX): run;
This represents a complex process I want to abstract.
Related
We want to be able to provide a predefined list of things to be done at the end of every SystemVerilog test. Since multiple people are working on this project, it'd be nice if they did not have to think about the things we are doing in the background, but simply call $finish at the end of a test as usual. I know we could create our own custom $finish macro, but we would prefer to not have to change preexisting tests.
Is there any way in SystemVerilog to have a block of code run after a $finish call? Using something like UVM is not an option. I've looked around, but I can't seem to find something that does this behavior.
The final keyword can help you out here. Refer to IEEE Std 1800-2017, section 9.2.3 Final procedures:
A final procedure executes when simulation ends due to an explicit or
implicit call to $finish .
One limitation is that it executes in zero time, which means you can not have any delays, etc. Read the full description for all the details.
Example:
final begin
$display("something");
do_something();
end
If the list of things does not consume time, a final block is the antithesis of an initial block, except it cannot consume any time. Otherwise, it would not be the "final" thing.
If you need steps that consume time, there is no way of doing this without modifying the existing tests. The simplest approach is declaring a global event like test_done in a package p, and then replacing $finish; with ->p::test_done;. But sometimes you need to shut down other free-running process. Doing that requires much more coordination, which is exactly what UVM accomplishes with its phases and objections mechanism.
How should we insert a page break via the code itself in maple program ?
Not insert page break? of menu
As I want to insert the page break as the code runs and with other outputs. In maple code.
Let's suppose that you have the following PutGraph procedure defined and available.
(You could define it in the Startup Code region of your Document/Worksheet. Or you could define it in your Maple initialization file. Or you could define it and store it in a .mla Library archive within your libname path. Or you could simply define it at the start of any Document/Worksheet in which you want to utilize it. I leave that choice to you.)
Then you can utilize this Putgraph procedure instead of DrawGraph, with the extra effect that it inserts a pagebreak right before the inlined plot of the Graph.
It also accepts a 2D or 3D plot as its argument. And if its argument is a Graph then you can also pass additional arguments that would normally be accepted by DrawGraph.
The restriction is that you can only effectively use one such call to this implementation of PutGraph per Execution Group or Document Block. If you want it done otherwise then you will have to show me explicitly how you need to call it once for multiple insertions. If you want specific behaviour then you will have to describe it adequately.
Here's the procedure, as 1D Maple notation code:
PutGraph:=proc(GG::{Graph,specfunc({PLOT,PLOT3D})})
local T,xml;
uses DocumentTools,DocumentTools:-Layout,GraphTheory;
if GG::Graph then
T:=DrawGraph(GG,_rest);
else T:=GG; end if;
xml:=Worksheet(Group(Input(Textfield(InlinePlot(T)))));
InsertContent(subsindets(xml,'specfunc(anything,_XML_Input)',
u->':-_XML_Input'(':-_XML_Pagebreak'(),
op(u))));
return NULL;
end proc:
Here's some example of calling it. (I'll mention again: it needs to be called at most once per Execution Group or Document Block. If you put it more than once in the same Execution Group/Block then only the last instance will work as intended.)
G1 := GraphTheory:-RandomGraphs:-RandomGraph(6,degree=3):
Now, in its own Execution Group/Document Block,
PutGraph(G1);
You can have something else appear before the next page break, or several things like text, other input/output, etc.
somethingelse;
And now, in its own Execution Group/Block,
PutGraph(G1,showlabels);
And so on,
somethingelse;
PutGraph(G1,showweights);
somethingelse;
P := GraphTheory:-DrawGraph(G1):
PutGraph(P);
somethingelse;
I have MATLAB Simulink model running successfully. In this model, a function runs code to do the operation. The function code generates a value during the operation. I want to save this value and use at successive operations.
In above screen shot, Icur_in and Icur_ou are input and output of the function. In fact both refer to same value. I am using memory function to hold the value for next operation. Up to this point is fine. But the value keeps on changing.
My present code is:
Function [Icur_ou] = fun(Icur_in)
Icur_ou = Icur_in;
if somecondition
Icur_ou = I_s;
end
end
I am not 100% sure about what your problem is, since you state: "Up to this point is fine." And also your model looks fine to me, but nevertheless I found this link, which seems to be about the same problem. Besides the suggested solutions, I also liked the first two comments about persistent variables, which I think could maybe also help you depending on the sample time of your model (see comments in the link).
In this case, you would not need the memory block, but instead you'd have to make your Icur_ variable persistent, so that it stays in memory between function calls. Similar to this:
Function [Icur_ou] = fun(Icur_in)
persistent Icur_ou = Icur_in;
if somecondition
Icur_ou = I_s;
end
end
I have to work with a lot of data and run the same MATLAB program more than once, and every time the program is run it will store the data in the same preset variables. The problem is, every time the program is run the values are overwritten and replaced, most likely because all the variables are type double and are not a matrix. I know how to make a variable that can store multiple values in a program, but only when the program is run once.
This is the code I am able to provide:
volED = reconstructVolume(maskAlignedED1,maskAlignedED2,maskAlignedED3,res)
volMean = (volED1+volED2+volES3)/3
strokeVol = volED-volES
EF = strokeVol/volED*100
The program I am running depends on a ton more MATLAB files that I cannot provide at this moment, however I believe the double variables strokeVol and EF are created at this instant. How do I create a variable that will store multiple values and keep adding the values every time the program is run?
The reason your variables are "overwritten" with each run is that every function (or standalone program) has its own workspace where the local variables are located, and these local variables cease to exist when the function (or standalone program) returns/terminates. In order to preserve the value of a variable, you have to return it from your function. Since MATLAB passes its variables by value (rather than reference), you have to explicitly provide a vector (or more generally, an array) as input and output from your function if you want to have a cumulative set of data in your calling workspace. But it all depends on whether you have a function or a deployed program.
Assuming your program is a function
If your function is now declared as something like
function strokefraction(inputvars)
you can change its definition to
function [EFvec]=strokefraction(inputvars,EFvec)
%... code here ...
%volES initialized somewhere
volED = reconstructVolume(maskAlignedED1,maskAlignedED2,maskAlignedED3,res);
volMean = (volED1+volED2+volES3)/3;
strokeVol = volED-volES;
EF = strokeVol/volED*100;
EFvec = [EFvec; EF]; %add EF to output (column) vector
Note that it's legal to have the same name for an input and an output variable. Now, when you call your function (from MATLAB or from another function) each time, you add the vector to its call, like this:
EFvec=[]; %initialize with empty vector
for k=1:ndata %simulate several calls
inputvar=inputvarvector(k); %meaning that the input changes
EFvec=strokefraction(inputvar,EFvec);
end
and you will see that the size of EFvec grows from call to call, saving the output from each run. If you want to save several variables or arrays, do the same (for arrays, you can always introduce an input/output array with one more dimension for this purpose, but you probably have to use explicit indexing instead of just shoving the next EF value to the bottom of your vector).
Note that if your input/output array eventually grows large, then it will cost you a lot of time to keep allocating the necessary memory by small chunks. You could then choose to allocate the EFvec (or equivalent) array instead of initializing it to [], and introduce a counter variable telling you where to overwrite the next data points.
Disclaimer: what I said about the workspace of functions is only true for local variables. You could also define a global EFvec in your function and on your workspace, and then you don't have to pass it in and out of the function. As I haven't yet seen a problem which actually needed the use of global variables, I would avoid this option. Then you also have persistent variables, which are basically globals with their scope limited to their own workspace (run help global and help persistent in MATLAB if you'd like to know more, these help pages are surprisingly informative compared to usual help entries).
Assuming your program is a standalone (deployed) program
While I don't have any experience with standalone MATLAB programs, it seems to me that it would be hard to do what you want for that. A MathWorks Support answer suggests that you can pass variables to standalone programs, but only as you would pass to a shell script. By this I mean that you have to pass filenames or explicit numbers (but this makes sense, as there is no MATLAB workspace in the first place). This implies that in order to keep a cumulative set of output from your program you would probably have to store those in a file. This might not be so painful: opening a file to append the next set of data is straightforward (I don't know about issues such as efficiency, and anyway this all depends on how much data and how many runs of your function we're talking about).
I am currently working on a tool written in M-Script that executes a set of checks on a given simulink model. This tool does not compile/execute the model, I'm using find_system and get_param to retrieve all the information I need in order to run the routines of my tool.
I've reached a point where I need to determine whether a certain block has direct-feedthrough or not. I am not entirely sure how to do this. Two possible solutions come to mind:
A property might store this information and might be accessible via get_param. After investigating this, I could not find any such property.
Some block types have direct-feedthrough (Sum, Logic, ...), some other do not (Unit Delay, Integrator), so I could use the block type to determine whether a block has direct-feedthrough or not. Since I'm not an experienced Simulink modeller, I'm not sure if its possible to tell whether a block has direct-feedthrough by solely looking at its block type. Also, this would require a lookup table including all Simulink block types. An impossible task, since additional block types might get added to Simulink via third party modules.
Any help or pointers to possible solutions are greatly appreciated.
after some further research...
There is an "official solution" by Matlab:
just download the linked m-file
It shows that my idea was not that bad ;)
and for the record, my idea:
I think it's doable quite easily. I cannot present you some code yet, but I'll see what I can do. My idea is the following:
programatically create a new model
Add a Constant source block and a Terminator
add the Block you want to get to know the direct feedthrough ability in the middle
add_lines
run the simulation and log the states, which will give you the xout variable in the workspace.
If there is direct feedthrough the vector is empty, otherwise not.
probably you need to include some try/catch error catching for special cases
This way you can analyse a block for direct feedthrough by just migrating it to another model, without compiling your actual main model. It's not the fastest solution, but I can not imagine that performance matters that much for you.
Here we go, this script works fine for my examples:
function feedthrough = hasfeedthrough( input )
% get block path
blockinfo = find_system('simulink','Name',input);
blockpath = blockinfo{1};
% create new system
new_system('feed');
open_system('feed');
% add test model elements
src = add_block('simulink/Sources/Constant','feed/Constant');
src_ports = get_param(src,'PortHandles');
src_out = src_ports.Outport;
dest = add_block('simulink/Sinks/To Workspace','feed/simout');
dest_ports = get_param(dest,'PortHandles');
dest_in = dest_ports.Inport;
test = add_block(blockpath,'feed/test');
test_ports = get_param(test,'PortHandles');
test_in = test_ports.Inport;
test_out = test_ports.Outport;
add_line('feed',src_out,test_in);
add_line('feed',test_out,dest_in);
% setup simulation
set_param('feed','StopTime','0.1');
set_param('feed','Solver','ode3');
set_param('feed','FixedStep','0.05');
set_param('feed','SaveState','on');
% run simulation and get states
sim('feed');
% if condition for blocks like state space
feedthrough = isempty(xout);
if ~feedthrough
a = simout.data;
if ~any(a == xout);
feedthrough = ~feedthrough;
end
end
delete system
close_system('feed',1)
delete('feed');
end
When enter for example 'Gain' it will return 1, when you enter 'Integrator' it will return 0.
Execution time on my ancient machine is 1.3sec, not that bad.
Things you probably still have to do:
add another parameter, to define whether the block is continuous or discrete time and set the solver accordingly.
test some "extraordinary" blocks, maybe it's not working for everything. Also I haven implemented anything which could deal with logic, but actually the constant is 1 so it should work as well.
Just try out everything, at least it's a good base for you to work on.
A famous exception is the StateSpace Block which can have direct feedthrough AND states. But there are not sooo much standard blocks with this "behaviour". If you also have to deal with third party blocks you could get into some trouble, I have to admit that.
possible solution for the state space: if one compares xout with yout than one can find another indicator for direct feedthrough: if there is, the vectors are not equal. If so, than they are equal. Just an example, but I can imagine that it is possible to find more general ways to test things like that.
besides the added simout block above one needs the condition:
% if condition for blocks like state space
feedthrough = isempty(xout);
if ~feedthrough
a = simout.data;
if ~any(a == xout);
feedthrough = ~feedthrough;
end
end
From the documentation:
Tip
To determine if a block has direct feedthrough:
Double-click the
block. The block parameter dialog box opens.
Click the Help button in
the block parameter dialog box. The block reference page opens.
Scroll
to the Characteristics section of the block reference page, which
lists whether or not that block has direct feedthrough.
I couldn't find a programmatic equivalent though...
Based on a similar approach to the one by #thewaywewalk, you could set up a temporary model that contains an algebraic loop, similar to,
(Note that you would replace the State-Space block with any block that you want to test.)
Then set the diagnostics to error out if there is an algebraic loop,
If an error occurs when the model is compiled
>> modelname([],[],[],'compile');
(and you should check that it is the Algebraic Loop error that has occured), then the block has direct feed though.
If no error occurs then the block does not have direct feed though.
At this point you would need to terminate the model using
>> modelname([],[],[],'term');
If the block has multiple inports or outprts then you'll need to iterate over all combinations of them.