BookmarkSubscribeRSS Feed

Using Decisions (SID 5.4) in ESP 6.2

Started ‎04-21-2020 by
Modified ‎04-21-2020 by
Views 2,851

In my last post, I looked at extracting the generated DS2 code from SAS Intelligent Decisioning (SID). In this post, let's look at including that extracted code in SAS Event Stream Processing (ESP) projects. Our goals here are to:

  1. Better understand the differences between the "SID MAS" and the "ESP MAS".
  2. Better understand the SID to ESP DS2 conversion process for debugging and customization purposes.
  3. Better understand ESP MAS Maps.

Sample Decision

To aid us in understanding the whole process, we'll use a sample Decision as an example. The Decision supports auto auction bidding decisions and looks like this:

 

decision.png

Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.

 

The decision contains 2 rule sets, a predictive model, a custom DS2 code node, and some branching logic.

The Extracted Code

We looked at extracting SID code in my last post. Using the methods described there and the following parameters, codeTarget=MICROANALYTICS, lookupMode=inline, we get the following code for our decision:

 

/* Rule Set autoAuction version 1.0 generated on Tue Apr 07 09:41:24 EDT 2020 */

  package b_I2SJLZ3T2JBN7HYVMQV32RACEY  / inline;
      dcl package logger _logger('App.SASDCM');

        /* ---- RULE FIRED FLAG COUNT = 0 ---- */
            method setruleFiredBasePosition(integer newBasePosition); end;
            method setDecisionNodeID(varchar newDecisonNodeID); end;
            method enableRuleFiredRecording(); end;
            method disableRuleFiredRecording(); end;
            method initRuleFireHashes(); end;
            method getRuleFiredFlagCount() returns integer;  return 0; end;
            method recordRuleFired(
              in_out char    ruleFiredFlags,
              in_out integer rulesFiredForRecordCount,
              char(36)       this_rule_id);
            end;

    method execute(

         in_out double "Bid"
        ,in_out double "BlueBookPrice"
        ,in_out double "callOffice"
        ,in_out double "CurrentBid"
        ,in_out varchar "Make"
        ,in_out double "Miles"
        ,in_out varchar "Model"
        ,in_out double "OriginalInvoice"
        ,in_out double "OriginalMSRP"
        ,in_out varchar "state"
        ,in_out varchar "VIN"
        ,in_out double "Year");     

"Bid" = 0 ;
"callOffice" = 0 ;
         if (CurrentBid < BlueBookPrice) then do;
                   if ("Miles" < 50000.0) then do;
"Bid" = 1 ;
                   end;
         end;

  "end_546d28a3-341e-462f-a06b-0d4083d9e2d5":
         if ("state" = 'PA') then do;
"callOffice" = 1 ;
         end;

  "end_5b86f4d4-2055-4d96-995f-72f03bfba10c":

      rule_end:  /* label for rule RETURN action */
    end;

  endpackage; 
/* Custom Object californiaOverride retrieved on Tue Apr 07 09:41:29 EDT 2020 */ 
package c_EBWC2GAB6RB2BCVZ5DTMUWWRIU /inline;
    method execute(in_out double Bid,
                   in_out varchar(100) bidAction,
                   in_out char(2) state);
    if state = 'CA' then do;
       Bid = 1;
       bidAction = 'Buy anything from California!';
   end;
   end;
endpackage; 
/* Model Linear Regression (Pipeline 1) retrieved on Tue Apr 07 09:41:29 EDT 2020 */ 
ds2_options sas tkgmac scond=none;
 package m_3UWKJUHM5JBO5DSZSH34QQREHU/overwrite=yes;
 #_local _J_;
 #_local _I_;
 #_local _TEMP_;
 #_local _LINP_;
 #_local _BADVAL_;
 dcl double EM_PREDICTION;
 method score(char(17) "VIN", in_out double "EM_PREDICTION", in_out double 
"P_BlueBookPrice");
dcl char(17) _VIN_;
 dcl double _BETA_20892090_0_[15];
 dcl double _XROW_20892090_0_[15];
 _BETA_20892090_0_:=(8000, 1000, 0, 0, 4000, 0, 27000, 0, 0, 0, 20000, 
4000, 37000, 0, 0);
_BADVAL_ = 0.0;
_LINP_ = 0.0;
_TEMP_ = 0.0;
_I_ = 0.0;
_J_ = 0.0;
_VIN_ = LEFT(TRIM(put(VIN, $17.)));
do _I_ = 1.0 to 15.0;
_XROW_20892090_0_[_I_] = 0.0;
end;
_XROW_20892090_0_[1.0] = 1.0;
_TEMP_ = 1.0;
select (_VIN_);
when ('12345678901234560') _XROW_20892090_0_[2.0] = _TEMP_;
when ('12345678901234561') _XROW_20892090_0_[3.0] = _TEMP_;
when ('12345678901234562') _XROW_20892090_0_[4.0] = _TEMP_;
when ('12345678901234563') _XROW_20892090_0_[5.0] = _TEMP_;
when ('12345678901234564') _XROW_20892090_0_[6.0] = _TEMP_;
when ('12345678901234565') _XROW_20892090_0_[7.0] = _TEMP_;
when ('12345678901234566') _XROW_20892090_0_[8.0] = _TEMP_;
when ('12345678901234567') _XROW_20892090_0_[9.0] = _TEMP_;
when ('12345678901234568') _XROW_20892090_0_[10.0] = _TEMP_;
when ('12345678901234569') _XROW_20892090_0_[11.0] = _TEMP_;
when ('12345678901234571') _XROW_20892090_0_[12.0] = _TEMP_;
when ('12345678901234572') _XROW_20892090_0_[13.0] = _TEMP_;
when ('12345678901234573') _XROW_20892090_0_[14.0] = _TEMP_;
when ('12345678901234574') _XROW_20892090_0_[15.0] = _TEMP_;
otherwise do ;
_BADVAL_ = 1.0;
goto SKIP_20892090_0;
end;
end;
do _I_ = 1.0 to 15.0;
_LINP_ + _XROW_20892090_0_[_I_] * _BETA_20892090_0_[_I_];
end;
SKIP_20892090_0: if (_BADVAL_ = 0.0) & ^MISSING(_LINP_) then do ;
P_BLUEBOOKPRICE = _LINP_;
end;
 else do ;
_LINP_ = .;
P_BLUEBOOKPRICE = .;
end;
if "P_BLUEBOOKPRICE" = . then "P_BLUEBOOKPRICE" = 22142.857143;
EM_PREDICTION = "P_BLUEBOOKPRICE";
_return: ;
end;
 endpackage;

/* Rule Set autoAuctionNorthernStates version 1.0 generated on Tue Apr 07 09:41:24 EDT 2020 */

  package b_4FNY7JHNGNDU7OT7FNJPAP2F6A  / inline;
      dcl package logger _logger('App.SASDCM');

  dcl varchar(512)   lookup_value;

          dcl varchar(256)   lookup_key;
          dcl package hash lookup_0; /* lookup for $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K */
          dcl package hash lookup_1; /* lookup for $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K */

    /* for inline lookups we initialize hashes in the package constructor */    
     method b_4FNY7JHNGNDU7OT7FNJPAP2F6A();

            /* lookup: $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K */
            lookup_0 = _new_ [this] hash();
            lookup_0.keys([lookup_key]);
            lookup_0.data([lookup_value]);
            lookup_0.defineDone();
            lookup_0.clear();

            lookup_key = 'Buick';
            lookup_value = 'Buick';
            lookup_0.ref();
            lookup_key = 'Scion';
            lookup_value = 'Scion';
            lookup_0.ref();

            /* lookup: $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K */
            lookup_1 = _new_ [this] hash();
            lookup_1.keys([lookup_key]);
            lookup_1.data([lookup_value]);
            lookup_1.defineDone();
            lookup_1.clear();

            lookup_key = '0';
            lookup_value = 'Do NOT bid on this car!';
            lookup_1.ref();
            lookup_key = '1';
            lookup_value = 'Bid on the car!';
            lookup_1.ref();

            end;

    method getLookupValue() returns varchar(512);
      return lookup_value;
    end;

    /* $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K is the format name for Lookup bidCommands. */
    /* $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K is the format name for Lookup unwantedMakes. */

      method doLookupFind(varchar(256) lookupKey, varchar(100) lookupName) returns int;
        dcl integer rc;
        rc = 0;
        if ( missing(lookupKey) ) then do;
            lookup_value = '';
            return 1;
        end;
        lookup_key = strip(lookupkey);
        select(lookupName);
          when('$LPUEPB73F7BGHBEGN4YJK6AMEBQ_K.') rc = lookup_0.find(); 
          when('$LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K.') rc = lookup_1.find(); 
          otherwise rc = 1;
        end;
        if rc then lookup_value = '';
        return rc;
      end;

        /* ---- RULE FIRED FLAG COUNT = 0 ---- */
            method setruleFiredBasePosition(integer newBasePosition); end;
            method setDecisionNodeID(varchar newDecisonNodeID); end;
            method enableRuleFiredRecording(); end;
            method disableRuleFiredRecording(); end;
            method initRuleFireHashes(); end;
            method getRuleFiredFlagCount() returns integer;  return 0; end;
            method recordRuleFired(
              in_out char    ruleFiredFlags,
              in_out integer rulesFiredForRecordCount,
              char(36)       this_rule_id);
            end;

    method execute(

         in_out double "Bid"
        ,in_out varchar "bidAction"
        ,in_out double "BlueBookPrice"
        ,in_out double "callOffice"
        ,in_out double "CurrentBid"
        ,in_out varchar "Make"
        ,in_out double "Miles"
        ,in_out varchar "Model"
        ,in_out double "OriginalInvoice"
        ,in_out double "OriginalMSRP"
        ,in_out varchar "state"
        ,in_out varchar "VIN"
        ,in_out double "Year");     

"Bid" = 0 ;
         if (0 = doLookupFind("Make",'$LPUEPB73F7BGHBEGN4YJK6AMEBQ_K.')) then do;
                   if (CurrentBid < (0.9*BlueBookPrice)) then do;
"Bid" = 1 ;
                   end;
         end;

  "end_d71b5106-5b5d-4b44-8cee-a479673bd1c4":
if(0 = doLookupFind(Bid,'$LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K.')) then;"bidAction" = getLookupValue();;

      rule_end:  /* label for rule RETURN action */
    end;

      method term();
      if (null(lookup_0) = 0) then lookup_0.clear();
      if (null(lookup_1) = 0) then lookup_1.clear();
      end;

  endpackage; 

/* Decision autoAuction version 1.0 generated on Tue Apr 07 09:41:29 EDT 2020  */
  package Decision_60e45dfc_ce58_4143_896  / inline;
    dcl package logger logr_perf('App.tk.SID.Perf');
    dcl double timer_start;
    dcl double elapsed_time;
    dcl double total_timer_start;
    dcl double total_elapsed_time;

    dcl package b_I2SJLZ3T2JBN7HYVMQV32RACEY ruleset347();    dcl package c_EBWC2GAB6RB2BCVZ5DTMUWWRIU customobject348();    dcl package m_3UWKJUHM5JBO5DSZSH34QQREHU model345();    dcl package b_4FNY7JHNGNDU7OT7FNJPAP2F6A ruleset346();

    method init();
    end;

                method execute_internal(
       in_out double "BlueBookPrice"
      ,in_out double "CurrentBid"
      ,in_out varchar(20) "Make"
      ,in_out double "Miles"
      ,in_out varchar(20) "Model"
      ,in_out double "OriginalInvoice"
      ,in_out double "OriginalMSRP"
      ,in_out varchar(17) "VIN"
      ,in_out double "Year"
      ,in_out varchar(2) "state"
      ,in_out double "Bid"
      ,in_out double "P_BlueBookPrice"
      ,in_out varchar "bidAction"
      ,in_out double "callOffice"

      ,in_out integer "_filter_"

    )
    ;    
       dcl integer dg_filter_;

       dcl integer _masRC;

        total_timer_start = datetime();

        timer_start = datetime();
        model345.score("VIN", "BlueBookPrice", "P_BlueBookPrice");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: Linear Regression (Pipeline 1), Node ID: dd2ca4d0-ecea-42ee-8e59-91f7c842243d, NODE DURATION: ' .. elapsed_time);
        if ("_filter_" = 0) then return;

/*model code*/;
        /*begin branch node*/
        if ( "state" eq 'PA' )
         then do;

        timer_start = datetime();
             ruleset346.execute("Bid", "bidAction", "BlueBookPrice", "callOffice", "CurrentBid", "Make", "Miles", "Model", "OriginalInvoice", "OriginalMSRP", "state", "VIN", "Year");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: autoAuctionNorthernStates, Node ID: e15b8fa4-ed33-474f-ba7f-2b52f03f45f0, NODE DURATION: ' .. elapsed_time);
             if ("_filter_" = 0) then return;

        end;

        else do;

        timer_start = datetime();
             ruleset347.execute("Bid", "BlueBookPrice", "callOffice", "CurrentBid", "Make", "Miles", "Model", "OriginalInvoice", "OriginalMSRP", "state", "VIN", "Year");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: autoAuction, Node ID: 46a495e7-73d2-42df-9f15-642bbd440226, NODE DURATION: ' .. elapsed_time);
             if ("_filter_" = 0) then return;

        end;

        /*end branch node*/    

        timer_start = datetime();
        customobject348.execute("Bid", "bidAction", "state");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: californiaOverride, Node ID: 206c2d18-01f4-43a0-8ab9-e8e6ca5ad145, NODE DURATION: ' .. elapsed_time);
        if ("_filter_" = 0) then return;

        total_elapsed_time = datetime() - total_timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, TOTAL DURATION: ' .. total_elapsed_time);

     end;

    method execute(
       double "BlueBookPrice_"
      ,double "CurrentBid_"
      ,varchar(20) "Make_"
      ,double "Miles_"
      ,varchar(20) "Model_"
      ,double "OriginalInvoice_"
      ,double "OriginalMSRP_"
      ,varchar(17) "VIN_"
      ,double "Year_"
      ,varchar(2) "state_"
      ,in_out double "Bid"
      ,in_out double "BlueBookPrice"
      ,in_out double "CurrentBid"
      ,in_out varchar "Make"
      ,in_out double "Miles"
      ,in_out varchar "Model"
      ,in_out double "OriginalInvoice"
      ,in_out double "OriginalMSRP"
      ,in_out double "P_BlueBookPrice"
      ,in_out varchar "VIN"
      ,in_out double "Year"
      ,in_out varchar "bidAction"
      ,in_out double "callOffice"
      ,in_out varchar "state"
);

      dcl integer _filter_;
              "BlueBookPrice" = "BlueBookPrice_";
      "CurrentBid" = "CurrentBid_";
      "Make" = "Make_";
      "Miles" = "Miles_";
      "Model" = "Model_";
      "OriginalInvoice" = "OriginalInvoice_";
      "OriginalMSRP" = "OriginalMSRP_";
      "VIN" = "VIN_";
      "Year" = "Year_";
      "state" = "state_";

              execute_internal(
          "BlueBookPrice"
         ,"CurrentBid"
         ,"Make"
         ,"Miles"
         ,"Model"
         ,"OriginalInvoice"
         ,"OriginalMSRP"
         ,"VIN"
         ,"Year"
         ,"state"
         ,"Bid"
         ,"P_BlueBookPrice"
         ,"bidAction"
         ,"callOffice"
      ,"_filter_"      );

    end;
        endpackage;

 

The code is all DS2 and is organized into 5 packages -- 1 for each rule set (2), 1 for the custom DS2 code node, 1 for the predictive model, and 1 for the decision itself. The decision package calls the other packages and includes the branching logic. Also, one of the rule sets includes SID lookup tables.

 

Note that any generated SID lookup table code should utilize DS2 hash tables and not SAS formats. Setting the lookupMode=inline should take care of this but it's worth double checking. The other setting that applies here is the lookupStaticBinding parameter. For information on that, see this article on lookup tables. The generated lookup table code should include code that looks like this:

 

 

 

lookup_key = '0';
lookup_value = 'Do NOT bid on this car!';
lookup_1.ref();
lookup_key = '1';
lookup_value = 'Bid on the car!';
lookup_1.ref();

 

 

SID MAS vs ESP MAS

But why do we need to export the code at all? Don't both SID and ESP use the MAS to run DS2 code? Yes, they do. However the MAS is implemented very differently in the two applications. In support of SID, the MAS is deployed as a web application with a REST API. In support of ESP, the MAS is integrated directly into the ESP server in the form of shared object libraries. So, while "SID MAS" needs DS2 packages pushed to it using HTTP POST commands, "ESP MAS" needs the DS2 packages presented to it using ESP's XML coding language.

Getting our Extracted Code into ESP

ESP executes DS2 code via ESP MAS modules and ESP Calculate windows. However, ESP only allows one DS2 package per MAS Module. So, how do we call our 5 DS2 package code? With 5 MAS modules of course.

Put Each Package in its own File

We break the code into 5 separate packages -- 1 for each rule set, 1 for the custom DS2 code node, 1 for the predictive model, and 1 for the overall decision. Each package is put into its own file.

autoAuctionRuleSet.ds2:

/* Rule Set autoAuction version 1.0 generated on Tue Apr 07 09:41:24 EDT 2020 */

  package b_I2SJLZ3T2JBN7HYVMQV32RACEY  / inline;
      dcl package logger _logger('App.SASDCM');

        /* ---- RULE FIRED FLAG COUNT = 0 ---- */
            method setruleFiredBasePosition(integer newBasePosition); end;
            method setDecisionNodeID(varchar newDecisonNodeID); end;
            method enableRuleFiredRecording(); end;
            method disableRuleFiredRecording(); end;
            method initRuleFireHashes(); end;
            method getRuleFiredFlagCount() returns integer;  return 0; end;
            method recordRuleFired(
              in_out char    ruleFiredFlags,
              in_out integer rulesFiredForRecordCount,
              char(36)       this_rule_id);
            end;

    method execute(

         in_out double "Bid"
        ,in_out double "BlueBookPrice"
        ,in_out double "callOffice"
        ,in_out double "CurrentBid"
        ,in_out varchar "Make"
        ,in_out double "Miles"
        ,in_out varchar "Model"
        ,in_out double "OriginalInvoice"
        ,in_out double "OriginalMSRP"
        ,in_out varchar "state"
        ,in_out varchar "VIN"
        ,in_out double "Year");     

"Bid" = 0 ;
"callOffice" = 0 ;
         if (CurrentBid < BlueBookPrice) then do;
                   if ("Miles" < 50000.0) then do;
"Bid" = 1 ;
                   end;
         end;

  "end_546d28a3-341e-462f-a06b-0d4083d9e2d5":
         if ("state" = 'PA') then do;
"callOffice" = 1 ;
         end;

  "end_5b86f4d4-2055-4d96-995f-72f03bfba10c":

      rule_end:  /* label for rule RETURN action */
    end;

  endpackage; 

 

autoAuctionNorthernStatesRuleSet.ds2:

/* Rule Set autoAuctionNorthernStates version 1.0 generated on Tue Apr 07 09:41:24 EDT 2020 */

  package b_4FNY7JHNGNDU7OT7FNJPAP2F6A  / inline;
      dcl package logger _logger('App.SASDCM');

  dcl varchar(512)   lookup_value;

          dcl varchar(256)   lookup_key;
          dcl package hash lookup_0; /* lookup for $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K */
          dcl package hash lookup_1; /* lookup for $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K */

    /* for inline lookups we initialize hashes in the package constructor */    
     method b_4FNY7JHNGNDU7OT7FNJPAP2F6A();

            /* lookup: $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K */
            lookup_0 = _new_ [this] hash();
            lookup_0.keys([lookup_key]);
            lookup_0.data([lookup_value]);
            lookup_0.defineDone();
            lookup_0.clear();

            lookup_key = 'Buick';
            lookup_value = 'Buick';
            lookup_0.ref();
            lookup_key = 'Scion';
            lookup_value = 'Scion';
            lookup_0.ref();

            /* lookup: $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K */
            lookup_1 = _new_ [this] hash();
            lookup_1.keys([lookup_key]);
            lookup_1.data([lookup_value]);
            lookup_1.defineDone();
            lookup_1.clear();

            lookup_key = '0';
            lookup_value = 'Do NOT bid on this car!';
            lookup_1.ref();
            lookup_key = '1';
            lookup_value = 'Bid on the car!';
            lookup_1.ref();

            end;

    method getLookupValue() returns varchar(512);
      return lookup_value;
    end;

    /* $LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K is the format name for Lookup bidCommands. */
    /* $LPUEPB73F7BGHBEGN4YJK6AMEBQ_K is the format name for Lookup unwantedMakes. */

      method doLookupFind(varchar(256) lookupKey, varchar(100) lookupName) returns int;
        dcl integer rc;
        rc = 0;
        if ( missing(lookupKey) ) then do;
            lookup_value = '';
            return 1;
        end;
        lookup_key = strip(lookupkey);
        select(lookupName);
          when('$LPUEPB73F7BGHBEGN4YJK6AMEBQ_K.') rc = lookup_0.find(); 
          when('$LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K.') rc = lookup_1.find(); 
          otherwise rc = 1;
        end;
        if rc then lookup_value = '';
        return rc;
      end;

        /* ---- RULE FIRED FLAG COUNT = 0 ---- */
            method setruleFiredBasePosition(integer newBasePosition); end;
            method setDecisionNodeID(varchar newDecisonNodeID); end;
            method enableRuleFiredRecording(); end;
            method disableRuleFiredRecording(); end;
            method initRuleFireHashes(); end;
            method getRuleFiredFlagCount() returns integer;  return 0; end;
            method recordRuleFired(
              in_out char    ruleFiredFlags,
              in_out integer rulesFiredForRecordCount,
              char(36)       this_rule_id);
            end;

    method execute(

         in_out double "Bid"
        ,in_out varchar "bidAction"
        ,in_out double "BlueBookPrice"
        ,in_out double "callOffice"
        ,in_out double "CurrentBid"
        ,in_out varchar "Make"
        ,in_out double "Miles"
        ,in_out varchar "Model"
        ,in_out double "OriginalInvoice"
        ,in_out double "OriginalMSRP"
        ,in_out varchar "state"
        ,in_out varchar "VIN"
        ,in_out double "Year");     

"Bid" = 0 ;
         if (0 = doLookupFind("Make",'$LPUEPB73F7BGHBEGN4YJK6AMEBQ_K.')) then do;
                   if (CurrentBid < (0.9*BlueBookPrice)) then do;
"Bid" = 1 ;
                   end;
         end;

  "end_d71b5106-5b5d-4b44-8cee-a479673bd1c4":
if(0 = doLookupFind(Bid,'$LYJBKPM4WRFA6ZP5JX2GVLYNDX4_K.')) then;"bidAction" = getLookupValue();;

      rule_end:  /* label for rule RETURN action */
    end;

      method term();
      if (null(lookup_0) = 0) then lookup_0.clear();
      if (null(lookup_1) = 0) then lookup_1.clear();
      end;

  endpackage; 

 

LinearRegression.ds2:

/* Model Linear Regression (Pipeline 1) retrieved on Tue Apr 07 09:41:29 EDT 2020 */ 
ds2_options sas tkgmac scond=none;
 package m_3UWKJUHM5JBO5DSZSH34QQREHU/overwrite=yes;
 #_local _J_;
 #_local _I_;
 #_local _TEMP_;
 #_local _LINP_;
 #_local _BADVAL_;
 dcl double EM_PREDICTION;
 method score(char(17) "VIN", in_out double "EM_PREDICTION", in_out double 
"P_BlueBookPrice");
dcl char(17) _VIN_;
 dcl double _BETA_20892090_0_[15];
 dcl double _XROW_20892090_0_[15];
 _BETA_20892090_0_:=(8000, 1000, 0, 0, 4000, 0, 27000, 0, 0, 0, 20000, 
4000, 37000, 0, 0);
_BADVAL_ = 0.0;
_LINP_ = 0.0;
_TEMP_ = 0.0;
_I_ = 0.0;
_J_ = 0.0;
_VIN_ = LEFT(TRIM(put(VIN, $17.)));
do _I_ = 1.0 to 15.0;
_XROW_20892090_0_[_I_] = 0.0;
end;
_XROW_20892090_0_[1.0] = 1.0;
_TEMP_ = 1.0;
select (_VIN_);
when ('12345678901234560') _XROW_20892090_0_[2.0] = _TEMP_;
when ('12345678901234561') _XROW_20892090_0_[3.0] = _TEMP_;
when ('12345678901234562') _XROW_20892090_0_[4.0] = _TEMP_;
when ('12345678901234563') _XROW_20892090_0_[5.0] = _TEMP_;
when ('12345678901234564') _XROW_20892090_0_[6.0] = _TEMP_;
when ('12345678901234565') _XROW_20892090_0_[7.0] = _TEMP_;
when ('12345678901234566') _XROW_20892090_0_[8.0] = _TEMP_;
when ('12345678901234567') _XROW_20892090_0_[9.0] = _TEMP_;
when ('12345678901234568') _XROW_20892090_0_[10.0] = _TEMP_;
when ('12345678901234569') _XROW_20892090_0_[11.0] = _TEMP_;
when ('12345678901234571') _XROW_20892090_0_[12.0] = _TEMP_;
when ('12345678901234572') _XROW_20892090_0_[13.0] = _TEMP_;
when ('12345678901234573') _XROW_20892090_0_[14.0] = _TEMP_;
when ('12345678901234574') _XROW_20892090_0_[15.0] = _TEMP_;
otherwise do ;
_BADVAL_ = 1.0;
goto SKIP_20892090_0;
end;
end;
do _I_ = 1.0 to 15.0;
_LINP_ + _XROW_20892090_0_[_I_] * _BETA_20892090_0_[_I_];
end;
SKIP_20892090_0: if (_BADVAL_ = 0.0) & ^MISSING(_LINP_) then do ;
P_BLUEBOOKPRICE = _LINP_;
end;
 else do ;
_LINP_ = .;
P_BLUEBOOKPRICE = .;
end;
if "P_BLUEBOOKPRICE" = . then "P_BLUEBOOKPRICE" = 22142.857143;
EM_PREDICTION = "P_BLUEBOOKPRICE";
_return: ;
end;
 endpackage;

 

californiaOverride.ds2:

/* Custom Object californiaOverride retrieved on Tue Apr 07 09:41:29 EDT 2020 */ 
package c_EBWC2GAB6RB2BCVZ5DTMUWWRIU /inline;
    method execute(in_out double Bid,
                   in_out varchar(100) bidAction,
                   in_out char(2) state);
    if state = 'CA' then do;
       Bid = 1;
       bidAction = 'Buy anything from California!';
   end;
   end;
endpackage; 

 

autoAuctionDecision.ds2:

/* Decision autoAuction version 1.0 generated on Tue Apr 07 09:41:29 EDT 2020  */
  package Decision_60e45dfc_ce58_4143_896  / inline;
    dcl package logger logr_perf('App.tk.SID.Perf');
    dcl double timer_start;
    dcl double elapsed_time;
    dcl double total_timer_start;
    dcl double total_elapsed_time;

    dcl package b_I2SJLZ3T2JBN7HYVMQV32RACEY ruleset347();    dcl package c_EBWC2GAB6RB2BCVZ5DTMUWWRIU customobject348();    dcl package m_3UWKJUHM5JBO5DSZSH34QQREHU model345();    dcl package b_4FNY7JHNGNDU7OT7FNJPAP2F6A ruleset346();

    method init();
    end;

                method execute_internal(
       in_out double "BlueBookPrice"
      ,in_out double "CurrentBid"
      ,in_out varchar(20) "Make"
      ,in_out double "Miles"
      ,in_out varchar(20) "Model"
      ,in_out double "OriginalInvoice"
      ,in_out double "OriginalMSRP"
      ,in_out varchar(17) "VIN"
      ,in_out double "Year"
      ,in_out varchar(2) "state"
      ,in_out double "Bid"
      ,in_out double "P_BlueBookPrice"
      ,in_out varchar "bidAction"
      ,in_out double "callOffice"

      ,in_out integer "_filter_"

    )
    ;    
       dcl integer dg_filter_;

       dcl integer _masRC;

        total_timer_start = datetime();

        timer_start = datetime();
        model345.score("VIN", "BlueBookPrice", "P_BlueBookPrice");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: Linear Regression (Pipeline 1), Node ID: dd2ca4d0-ecea-42ee-8e59-91f7c842243d, NODE DURATION: ' .. elapsed_time);
        if ("_filter_" = 0) then return;

/*model code*/;
        /*begin branch node*/
        if ( "state" eq 'PA' )
         then do;

        timer_start = datetime();
             ruleset346.execute("Bid", "bidAction", "BlueBookPrice", "callOffice", "CurrentBid", "Make", "Miles", "Model", "OriginalInvoice", "OriginalMSRP", "state", "VIN", "Year");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: autoAuctionNorthernStates, Node ID: e15b8fa4-ed33-474f-ba7f-2b52f03f45f0, NODE DURATION: ' .. elapsed_time);
             if ("_filter_" = 0) then return;

        end;

        else do;

        timer_start = datetime();
             ruleset347.execute("Bid", "BlueBookPrice", "callOffice", "CurrentBid", "Make", "Miles", "Model", "OriginalInvoice", "OriginalMSRP", "state", "VIN", "Year");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: autoAuction, Node ID: 46a495e7-73d2-42df-9f15-642bbd440226, NODE DURATION: ' .. elapsed_time);
             if ("_filter_" = 0) then return;

        end;

        /*end branch node*/    

        timer_start = datetime();
        customobject348.execute("Bid", "bidAction", "state");
        elapsed_time = datetime() - timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, Node Name: californiaOverride, Node ID: 206c2d18-01f4-43a0-8ab9-e8e6ca5ad145, NODE DURATION: ' .. elapsed_time);
        if ("_filter_" = 0) then return;

        total_elapsed_time = datetime() - total_timer_start;
        logr_perf.log( 'd', 'Package Name: Decision_60e45dfc_ce58_4143_896, TOTAL DURATION: ' .. total_elapsed_time);

     end;

    method execute(
       double "BlueBookPrice_"
      ,double "CurrentBid_"
      ,varchar(20) "Make_"
      ,double "Miles_"
      ,varchar(20) "Model_"
      ,double "OriginalInvoice_"
      ,double "OriginalMSRP_"
      ,varchar(17) "VIN_"
      ,double "Year_"
      ,varchar(2) "state_"
      ,in_out double "Bid"
      ,in_out double "BlueBookPrice"
      ,in_out double "CurrentBid"
      ,in_out varchar "Make"
      ,in_out double "Miles"
      ,in_out varchar "Model"
      ,in_out double "OriginalInvoice"
      ,in_out double "OriginalMSRP"
      ,in_out double "P_BlueBookPrice"
      ,in_out varchar "VIN"
      ,in_out double "Year"
      ,in_out varchar "bidAction"
      ,in_out double "callOffice"
      ,in_out varchar "state"
);

      dcl integer _filter_;
              "BlueBookPrice" = "BlueBookPrice_";
      "CurrentBid" = "CurrentBid_";
      "Make" = "Make_";
      "Miles" = "Miles_";
      "Model" = "Model_";
      "OriginalInvoice" = "OriginalInvoice_";
      "OriginalMSRP" = "OriginalMSRP_";
      "VIN" = "VIN_";
      "Year" = "Year_";
      "state" = "state_";

              execute_internal(
          "BlueBookPrice"
         ,"CurrentBid"
         ,"Make"
         ,"Miles"
         ,"Model"
         ,"OriginalInvoice"
         ,"OriginalMSRP"
         ,"VIN"
         ,"Year"
         ,"state"
         ,"Bid"
         ,"P_BlueBookPrice"
         ,"bidAction"
         ,"callOffice"
      ,"_filter_"      );

    end;
        endpackage;

The files must be saved to a directory location accessible by the ESP server. For our example, we'll just use "/home".

Create Your ESP MAS Module Code

The MAS Module code is the ESP XML code required to register the 5 DS2 packages to ESP. It will ultimately become part of the ESP project code. Note that the code defines a "MAS Module" for each DS2 package, points the code tag to the package's file location, and sets the "func-name" (DS2 method) to execute.

 

 

 

<mas-modules>
 <mas-module module='autoAuctionRuleSet' language="ds2" func-names="execute">
    <code-file>/home/autoAuctionRuleSet.ds2</code-file>
 </mas-module>
 <mas-module module='autoAuctionNorthernStatesRuleSet' language="ds2" func-names="execute">
    <code-file>/home/autoAuctionNorthernStatesRuleSet.ds2</code-file>
 </mas-module>
 <mas-module module='californiaOverride' language="ds2" func-names="execute">
    <code-file>/home/californiaOverride.ds2</code-file>
 </mas-module>
 <mas-module module='linearRegression' language="ds2" func-names="execute">
    <code-file>/home/linearRegression.ds2</code-file>
 </mas-module>
 <mas-module module='autoAuctionDecision' language="ds2" func-names="execute">
    <code-file>/home/autoAuctionDecision.ds2</code-file>
 </mas-module>
</mas-modules>

 

 

 

Note: The decision is defined last, ensuring that it will be loaded last into ESP. This is necessary because it has dependencies on the other modules and they must exist before it is defined.

Create Your ESP Calculate Window MAS Map Code

The Calculate window maps the window input (SRCWin) to the decision MAS Module. The fields on the input are mapped to the Decision's EXECUTE method field definitions by name. So the Calculate window's input must provide every field required by the decision.

 

 

 

<window-calculate pubsub="true" name="Calculate1" algorithm="MAS">
 ........
 <mas-map>
  <window-map module="autoAuctionDecision" function="execute" revision="0" source="SRCWin"/>
 </mas-map>
</window-calculate>

 

 

Put it All Together

With your MAS modules defined and your calculate window MAS map created, integrate them with the rest of your ESP project and start testing. A basic project that simply applies the DS2 logic to streaming auto auction events would look like this:

 

<project name="SIDCodeTest" threads="1" pubsub="auto">
  <mas-modules>
     <mas-module module='autoAuctionRuleSet' language="ds2" func-names="execute">
        <code-file>/home/autoAuctionRuleSet.ds2</code-file>
     </mas-module>
     <mas-module module='autoAuctionNorthernStatesRuleSet' language="ds2" func-names="execute">
        <code-file>/home/autoAuctionNorthernStatesRuleSet.ds2</code-file>
     </mas-module>
     <mas-module module='californiaOverride' language="ds2" func-names="execute">
        <code-file>/home/californiaOverride.ds2</code-file>
     </mas-module>
     <mas-module module='linearRegression' language="ds2" func-names="execute">
        <code-file>/home/linearRegression.ds2</code-file>
     </mas-module>
     <mas-module module='autoAuctionDecision' language="ds2" func-names="execute">
        <code-file>/home/autoAuctionDecision.ds2</code-file>
     </mas-module>
  </mas-modules>
  <contqueries>
    <contquery name="cq1">
      <windows>
        <window-source name="autoAuctionSource" pubsub="true">
          <schema>
            <fields>
              <field name="Make_" type="string"/>
              <field name="Model_" type="string"/>
              <field name="state_" type="string"/>
              <field name="Year_" type="double"/>
              <field name="BlueBookPrice_" type="double"/>
              <field name="CurrentBid_" type="double"/>
              <field name="OriginalInvoice_" type="double"/>
              <field name="OriginalMSRP_" type="double"/>
              <field name="Miles_" type="double"/>
              <field name="VIN_" type="string" key="true"/>
            </fields>
          </schema>
          <connectors>
            <connector class='fs'>
              <properties>
                <property name='type'>pub</property>
                <property name='fstype'>csv</property>
                <property name='fsname'>auto.csv</property>
                <property name='transactional'>true</property>
                <property name='blocksize'>1</property>
                <property name='addcsvopcode'>true</property>
                <property name='addcsvflags'>normal</property>
              </properties>
            </connector>
          </connectors>
        </window-source>
        <window-calculate pubsub="true" name="Calculate1" algorithm="MAS">
          <schema>
            <fields>
              <field name="Bid" type="double"/>
              <field name="bidAction" type="string"/>
              <field name="callOffice" type="double"/>
              <field name="VIN" type="string" key="true"/>
              <field name="OriginalInvoice" type="double"/>
              <field name="OriginalMSRP" type="double"/>
              <field name="BlueBookPrice" type="double"/>
              <field name="Make" type="string"/>
              <field name="CurrentBid" type="double"/>
              <field name="Miles" type="double"/>
              <field name="Model" type="string"/>
              <field name="P_BlueBookPrice" type="double"/>
              <field name="Year" type="double"/>
              <field name="state" type="string"/>
            </fields>
          </schema>
          <mas-map>
            <window-map module="autoAuctionDecision" function="execute" revision="0" source="autoAuctionSource"/>
          </mas-map>
          <connectors>
            <connector class='fs'>
              <properties>
                <property name='type'>sub</property>
                <property name='fstype'>csv</property>
                <property name='fsname'>calc.csv</property>
                <property name='snapshot'>true</property>
              </properties>
            </connector>
          </connectors>
        </window-calculate>
      </windows>
      <edges>
        <edge source="autoAuctionSource" target="Calculate1" role="data"/>
      </edges>
    </contquery>
  </contqueries>
</project>

Further Considerations

  • Some minor code modifications may be required. In the example provided, the generated SID code changed the input variable names from how they were defined in the User Interface. Each variable included an underscore suffix, e.g. "originalInvoice_" and "BlueBookPrice_". This forced me to change the ESP input schema to match. Small modifications like this will likely be required to make SID code run in ESP.
  • The main decision DS2 package must be modified to include the following statement, "ds2_options sas;." This should be included at the beginning.
  • There are slight differences between what DS2 statements are supported in ESP versus SID. Please refer to the documentation for the latest details.
  • In the above example, we referenced our MAS module code files by location. A more formal way to manage MAS modules in ESP is with MAS module stores.
  • You can create the ESP MAS Modules, Calculate window, and ESP Project all in ESP Studio if desired.

Version history
Last update:
‎04-21-2020 10:27 AM
Updated by:
Contributors

SAS INNOVATE 2024

Innovate_SAS_Blue.png

Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.

If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website. 

Register now!

Free course: Data Literacy Essentials

Data Literacy is for all, even absolute beginners. Jump on board with this free e-learning  and boost your career prospects.

Get Started

Article Labels
Article Tags