Learning API example
This section contains a sample implementation of the ILearningInterface. Note that this implementation is just a sample and is not designed to be used in a production environment.
This example keeps track of accept and contact counts and uses the ratio of accept to contacts for a particular offer as the acceptance probability rate for the offer. Offers not presented get higher priority for recommending. Offers with at least one contact are be ordered based on descending acceptance probability rate.
In this example, all counts are kept in memory. This is not a realistic scenario as the runtime server will run out of memory. In a real production scenario, the counts should be persisted into a database.
package com.unicacorp.interact.samples.learning.v2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.unicacorp.interact.samples.learning.SampleOptimizer.MyOfferSorter;
import com.unicacorp.interact.treatment.optimization.IClientArgs;
import com.unicacorp.interact.treatment.optimization.IInteractSession;
import com.unicacorp.interact.treatment.optimization.ILearningConfig;
import com.unicacorp.interact.treatment.optimization.ILearningContext;
import com.unicacorp.interact.treatment.optimization.IOffer;
import com.unicacorp.interact.treatment.optimization.LearningException;
import com.unicacorp.interact.treatment.optimization.v2.ILearning;
import com.unicacorp.interact.treatment.optimization.v2.ITreatment;

/**
* This is a sample implementation of the learning optimizer.
* The interface ILearning may be found in the interact.jar library.
*
* To actually use this implementation, select ExternalLearning as the optimizationType in the offerServing node
* of the Interact application within the Platform configuration. Within the offerserving node there is also
* an External Learning config category - within there you must set the name of the class to this:
* com.unicacorp.interact.samples.learning.v2.SampleLearning. Please note however, this implementation is just a sample
* and was not designed to be used in a production environment.
*
*
* This example keeps track of accept and contact counts and uses the ratio of accept to contacts
* for a particular offer as the acceptance probability rate for the offer.
*
*
* Offers not presented will get higher priority for recommending.
* Offers with at least one contact will be ordered based on descending acceptance probability rate.
*
* Note: all counts are kept in memory. This is not a realistic scenario since you would run out of memory sooner or
* later. In a real production scenario, the counts should be persisted into a database.
*
*/

public class SampleLearning implements ILearning
{

// A map of offer ids to contact count for the offer id
private Map<Long,Integer> _offerToContactCount = new HashMap<Long, Integer>();

// A map of offer ids to contact count for the offer id
private Map<Long,Integer> _offerToAcceptCount = new HashMap<Long, Integer>();


/* (non-Javadoc)
* @see com.unicacorp.interact.treatment.optimization.v2.ILearning#initialize
* (com.unicacorp.interact.treatment.optimization.v2.ILearningConfig, boolean)
*/
public void initialize(ILearningConfig config, boolean debug) throws LearningException
{
// If any remote connections are required, this is a good place to initialize those connections as this
// method is called once at the start of the interact runtime webapp.
// This example does not have any remote connections and prints for debugging purposes that this method will
// be called
System.out.println("Calling initialize for SampleLearning");
}

/* (non-Javadoc)
* @see com.unicacorp.interact.treatment.optimization.v2.ILearning#reinitialize
* (com.unicacorp.interact.treatment.optimization.v2.ILearningConfig, boolean)
*/
public void reinitialize(ILearningConfig config, boolean debug) throws LearningException
{
// If an IC is deployed, this reinitialize method is called to allow the implementation to
// refresh any updated configuration settings
System.out.println("Calling reinitialize for SampleLearning");
}


/* (non-Javadoc)
* @see com.unicacorp.interact.treatment.optimization.v2.ILearning#logEvent
* (com.unicacorp.interact.treatment.optimization.v2.ILearningContext,
* com.unicacorp.interact.treatment.optimization.v2.IOffer,
* com.unicacorp.interact.treatment.optimization.v2.IClientArgs,
* com.unicacorp.interact.treatment.optimization.IInteractSession, boolean)
*/
public void logEvent(ILearningContext context, IOffer offer, IClientArgs clientArgs,
IInteractSession session, boolean debug) throws LearningException
{
System.out.println("Calling logEvent for SampleLearning");


if(context.getLearningContext()==ILearningContext.LOG_AS_CONTACT)
{
System.out.println("adding contact");

// Keep track of all contacts in memory
synchronized(_offerToAcceptCount)
{
Integer count = _offerToAcceptCount.get(offer.getOfferId());
if(count == null)
count = new Integer(1);
else
count++;
_offerToAcceptCount.put(offer.getOfferId(), ++count);
}

}
else if(context.getLearningContext()==ILearningContext.LOG_AS_ACCEPT)
{
System.out.println("adding accept");
// Keep track of all accept counts in memory by adding to the map
synchronized(_offerToAcceptCount)
{
Integer count = _offerToAcceptCount.get(offer.getOfferId());
if(count == null)
count = new Integer(1);
else
count++;
_offerToAcceptCount.put(offer.getOfferId(), ++count);
}
}

}

/* (non-Javadoc)
* @see com.unicacorp.interact.treatment.optimization.v2.ILearning#optimizeRecommendList
* (java.util.List, com.unicacorp.interact.treatment.optimization.v2.IClientArgs,
* com.unicacorp.interact.treatment.optimization.IInteractSession, boolean)
*/
public List<ITreatment> optimizeRecommendList(List<ITreatment> recList,
IClientArgs clientArgs, IInteractSession session, boolean debug)
throws LearningException
{
System.out.println("Calling optimizeRecommendList for SampleLearning");

// Sort the candidate treatments by calling the sorter defined in this class and return the sorted list
Collections.sort(recList,new MyOfferSorter());

// now just return what was asked for via "numberRequested" variable
List<ITreatment> result = new ArrayList<ITreatment>();

for(int x=0;x<(Integer)clientArgs.getValue(IClientArgs.NUMBER_OF_OFFERS_REQUESTED) && x<recList.size();x++)
{
result.add(recList.get(x));
}
return result;
}

/* (non-Javadoc)
* @see com.unicacorp.interact.treatment.optimization.v2.ILearning#shutdown
* (com.unicacorp.interact.treatment.optimization.v2.ILearningConfig, boolean)
*/
public void shutdown(ILearningConfig config, boolean debug) throws LearningException
{
// If any remote connections exist, this would be a good place to gracefully
// disconnect from them as this method is called at the shutdown of the Interact runtime
// webapp. For this example, there is nothing really to do
// except print out a statement for debugging.
System.out.println("Calling shutdown for SampleLearning");

}
// Sort by:
// 1. offers with zero contacts - for ties, order is based on original input
// 2. descending accept probability rate - for ties, order is based on original input

public class MyOfferSorter implements Comparator<ITreatment>
{
private static final long serialVersionUID = 1L;

/* (non-Javadoc)
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compare(ITreatment treatment1, ITreatment treatment2)
{

// get contact count for both treatments
Integer contactCount1 = _offerToContactCount.get(treatment1.getOffer().getOfferId());
Integer contactCount2 = _offerToContactCount.get(treatment2.getOffer().getOfferId());

// if treatment hasn't been contacted, then that wins
if(contactCount1 == null || contactCount1 == 0)
return -1;

if(contactCount2 == null || contactCount2 == 0)
return 1;

// get accept counts
Integer acceptCount1 = _offerToAcceptCount.get(treatment1.getOffer().getOfferId());
Integer acceptCount2 = _offerToAcceptCount.get(treatment2.getOffer().getOfferId());

float acceptProbability1 = (float) acceptCount1 / (float) contactCount1;
float acceptProbability2 = (float) acceptCount2 / (float) contactCount2;

// descending order
return (int) (acceptProbability2 - acceptProbability1);

}
}

}