BookmarkSubscribeRSS Feed
rj_missionbeach
Fluorite | Level 6

Rob has generously helped me out in getting my optmodel code running the past month.

 

Everything seems to be working well, except in a few cases, I get strange solutions where, per the log, the model did not converge and "the least infeasible solution found is returned"

 

Consider the simplified example below - A and B are identical except that B's last two deposits are each one day later. In the case of A, the model appears to converge although totalinterest and total_interest differ by a fair amount (which I'm good with). The key is that EodBalance[t] = EodBalance[t-1] + Interest[t] + deposit[t] and EodBalance is always ge 0.

 

In the case of B, however, (i)  EodBalance[t] is not equal to EodBalance[t-1] + Interest[t] + deposit[t] and (ii) EodBalance is not always ge 0. For example, the EOD balance is decreasing for the first 47 days (when it should be increasing given the initial balance is gt 0) and turns negative on day 40. It does generate total_interest close to totalinterest, but daily interest is no longer the difference between the daily eodbalances.

 

So two questions:

 

1. In case B, I'd like to have a solution analogous to the case A solution (an initial balance of 5424.30 would meet the constraints and generate totalinterest of 165.6156). Can I force SAS to find that solution (which I realize is not really a solution as total_interest differs from totalinterest by quite a bit, that that also is true for case A).

 

2. If I can't force SAS to keep the constraints in case B, can I add some sort of indicator to my output that identifies this solution as not meeting the constraints?

 

Thanks for your help.

options nocenter;
proc datasets kill;

data a1; input id $ count date :yymmdd10. deposit; /*this is investor id, a count variable (that goes from 1-90 for each investor), and dates of investors' deposits (positive value) or withdrawals (negative values*/
format date yymmdd10.;
datalines;
A	1	2022-04-14	0.00
A	2	2022-04-15	0.00
A	3	2022-04-16	0.00
A	4	2022-04-17	0.00
A	5	2022-04-18	0.00
A	6	2022-04-19	0.00
A	7	2022-04-20	0.00
A	8	2022-04-21	0.00
A	9	2022-04-22	0.00
A	10	2022-04-23	0.00
A	11	2022-04-24	0.00
A	12	2022-04-25	0.00
A	13	2022-04-26	0.00
A	14	2022-04-27	0.00
A	15	2022-04-28	0.00
A	16	2022-04-29	0.00
A	17	2022-04-30	0.00
A	18	2022-05-01	0.00
A	19	2022-05-02	0.00
A	20	2022-05-03	0.00
A	21	2022-05-04	0.00
A	22	2022-05-05	0.00
A	23	2022-05-06	0.00
A	24	2022-05-07	0.00
A	25	2022-05-08	0.00
A	26	2022-05-09	0.00
A	27	2022-05-10	0.00
A	28	2022-05-11	0.00
A	29	2022-05-12	0.00
A	30	2022-05-13	0.00
A	31	2022-05-14	0.00
A	32	2022-05-15	0.00
A	33	2022-05-16	0.00
A	34	2022-05-17	0.00
A	35	2022-05-18	0.00
A	36	2022-05-19	0.00
A	37	2022-05-20	0.00
A	38	2022-05-21	0.00
A	39	2022-05-22	0.00
A	40	2022-05-23	0.00
A	41	2022-05-24	0.00
A	42	2022-05-25	0.00
A	43	2022-05-26	0.00
A	44	2022-05-27	0.00
A	45	2022-05-28	0.00
A	46	2022-05-29	0.00
A	47	2022-05-30	0.00
A	48	2022-05-31	19837.527273
A	49	2022-06-01	0.00
A	50	2022-06-02	0.00
A	51	2022-06-03	0.00
A	52	2022-06-04	0.00
A	53	2022-06-05	48686.54922
A	54	2022-06-06	0.00
A	55	2022-06-07	0.00
A	56	2022-06-08	0.00
A	57	2022-06-09	0.00
A	58	2022-06-10	0.00
A	59	2022-06-11	0.00
A	60	2022-06-12	-74114.00
A	61	2022-06-13	0.00
A	62	2022-06-14	0.00
A	63	2022-06-15	0.00
A	64	2022-06-16	0.00
A	65	2022-06-17	0.00
A	66	2022-06-18	0.00
A	67	2022-06-19	0.00
A	68	2022-06-20	0.00
A	69	2022-06-21	0.00
A	70	2022-06-22	0.00
A	71	2022-06-23	0.00
A	72	2022-06-24	0.00
A	73	2022-06-25	0.00
A	74	2022-06-26	0.00
A	75	2022-06-27	0.00
A	76	2022-06-28	0.00
A	77	2022-06-29	0.00
A	78	2022-06-30	0.00
A	79	2022-07-01	0.00
A	80	2022-07-02	0.00
A	81	2022-07-03	0.00
A	82	2022-07-04	0.00
A	83	2022-07-05	0.00
A	84	2022-07-06	0.00
A	85	2022-07-07	0.00
A	86	2022-07-08	0.00
A	87	2022-07-09	0.00
A	88	2022-07-10	0.00
A	89	2022-07-11	0.00
A	90	2022-07-12	0.00
A	91	2022-07-13	0.00
B	1	2022-04-14	0.00
B	2	2022-04-15	0.00
B	3	2022-04-16	0.00
B	4	2022-04-17	0.00
B	5	2022-04-18	0.00
B	6	2022-04-19	0.00
B	7	2022-04-20	0.00
B	8	2022-04-21	0.00
B	9	2022-04-22	0.00
B	10	2022-04-23	0.00
B	11	2022-04-24	0.00
B	12	2022-04-25	0.00
B	13	2022-04-26	0.00
B	14	2022-04-27	0.00
B	15	2022-04-28	0.00
B	16	2022-04-29	0.00
B	17	2022-04-30	0.00
B	18	2022-05-01	0.00
B	19	2022-05-02	0.00
B	20	2022-05-03	0.00
B	21	2022-05-04	0.00
B	22	2022-05-05	0.00
B	23	2022-05-06	0.00
B	24	2022-05-07	0.00
B	25	2022-05-08	0.00
B	26	2022-05-09	0.00
B	27	2022-05-10	0.00
B	28	2022-05-11	0.00
B	29	2022-05-12	0.00
B	30	2022-05-13	0.00
B	31	2022-05-14	0.00
B	32	2022-05-15	0.00
B	33	2022-05-16	0.00
B	34	2022-05-17	0.00
B	35	2022-05-18	0.00
B	36	2022-05-19	0.00
B	37	2022-05-20	0.00
B	38	2022-05-21	0.00
B	39	2022-05-22	0.00
B	40	2022-05-23	0.00
B	41	2022-05-24	0.00
B	42	2022-05-25	0.00
B	43	2022-05-26	0.00
B	44	2022-05-27	0.00
B	45	2022-05-28	0.00
B	46	2022-05-29	0.00
B	47	2022-05-30	0.00
B	48	2022-05-31	19837.527273
B	49	2022-06-01	0.00
B	50	2022-06-02	0.00
B	51	2022-06-03	0.00
B	52	2022-06-04	48686.54922
B	53	2022-06-05	0.00
B	54	2022-06-06	0.00
B	55	2022-06-07	0.00
B	56	2022-06-08	0.00
B	57	2022-06-09	0.00
B	58	2022-06-10	0.00
B	59	2022-06-11	-74114.00
B	60	2022-06-12	0.00
B	61	2022-06-13	0.00
B	62	2022-06-14	0.00
B	63	2022-06-15	0.00
B	64	2022-06-16	0.00
B	65	2022-06-17	0.00
B	66	2022-06-18	0.00
B	67	2022-06-19	0.00
B	68	2022-06-20	0.00
B	69	2022-06-21	0.00
B	70	2022-06-22	0.00
B	71	2022-06-23	0.00
B	72	2022-06-24	0.00
B	73	2022-06-25	0.00
B	74	2022-06-26	0.00
B	75	2022-06-27	0.00
B	76	2022-06-28	0.00
B	77	2022-06-29	0.00
B	78	2022-06-30	0.00
B	79	2022-07-01	0.00
B	80	2022-07-02	0.00
B	81	2022-07-03	0.00
B	82	2022-07-04	0.00
B	83	2022-07-05	0.00
B	84	2022-07-06	0.00
B	85	2022-07-07	0.00
B	86	2022-07-08	0.00
B	87	2022-07-09	0.00
B	88	2022-07-10	0.00
B	89	2022-07-11	0.00
B	90	2022-07-12	0.00
B	91	2022-07-13	0.00

;
proc sort; by id;
run;

data a2; input id $ total_interest; /*this is investor id and the total interest they earned over the 90 days*/
datalines;
A	107.063
B	107.063
;
proc sort; by id;



proc optmodel ;
   set DAYS;
   num deposit {DAYS} ;
   num date {DAYS};
   read data a1(where=(id='A')) into DAYS=[count] deposit date;
   DAYS = {0} union DAYS;

   num total_interest;
   read data a2(where=(id='A')) into total_interest;
   
   var EodBalance {DAYS} >= 0;
   impvar Interest {t in DAYS diff {0}} = EodBalance[t-1]*((1+0.071)**(1/365)-1);
   var TotalInterest >= 0;
   con TotalInterestCon:
      TotalInterest = sum {t in DAYS diff {0}} Interest[t];
      min absdif=abs(TotalInterest - total_interest);
   con BalanceCon {t in DAYS diff {0}}:
       EodBalance[t] = EodBalance[t-1] + Interest[t] + deposit[t];
   solve with nlp / opttol=1e-10;
   print TotalInterest total_interest;
   create data OutData_A from [count] EodBalance Interest deposit date totalInterest total_interest;
quit;
run;

data outdata_A; set outdata_A;
title 'optmodel solution for A';
options ps=120;
format eodBalance 15.10;
format interest 15.10;
format deposit 15.10;
format date yymmddn8.;
proc print;run;



proc optmodel ;
   set DAYS;
   num deposit {DAYS} ;
   num date {DAYS};
   read data a1(where=(id='B')) into DAYS=[count] deposit date;
   DAYS = {0} union DAYS;

   num total_interest;
   read data a2(where=(id='B')) into total_interest;
   
   var EodBalance {DAYS} >= 0;
   impvar Interest {t in DAYS diff {0}} = EodBalance[t-1]*((1+0.071)**(1/365)-1);
   var TotalInterest >= 0;
   con TotalInterestCon:
      TotalInterest = sum {t in DAYS diff {0}} Interest[t];
      min absdif=abs(TotalInterest - total_interest);
   con BalanceCon {t in DAYS diff {0}}:
       EodBalance[t] = EodBalance[t-1] + Interest[t] + deposit[t];
   solve with nlp / opttol=1e-10;
   print TotalInterest total_interest;
   create data OutData_B from [count] EodBalance Interest deposit date totalInterest total_interest;
quit;
run;

data outdata_B; set outdata_B;
options ps=120;
format eodBalance 15.10;
format interest 15.10;
format deposit 15.10;
format date yymmddn8.;
proc print;run;

 

 

2 REPLIES 2
RobPratt
SAS Super FREQ

I have a few suggestions.

 

An alternative approach to define TotalInterest is to instead use an implicit variable, saving one explicit variable and one constraint:

/*   var TotalInterest >= 0;*/
/*   con TotalInterestCon:*/
/*      TotalInterest = sum {t in DAYS diff {0}} Interest[t];*/
   impvar TotalInterest = sum {t in DAYS diff {0}} Interest[t];

Note that Interest[t] is already nonnegative, so TotalInterest will also be nonnegative without explicitly imposing a lower bound of 0.

 

To increase the likelihood of finding a feasible and optimal solution, you can use the MULTISTART option (alias MS) in the SOLVE statement:

   solve with nlp / opttol=1e-10 ms;

To detect the resulting solution status (OPTIMAL, INFEASIBLE, and so on), you can use the automatically generated _SOLUTION_STATUS_ parameter:

   put _solution_status_=;
rj_missionbeach
Fluorite | Level 6

Thanks Rob -

 

I think I'm getting there. For id=B, the solutions status is failed and both the original code and the new code yield (a) negative values for interest and (b) violate the equation EodBalance[t] = EodBalance[t-1] + Interest[t] + deposit[t] (when I don't put in the MS option).

 

In contrast, for id=A, the solution status is failed, but the results are consistent with the model (i.e. non-negative interest and EodBalance[t] = EodBalance[t-1] + Interest[t] + deposit[t].

 

Am I right in concluding that the difference is id=A generates a feasible (but failed solution) and id=B generates an infeasible (and also failed solution). That is, does "infeasible solution" mean it doesn't meet the parameters of the model (and thus, generates, for example, negative interest)?

 

Thank you for your help,

Rick

 

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

Multiple Linear Regression in SAS

Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 2 replies
  • 648 views
  • 0 likes
  • 2 in conversation