Please consider the following Modelica example model:
model Detection2
Real x = (1-2*time)*1e5;
parameter Real x_min=0.1;
Real y;
equation
y = max(x,x_min);
assert(y>x_min, "(assert) triggered at t = " + String(time), level=AssertionLevel.warning);
when y <= x_min then
Modelica.Utilities.Streams.print("(when) triggered at t = " + String(time));
end when;
annotation (experiment(StopTime=1));
end Detection2;
When I simulate it (in Dymola 2019FD01), I get the following results:
Results look as expected, but both the assert message and the print statement print the wrong time, they always print the stop time, not the time I expected!?
There are two parts to this:
Why didn't it trigger before the end? The reason is that for numerical reasons solvers create events for such relations, and don't want too many events, and thus consider y>x_min true as long as it is "almost true", i.e. that y>x_min-eps, for some small epsilon. This is explained in section 8.5 Events and Synchronization of the specification. https://specification.modelica.org/v3.4/Ch8.html#events-and-synchronization
Why did it trigger at the end? The reason is that there is an event at the end of the simulation, because the model may contain the terminal operator. At an event the comparisons are treated more literally.
If you had used x>x_min and x<=x_min instead it would have worked as you expected.
Alternatively if you had used y = if x>=x_min then x else x_min then y might be slightly below x_min, but the messages would trigger.
Related
I am trying to collect the participants' responses (i.e., the first key they press on the keyboard) and their reaction time (i.e., the time elapsed since the presentation of a picture and the response). I am using the KbQueueXXX functions in Psychtoolbox, but I am not sure if I am using them properly.
Thanks for your help.
Dear Psychtoolbox users,
I wrote a script to run a 2AFC task where participants must respond within a fixed deadline of 2 seconds. If participants respond earlier, then they move to the subsequent trial straight away. To collect their responses (i.e., the first key they pressed and their RT), I coded a custom function that incorporates KbQueueCheck(). I am now reviewing and debugging my script but I have some doubts about the KbQueueXXX functions. I would be grateful if you could give me some feedback.
Task script
In a mixture of code and pseudocode, here is what I am doing.
KbQueueCreate(KEYBOARD, KEYS_OF_INTEREST);
KbQueueStart(KEYBOARD);
% TRIALS' LOOP STARTS
for iTrial = 1:nTrials
KbQueueFlush(KEYBOARD);
% show stimulus and record its onset and offset times
respDeadline = stimulusOffset + 2;
collectResponse(KEYBOARD, respDeadline, stimulusOnset); % <- KbQueueCheck() here!
end
% TRIALS' LOOP ENDS
KbQueueStop(KEYBOARD);
KbQueueRelease(KEYBOARD);
Custom function
Below is my custom function collectResponse() where I embedded KbQueueCheck().
function [choice, rt, choiceTime, firstPress, keyCodes, pressed] = collectResponse(deviceIndex, untilTime, targetOnset)
%% LOOK FOR KEYPRESSES
pressed = false;
while pressed == false && GetSecs <= untilTime
[pressed, firstPress] = KbQueueCheck(deviceIndex);
end
%% PROCESS KEYPRESSES
if pressed == false % NO KEYS WERE PRESSED
keyCodes = NaN;
firstPress = NaN;
choiceTime = NaN;
choice = NaN;
rt = NaN;
else % ONE OR MORE KEYS WERE PRESSED
keyCodes = find(firstPress > 0); % all keypresses
choiceTime = min(firstPress(keyCodes)); % ts of first keypress
choice = find(firstPress == choiceTime); % first keypress
if length(choice) > 1
% handle simultaneous keypresses
choice = -999;
end
rt = choiceTime - targetOnset; % reaction time
end
end
My questions
I am not sure whether I am calling KbQueueXXX functions correctly and whether they are in the expected position.
Shall I keep KbQueueCreate()/KbQueueStart() and KbQueueStop()/KbQueueRelease() respectively before and after the trials’ loop?
Shall I rather just KbQueueStart(), KbQueueCheck(), and KbQueueStop() at each trial iteratively?
Is it OK to KbQueueFlush() at the beginning of each trial, before the presentation of a new stimulus?
Is my custom function collectResponse() fit for the purpose I described at the top of this post?
Thank you very much for your time, I look forward to knowing your thoughts.
Kind regards,
Valerio
OS: Windows 10
MATLAB: 9.10.0.1851785 (R2021a) Update 6
PTB: 3.0.18 - Flavor: beta - Corresponds to SVN Revision 13009
The original post can be found in the Psychtoolbox forum.
KbQueueXXX functions: which to use and where do they go in relation to the trials' loop?
I've tried searching for help but I haven't found a solution yet, I'm trying to repeat math.random.
current code:
local ok = ""
for i = 0,10 do
local ok = ok..math.random(0,10)
end
print(ok)
no clue why it doesn't work, please help
Long answer
Even if the preferable answer is already given, just copying it will probably not lead to the solution you may expect or less future mistakes. So I decided to explain why your code fails and to fix it and also help better understand how DarkWiiPlayer's answer works (except for string.rep and string.gsub).
Issues
There are at least three issues in your code:
the math.random(m, n) function includes lower and the upper values
local declarations hide a same-name objects in outer scopes
math.random gives the same number sequence unless you set its seed with math.randomseed
See Detailed explanation section below for more.
Another point seems at least worth mentioning or suspicious to me, as I assume you might be puzzled by the result (it seems to me to reflect exactly the perspective of the C programmer, from which I also got to know Lua): the Lua for loop specifies start and end value, so both of these values are included.
Attempt to repair
Here I show how a version of your code that yields the same results as the answer you accepted: a sequence of 10 percent-encoded decimal digits.
-- this will change the seed value (but mind that its resolution is seconds)
math.randomseed(os.time())
-- initiate the (only) local variable we are working on later
local ok = ""
-- encode 10 random decimals (Lua's for-loop is one-based and inclusive)
for i = 1, 10 do
ok = ok ..
-- add fixed part
'%3' ..
-- concatenation operator implicitly converts number to string
math.random(0, 9) -- a random number in range [0..9]
end
print(ok)
Detailed explanation
This explanation makes heavily use of the assert function instead of adding print calls or comment what the output should be. In my opinion assert is the superior choice for illustrating expected behavior: The function guides us from one true statement - assert(true) - to the next, at the first miss - assert(false) - the program is exited.
Random ranges
The math library in Lua provides actually three random functions depending on the count of arguments you pass to it. Without arguments, the result is in the interval [0,1):
assert(math.random() >= 0)
assert(math.random() < 1)
the one-argument version returns a value between 1 and the argument:
assert(math.random(1) == 1)
assert(math.random(10) >= 1)
assert(math.random(10) <= 10)
the two-argument version explicitly specifies min and max values:
assert(math.random(2,2) == 2)
assert(math.random(0, 9) >= 0)
assert(math.random(0, 9) <= 9)
Hidden outer variable
In this example, we have two variables x of different type, the outer x is not accessible from the inner scope.
local x = ''
assert(type(x) == 'string')
do
local x = 0
assert(type(x) == 'number')
-- inner x changes type
x = x .. x
assert(x == '00')
end
assert(type(x) == 'string')
Predictable randomness
The first call to math.random() in a Lua program will return always the same number because the pseudorandom number generator (PRNG) starts at seed 1. So if you call math.randomseed(1), you'll reset the PRNG to its initial state.
r0 = math.random()
math.randomseed(1)
r1 = math.random()
assert(r0 == r1)
After calling math.randomseed(os.time()) calls to math.random() will return different sequences presuming that subsequent program starts differ at least by one second. See question Current time in milliseconds and its answers for more information about the resolutions of several Lua functions.
string.rep(".", 10):gsub(".", function() return "%3" .. math.random(0, 9) end)
That should give you what you want
I have tried this code:
model var
Real x;
Real y;
Real z;
equation
x=6*time;
when time>=6 then
z=x;
end when;
y=3*z;
end var;
But it will give me y = 3*x(at time = 6) but from time = 6 while i need it from the beginning.
Any direct method for this problem?
Based on folks comments you now know that Modelica is quite strict in its treatment of time behavior. You could argue that it is a more physical representation of time (quantum and other crazy physics aside), as in you can not time-travel in your code.
Depending on your application there may be ways around your issue. One possibility is to move the time behavior to the initialization. That way you capture the behavior prior to time=0 and start at time=0 with the expected behavior.
For example:
model var
parameter Modelica.SIunits.Time t_zero = 6;
parameter Real x(fixed=false);
Real y;
Real z;
initial equation
x = 6*t_zero; // or some more complicated set of equations/functions
equation
z = x;
y=3*z;
end var;
Recognized this restricts things, potentially too much, but you can have many parameters and have a more complex representation in the initial equation block. You can also call a function x=func() where you have performed integrations, etc. to get the value of x at time=0.
Hope that helps, either now or in the future.
I am relatively new to Modelica (Dymola-environment) and I am getting very desperate/upset that I cannot solve such a simple problem as a random number generation in Modelica and I hope that you can help me out.
The simple function random produces a random number between 0 and 1 with an input seed seedIn[3] and produces the output seed seedOut[3] for the next time step or event. The call
(z,seedOut) = random(seedIn);
works perfectly fine.
The problem is that I cannot find a way in Modelica to compute this assignment over time by using the seedOut[3] as the next seedIn[3], which is very frustrating.
My simple program looks like this:
*model Randomgenerator
Real z;
Integer seedIn[3]( start={1,23,131},fixed=true), seedOut[3];
equation
(z,seedOut) = random(seedIn);
algorithm
seedIn := seedOut;
end Randomgenerator;*
I have tried nearly all possibilities with algorithm assignments, initial conditions and equations but none of them works. I just simply want to use seedOut in the next time step. One problem seems to be that when entering into the algorithm section, neither the initial conditions nor the values from the equation section are used.
Using the 'sample' and 'reinit' functions the code below will calculate a new random number at the frequency specified in 'sample'. Note the way of defining the "start value" of seedIn.
model Randomgenerator
Real seedIn[3] = {1,23,131};
Real z;
Real[3] seedOut;
equation
(z,seedOut) = random(seedIn);
when sample(1,1) then
reinit(seedIn,pre(seedOut));
end when;
end Randomgenerator;
The 'pre' function allows the use of the previous value of the variable. If this was not used, the output 'z' would have returned a constant value. Two things regarding the 'reinint' function, it requires use of 'when' and requires 'Real' variables/expressions hence seedIn and seedOut are now defined as 'Real'.
The simple "random" generator I used was:
function random
input Real[3] seedIn;
output Real z;
output Real[3] seedOut;
algorithm
seedOut[1] :=seedIn[1] + 1;
seedOut[2] :=seedIn[2] + 5;
seedOut[3] :=seedIn[3] + 10;
z :=(0.1*seedIn[1] + 0.2*seedIn[2] + 0.3*seedIn[3])/(0.5*sum(seedIn));
end random;
Surely there are other ways depending on the application to perform this operation. At least this will give you something to start with. Hope it helps.
Rubocop is always report the error:
app/controllers/account_controller.rb:5:3: C: Assignment Branch Condition size for index is too high. [30.95/24]
if params[:role]
#users = #search.result.where(:role => params[:role])
elsif params[:q] && params[:q][:s].include?('count')
#users = #search.result.order(params[:q][:s])
else
#users = #search.result
end
How to fix it? Anyone has good idea?
The ABC size [1][2] is
computed by counting the number of assignments, branches and conditions for a section of code. The counting rules in the original C++ Report article were specifically for the C, C++ and Java languages.
The previous links details what counts for A, B, and C. ABC size is a scalar magnitude, reminiscent of a triangulated relationship:
|ABC| = sqrt((A*A)+(B*B)+(C*C))
Actually, a quick google on the error shows that the first indexed page is the Rubocop docs for the method that renders that message.
Your repo or analysis tool will define a threshold amount when the warning is triggered.
Calculating, if you like self-inflicting....
Your code calcs as
(1+1+1)^2 +
(1+1+1+1+1+1+1+1+1+1+1+1+1)^2 +
(1+1+1+1)^2
=> 194
That's a 'blind' calculation with values I've made up (1s). However, you can see that the error states numbers that probably now make sense as your ABC and the threshold:
[30.95/24]
So cop threshold is 24 and your ABC size is 30.95. This tells us that the rubocop engine assign different numbers for A, B, and C. As well, different kinds or Assignments (or B or C) could have different values, too. E.G. a 'normal' assignment x = y is perhaps scored lower than a chained assignment x = y = z = r.
tl;dr answer
At this point, you probably have a fairly clear idea of how to reduce your ABC size. If not:
a simple way it to take the conditional used for your elsif and place it in a helper method.
since you are assigning an # variable, and largely calling from one as well, your code uses no encapsulation of memory. Thus, you can move both if and elsif block actions into each their own load_search_users_by_role and load_search_users_by_order methods.