Name

global.MLPredictor

Description

Utilities to find and apply predictions on a table

Script

var MLPredictor = Class.create();
MLPredictor.prototype = {
  ML_SOLUTION_DEFINITION : 'ml_solution_definition',
  ML_CAPABILITY_DEFINITION_BASE : 'ml_capability_definition_base',
  CAPABILITY_CLASSIFICATION : 'classification_trainer',
  CAPABILITY_SIMILARITY : 'similarity_trainer',
  CAPABILITY_CLUSTERING : 'clustering_trainer',
  CAPABILITY_REGRESSION : 'regression_trainer',
  CAPABILITY_CLASSIFICATION_WORKFLOW : 'workflow_classification_trainer',
  CAPABILITY_REGRESSION_WORKFLOW : 'workflow_regression_trainer',
  CAPABILITY_CLUSTERING_WORKFLOW : 'workflow_clustering_trainer',
  CAPABILITY_SIMILARITY_WORKFLOW : 'workflow_similarity_trainer',

  initialize: function() {
  },

  type: 'MLPredictor',
  
  /**
   * Return the solution object for specified solutionName
   * Only the solution definition and the solution both are active, return solution or return null
   */
  findActiveSolution: function(solutionName) {
  	var solutionDef = new GlideRecord(this.ML_CAPABILITY_DEFINITION_BASE); 
  	solutionDef.addQuery('solution_name', solutionName); 
  	solutionDef.addActiveQuery(); 
  	solutionDef.query();
  	if(!solutionDef.next()) {
  		return null;
  	}
  	
  	var finder = new sn_ml.SolutionFinder();
  	
  	var solution = finder.getSolution(solutionName);
  	if (gs.nil(solution)) {
  		return null;
  	}
  	
  	if (!solution.isActive()) {
  		return null;
  	}
  	
  	return solution;
  },	
  
  /**
   * Return array of solution objects which represent applicable active solutions for table the specified
   * record is for
   */
  findActiveSolutionsForRecord: function(gr) {
  	var finder = new sn_ml.SolutionFinder();
  	var solutions = finder.findActiveSolutionsForRecord(gr);

  	return solutions;
  },
  
  /**
   * Apply predicted values from specified solutions (an array) to specified record gr.
   */
  applyPrediction: function(gr, solutions) {
  	for (var i = 0; i < solutions.length; ++i)
  		this.applyPredictionForSolution(gr, solutions[i]);
  },
  
  /**
   * Apply predicted value from specified solution to specified record gr.
   * Return true if the prediction happened.
   */	
  applyPredictionForSolution: function(gr, solution) {
  	if (this.validSolutionCapability(solution)!==this.CAPABILITY_CLASSIFICATION && this.validSolutionCapability(solution)!==this.CAPABILITY_CLASSIFICATION_WORKFLOW) {
  		gs.addErrorMessage("applyPredictionForSolution(gr, solution) is only applicable for classification solution");
  		return;
  	}
  	var outcome = solution.predict(gr);
  	if (!outcome.hasPrediction()) {
  		var errorMessage = gs.getMessage("Can't predict {0}", outcome.toJSONString());
  		gs.addErrorMessage(errorMessage);
  		return false;
  	}

  	var predictedValue = this.getPredictedValue(solution, outcome);
  	var predictedValueSysId = outcome.predictedValueSysId();

  	var isPredicted = this.__applyPredictedValue(gr, solution, predictedValue, predictedValueSysId);

  	// Record the prediction stat
  	sn_ml.SolutionStats.recordPredictValues(outcome, solution);

  	return isPredicted;
  },
  
  /**
   * Return the predicted value for specified solution based on the specified outcome of
   * prediction. If confidence of prediction does not satisfy thresholds, return null.
   */
  getPredictedValue: function(solution, outcome) {
  	var predictedValue = outcome.predictedValue();
  	if (predictedValue === null) {
  		return null;
  	}
  	
  	var threshold = this.__getPredictionThreshold(solution, outcome);
  
  	// Set the value for the predicted field
  	if (outcome.confidence() >= threshold)
  		return predictedValue;
  	
  	return null;
  },

  
  /**
   * gr: Glide record to be predicted
   * solution: Solution object to be executed
   * options.top_n: (Optional) if provided returns results upto topN, 
   * otherwise default will be read from property "glide.platform_ml.max_num_predictions"
   * options.apply_threshold:  (Optional) checks the threshold value (solution threshold for similarity, 
   * class level threshold for classification) 
   * for the solution and applies on result set. default value is true
   */
getPredictions: function(gr, solution, options) {
  	//getting topN predictions
  	var outcomeArr = [];
  	
  	try{
  		options = options || {};
  
  		var topN = options.top_n;
  		var applyThreshold = options.apply_threshold;
  		var confidenceLevel = options.confidence_level;
  		confidenceLevel =  JSUtil.doesNotHave(confidenceLevel) ? 95 : confidenceLevel;
  		//confidence level for regression lower and upper bound predictions
  		if (!(JSUtil.type_of(confidenceLevel) === "number") || confidenceLevel < 0 || confidenceLevel >100 ){
  			throw "Unable to predict: Confidence Level must lie in 0-100";
  		}
  		
  		if (this.validSolutionCapability(solution)===this.CAPABILITY_REGRESSION || this.validSolutionCapability(solution)===this.CAPABILITY_CLUSTERING || this.validSolutionCapability(solution)===this.CAPABILITY_REGRESSION_WORKFLOW || this.validSolutionCapability(solution)===this.CAPABILITY_CLUSTERING_WORKFLOW){
  			topN = 1;
  			applyThreshold = false;
  		}
  		
  		//if null - reset applyThreshold to true
  		if (JSUtil.nil(applyThreshold)) {
  			applyThreshold = true;
  		}
  		//classification solution each class has different threshold
  		//therefor needs to get all the results from ml engine
  		if (applyThreshold && (this.validSolutionCapability(solution)===this.CAPABILITY_CLASSIFICATION || this.validSolutionCapability(solution)===this.CAPABILITY_CLASSIFICATION_WORKFLOW)) {
  			var maxClassificationTopN = (topN * MLBaseConstants.CLASSIFICATION_TOP_N_MULTIPLIER).toFixed();
  			outcomeArr = solution.predictTopN(gr, maxClassificationTopN);
  		}
  		else if (this.validSolutionCapability(solution)===this.CAPABILITY_REGRESSION || this.validSolutionCapability(solution)===this.CAPABILITY_REGRESSION_WORKFLOW){
  				outcomeArr = solution.predictTopNConfInterval(gr, topN, confidenceLevel);
  		} else { 
  			outcomeArr = solution.predictTopN(gr, topN); 
  		}

  		if (outcomeArr === null) {
  			//instead of returning null returning empty array
  			return [];
  		}

  		//applying threshold if set to true
  		if(applyThreshold) {
  			//keeping elements greater than threshold
  			for (var index = outcomeArr.length - 1; index >= 0; --index) {
  				var outcome = outcomeArr[index];
  				var predicted = outcome.predictedValue();
  				var threshold = this.__getPredictionThreshold(solution, outcome);			
  				// remove the element if less than threshold
  				if (outcome.confidence() < threshold) {
  					outcomeArr.splice(index, 1);
  				}  else if(this.validSolutionCapability(solution)!==this.CAPABILITY_CLASSIFICATION && this.validSolutionCapability(solution)!==this.CAPABILITY_CLASSIFICATION_WORKFLOW){
  					//the rest of the elements have greater threshold
  					break;
  				}
  			}

  			//get subset upto topN
  			if(outcomeArr.length > topN) {
  				outcomeArr = outcomeArr.slice(0,topN);
  			}
  		}
  		
  		sn_ml.SolutionStats.recordPredictValuesTopN(outcomeArr, solution);
  		
  	}catch(e){
  		gs.logError("Exception caught: "+e, 'MLPredictor');
  	}
  	
  	
  	return outcomeArr;
  },
  
  /**
   * Record final values to prediction results to specified record gr with optionally specified reason
   */
  recordFinalValuesInPredictionResults: function(gr, reason) {
  	sn_ml.SolutionStats.updateFinalValues(gr, reason);
  },
  
  /**
   * Returns true if the input solution is of type classification, otherwise returns false
   * To be deprecated, available only until Quebec release
   */
  isClassificationSolution: function(solution) {
  	var capability = solution.getCapability();
  	return JSUtil.notNil(capability) && (capability == this.CAPABILITY_CLASSIFICATION || capability == this.CAPABILITY_CLASSIFICATION_WORKFLOW);
  },
  /**
   * Returns true if the input solution is of type similarity, otherwise returns false
   * To be deprecated, available only until Quebec release
   */
  isSimilaritySolution: function(solution) {
  	var capability = solution.getCapability();
  	return JSUtil.notNil(capability) && (capability == this.CAPABILITY_SIMILARITY || capability == this.CAPABILITY_SIMILARITY_WORKFLOW);
  },
  
  /**
   * Returns true if the input solution is of type clustering, otherwise returns false
   * To be deprecated, available only until Quebec release
   */
  isClusteringSolution: function(solution) {
  	var capability = solution.getCapability();
  	return JSUtil.notNil(capability) && (capability == this.CAPABILITY_CLUSTERING || capability == this.CAPABILITY_CLUSTERING_WORKFLOW);
  },
  
  isWorkflowSolution: function(solution) {
  	var capability = solution.getCapability();
  	return JSUtil.notNil(capability) && capability == this.CAPABILITY_WORKFLOW;
  },
  
  /**
   * Returns null if the capability field is empty, otherwise returns the capability of the solution
   */
  validSolutionCapability: function(solution){
  	var capability = solution.getCapability();
  	if(!JSUtil.notNil(capability))
  		return null;
  	return capability;
  },
  
  // PRIVATE

  __applyPredictedValue: function(gr, solution, predictedValue, predictedValueSysId) {
  	if (predictedValue === null)
  		return false;
  	
  	var field = solution.getPredictedField();
  	if (field === null)
  		return false;

  	var ge = gr.getElement(field);
  	if (ge === null)
  		return false;

  	if (ge.getED().isReference()) {
  		if (gs.nil(predictedValueSysId)) // Maintain backwards compatibility
  			ge.setDisplayValue(predictedValue);
  		else
  			ge.setValue(predictedValueSysId);
  	} else
  		ge.setValue(predictedValue);
  	return true;
  },
  
  __getPredictionThreshold: function(solution, outcome) {
  	if (this.validSolutionCapability(solution)===this.CAPABILITY_CLASSIFICATION ||  this.validSolutionCapability(solution)===this.CAPABILITY_CLASSIFICATION_WORKFLOW){
  		return solution.getThreshold(outcome.predictedValue());
  	} else if (this.validSolutionCapability(solution)===this.CAPABILITY_SIMILARITY || this.validSolutionCapability(solution)===this.CAPABILITY_SIMILARITY_WORKFLOW) {
  		return solution.getThreshold();
  	}
  	return -1;
  }
  
};

Sys ID

f1845e31f33232008e31301e73612b80

Offical Documentation

Official Docs: