Hi everyone,
I tried to use the code below to solve r in the bove equation.
Could you please have a look at what is the issue in the attached SAS code?
Thank you very much for your help.
KindRegards,
Mac
Hi @MAC1430,
I think the reason for the "Read Access Violation" exception is that you're using array references B[0] (i.e., with index 0) in the definition of function h. Unlike arrays in a DATA step, arrays in PROC FCMP are always 1-based (see the remark about "lower-bound specifications" in the documentation), so the array indices of array B cannot match the corresponding variable "subscripts" (starting at 0). But you can define the array B so that B[1]=B0, B[2]=B1 and so on. This is exactly what your ARRAY statement
array B[*] B0-B11;
later in the DATA steps does.
The correction is a simple shift (by +1) of the indices i of the array references B[i] in the definition of function h:
c11= 11*p-B[1]*(F[1]+1)+B[2];
c10= -10*F[1]*B[1]-F[2]*B[2]-10*B[1]+9*B[2]+B[3]+55*p;
c9= -45*B[1]*(F[1]+1)+B[2]*(36-9*F[2])+B[3]*(8-F[3])+165*p+B[4];
c8= -120*B[1]*(F[1]+1)+B[2]*(84-36*F[2])+B[3]*(28-8*F[3])+330*p+(7-F[4])*B[4]+B[5];
c7= -210*B[1]*(F[1]+1)+B[2]*(126-84*F[2])+B[3]*(56-28*F[3])+B[5]*(6-F[5])+462*p+(21-7*F[4])*B[4]+B[6];
c6= -252*B[1]*(F[1]+1)+B[2]*(126-126*F[2])+B[3]*(70-56*F[3])+B[6]*(5-F[6])+B[5]*(15-6*F[5])+462*p+(35-21*F[4])*B[4]+B[7];
c5= -210*B[1]*(F[1]+1)+B[2]*(84-126*F[2])+B[3]*(56-70*F[3])+B[5]*(20-15*F[5])+B[6]*(10-5*F[6])+B[7]*(4-F[7])+330*p+35*B[4]*(1-F[4])+B[8];
c4= -120*B[1]*(F[1]+1)+B[2]*(36-84*F[2])+B[3]*(28-56*F[3])+B[5]*(15-20*F[5])+B[6]*(10-10*F[6])+B[7]*(6-4*F[7])+B[8]*(3-F[8])+165*p+B[4]*(21-35*F[4])+B[9];
c3= -45*B[1]*(F[1]+1)+B[2]*(9-36*F[2])+B[3]*(8-28*F[3])+B[5]*(6-15*F[5])+B[6]*(5-10*F[6])+B[7]*(4-6*F[7])+3*B[8]*(1-F[8])+B[9]*(2-F[9])+55*p+7*B[4]*(1-3*F[4])+B[10];
c2= -10*B[1]*(F[1]+1)+B[2]*(1-9*F[2])+B[3]*(1-8*F[3])+B[5]*(1-6*F[5])+B[6]*(1-5*F[6])+B[7]*(1-4*F[7])+B[8]*(1-3*F[8])+B[9]*(1-2*F[9])+B[10]*(1-F[10])+11*p+B[4]*(1-7*F[4])+B[11];
c1= -F[1]*B[1]-F[2]*B[2]-F[3]*B[3]-F[5]*B[5]-F[6]*B[6]-F[7]*B[7]-F[8]*B[8]-F[9]*B[9]-F[10]*B[10]-F[11]*B[11]-B[1]+p-F[4]*B[4]+B[12];
c0= -F[12]*B[12];
The original function has poles at r=-1 and r=0. Therefore, the IF condition in the definition of function xrate2 should be extended (cf. the solution in your earlier thread where the function had poles at r=-1 and r=g):
if min(abs(1+r),abs(r))<1e-4 then r=.; /* avoid poles of the original function */
Of course, if solutions like r=0.00009 are considered valid and useful, you should modify the criterion correspondingly (e.g., use a smaller upper bound like 1e-5).
After the above changes your code runs without issues (except for the trivial "Invalid data" notes in the creation of your sample data, as ballardw has already mentioned). The comment after the definition of array init should now read
/* Up to 12 roots are possible theoretically. */
... although for your current sample data at most one solution is found. Only 89 observations have non-missing values for all parameters. Among these, only 3 have a solution in variable r1 of dataset WANT. There are cases where r=0 or values very close to zero would be solutions if such were admissible (example: stock='BE0003810273' & year=2016). The good news is that those three solutions pass the check shown below:
data sel;
set want;
where r1>.;
run;
proc fcmp outlib=work.funcs.test;
function m(r, p, B[*], F[*]);
u=1+r;
s1=p-B[1];
do t=1 to 11;
s1 = s1 - (F[t]-r)/u**t*B[t];
end;
z = s1 - (F[t]-r)/r/u**11*B[12];
return(z);
endfunc;
run;
data check;
set sel;
array B[*] B0-B11;
array F[*] F1-F12;
diff=m(r1, p, B, F);
run;
proc print data=check;
var stock year r1 diff;
run;
Result:
Obs stock year r1 diff
1 AT0000606306 2014 0.67999 -3.4596E-8
2 BE0003565737 2009 0.59515 6.96638E-11
3 BE0974264930 2017 0.01498 1.878618E-9
I've checked one of the 89−3=86 cases with no solutions (in the interval (0, 1)) -- stock='AT0000606306' & year=2012 -- and found that the polynomial in this case has indeed no root in (0, 1), only one negative root (-0.006...) and one >10. You may want to check a few more of these cases, but I see no indications that the results might be incorrect. (I haven't checked the definitions of c0, c1, etc. in detail, though. At least the results in dataset CHECK are consistent with the assumption that you did a great job in calculating them.)
First thing, your data step generates hundreds of invalid data messages.
Replace the text NA in the numeric values with a . to read missing values correctly.
I get this error from your FCMP procedure code, do you?
ERROR: An exception has been encountered.
Please contact technical support and provide them with the following traceback information:
The SAS task name is [FCMP]
ERROR: Read Access Violation FCMP
Exception occurred at (3222DF2E)
Task Traceback
Address Frame (DBGHELP API Version 4.0 rev 5)
000000003222DF2E 000000003168D570 sascmpa:tkvercn1+0x11CEEE
0000000032242A01 000000003168D630 sascmpa:tkvercn1+0x1319C1
000000003223A215 000000003168D6C0 sascmpa:tkvercn1+0x1291D5
0000000032239249 000000003168D7C0 sascmpa:tkvercn1+0x128209
000000003223A3C2 000000003168D850 sascmpa:tkvercn1+0x129382
00000000322391A0 000000003168D950 sascmpa:tkvercn1+0x128160
000000003223A3C2 000000003168D9E0 sascmpa:tkvercn1+0x129382
00000000322391A0 000000003168DAE0 sascmpa:tkvercn1+0x128160
000000003223811D 000000003168DB90 sascmpa:tkvercn1+0x1270DD
0000000032237C99 000000003168E0C0 sascmpa:tkvercn1+0x126C59
000000003220A757 000000003168E140 sascmpa:tkvercn1+0xF9717
000000003220B765 000000003168E1D0 sascmpa:tkvercn1+0xFA725
0000000032209401 000000003168E280 sascmpa:tkvercn1+0xF83C1
0000000032207445 000000003168E370 sascmpa:tkvercn1+0xF6405
0000000032213E84 000000003168E460 sascmpa:tkvercn1+0x102E44
0000000032226212 000000003168E660 sascmpa:tkvercn1+0x1151D2
00000000322131AB 000000003168E810 sascmpa:tkvercn1+0x10216B
0000000032212BA1 000000003168E920 sascmpa:tkvercn1+0x101B61
000000003218744A 000000003168E9D0 sascmpa:tkvercn1+0x7640A
000000003218D7D3 000000003168EA20 sascmpa:tkvercn1+0x7C793
00000000321FBD51 000000003168EB10 sascmpa:tkvercn1+0xEAD11
0000000032185052 000000003168F560 sascmpa:tkvercn1+0x74012
0000000032183604 000000003168F6A0 sascmpa:tkvercn1+0x725C4
0000000030EB19B2 000000003168F6A8 sasfcmp:tkvercn1+0x972
000000000316946E 000000003168FBB8 sashost:Main+0x113BE
000000000316F2CD 000000003168FF20 sashost:Main+0x1721D
00007FF923DD7034 000000003168FF28 KERNEL32:BaseThreadInitThunk+0x14
00007FF925C22651 000000003168FF58 ntdll:RtlUserThreadStart+0x21
NOTE: The SAS System stopped processing this step because of errors.
NOTE: PROCEDURE FCMP used (Total process time):
real time 0.50 seconds
cpu time 0.25 seconds
If you get such a message it often means that you have some issue that is so severe that SAS didn't write a syntax check for it.
Strongly suggest that you use replace all the addition in the FCMP code with the SUM function. You have so many missing values you are extremely likely to get missing values for one or more of the elements of any of those C variables. Which will mean the C variable is missing unless that is your intent.
Hi @MAC1430,
I think the reason for the "Read Access Violation" exception is that you're using array references B[0] (i.e., with index 0) in the definition of function h. Unlike arrays in a DATA step, arrays in PROC FCMP are always 1-based (see the remark about "lower-bound specifications" in the documentation), so the array indices of array B cannot match the corresponding variable "subscripts" (starting at 0). But you can define the array B so that B[1]=B0, B[2]=B1 and so on. This is exactly what your ARRAY statement
array B[*] B0-B11;
later in the DATA steps does.
The correction is a simple shift (by +1) of the indices i of the array references B[i] in the definition of function h:
c11= 11*p-B[1]*(F[1]+1)+B[2];
c10= -10*F[1]*B[1]-F[2]*B[2]-10*B[1]+9*B[2]+B[3]+55*p;
c9= -45*B[1]*(F[1]+1)+B[2]*(36-9*F[2])+B[3]*(8-F[3])+165*p+B[4];
c8= -120*B[1]*(F[1]+1)+B[2]*(84-36*F[2])+B[3]*(28-8*F[3])+330*p+(7-F[4])*B[4]+B[5];
c7= -210*B[1]*(F[1]+1)+B[2]*(126-84*F[2])+B[3]*(56-28*F[3])+B[5]*(6-F[5])+462*p+(21-7*F[4])*B[4]+B[6];
c6= -252*B[1]*(F[1]+1)+B[2]*(126-126*F[2])+B[3]*(70-56*F[3])+B[6]*(5-F[6])+B[5]*(15-6*F[5])+462*p+(35-21*F[4])*B[4]+B[7];
c5= -210*B[1]*(F[1]+1)+B[2]*(84-126*F[2])+B[3]*(56-70*F[3])+B[5]*(20-15*F[5])+B[6]*(10-5*F[6])+B[7]*(4-F[7])+330*p+35*B[4]*(1-F[4])+B[8];
c4= -120*B[1]*(F[1]+1)+B[2]*(36-84*F[2])+B[3]*(28-56*F[3])+B[5]*(15-20*F[5])+B[6]*(10-10*F[6])+B[7]*(6-4*F[7])+B[8]*(3-F[8])+165*p+B[4]*(21-35*F[4])+B[9];
c3= -45*B[1]*(F[1]+1)+B[2]*(9-36*F[2])+B[3]*(8-28*F[3])+B[5]*(6-15*F[5])+B[6]*(5-10*F[6])+B[7]*(4-6*F[7])+3*B[8]*(1-F[8])+B[9]*(2-F[9])+55*p+7*B[4]*(1-3*F[4])+B[10];
c2= -10*B[1]*(F[1]+1)+B[2]*(1-9*F[2])+B[3]*(1-8*F[3])+B[5]*(1-6*F[5])+B[6]*(1-5*F[6])+B[7]*(1-4*F[7])+B[8]*(1-3*F[8])+B[9]*(1-2*F[9])+B[10]*(1-F[10])+11*p+B[4]*(1-7*F[4])+B[11];
c1= -F[1]*B[1]-F[2]*B[2]-F[3]*B[3]-F[5]*B[5]-F[6]*B[6]-F[7]*B[7]-F[8]*B[8]-F[9]*B[9]-F[10]*B[10]-F[11]*B[11]-B[1]+p-F[4]*B[4]+B[12];
c0= -F[12]*B[12];
The original function has poles at r=-1 and r=0. Therefore, the IF condition in the definition of function xrate2 should be extended (cf. the solution in your earlier thread where the function had poles at r=-1 and r=g):
if min(abs(1+r),abs(r))<1e-4 then r=.; /* avoid poles of the original function */
Of course, if solutions like r=0.00009 are considered valid and useful, you should modify the criterion correspondingly (e.g., use a smaller upper bound like 1e-5).
After the above changes your code runs without issues (except for the trivial "Invalid data" notes in the creation of your sample data, as ballardw has already mentioned). The comment after the definition of array init should now read
/* Up to 12 roots are possible theoretically. */
... although for your current sample data at most one solution is found. Only 89 observations have non-missing values for all parameters. Among these, only 3 have a solution in variable r1 of dataset WANT. There are cases where r=0 or values very close to zero would be solutions if such were admissible (example: stock='BE0003810273' & year=2016). The good news is that those three solutions pass the check shown below:
data sel;
set want;
where r1>.;
run;
proc fcmp outlib=work.funcs.test;
function m(r, p, B[*], F[*]);
u=1+r;
s1=p-B[1];
do t=1 to 11;
s1 = s1 - (F[t]-r)/u**t*B[t];
end;
z = s1 - (F[t]-r)/r/u**11*B[12];
return(z);
endfunc;
run;
data check;
set sel;
array B[*] B0-B11;
array F[*] F1-F12;
diff=m(r1, p, B, F);
run;
proc print data=check;
var stock year r1 diff;
run;
Result:
Obs stock year r1 diff
1 AT0000606306 2014 0.67999 -3.4596E-8
2 BE0003565737 2009 0.59515 6.96638E-11
3 BE0974264930 2017 0.01498 1.878618E-9
I've checked one of the 89−3=86 cases with no solutions (in the interval (0, 1)) -- stock='AT0000606306' & year=2012 -- and found that the polynomial in this case has indeed no root in (0, 1), only one negative root (-0.006...) and one >10. You may want to check a few more of these cases, but I see no indications that the results might be incorrect. (I haven't checked the definitions of c0, c1, etc. in detail, though. At least the results in dataset CHECK are consistent with the assumption that you did a great job in calculating them.)
It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.