package net.certware.evidence.hugin.view.jobs; import java.io.PrintWriter; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import net.certware.core.ui.log.CertWareLog; import net.certware.evidence.hugin.view.ViewTree; import net.certware.evidence.hugin.view.tree.VariableNode; import net.certware.evidence.hugin.view.tree.VariableNodeState; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.swt.widgets.Display; import edu.ucla.belief.CPTParameter; import edu.ucla.belief.CPTShell; import edu.ucla.belief.EliminationHeuristic; import edu.ucla.belief.FiniteVariable; import edu.ucla.belief.InferenceEngine; import edu.ucla.belief.PartialDerivativeEngine; import edu.ucla.belief.VariableImpl; import edu.ucla.belief.inference.RCEngineGenerator; import edu.ucla.belief.inference.RCSettings; import edu.ucla.belief.io.PropertySuperintendent; import edu.ucla.belief.sensitivity.SensitivityEngine; import edu.ucla.belief.sensitivity.SensitivityReport; import edu.ucla.belief.sensitivity.SingleCPTSuggestion; import edu.ucla.belief.sensitivity.SingleParamSuggestion; import edu.ucla.util.AbstractStringifier; import edu.ucla.util.ProbabilityInterval; /** * A job to perform the variable sensitivity calculation. * @author mrb * @since 1.2.1 */ public class SensitivityCalculationJob extends AbstractCalculationJob { /** user memory proportion for engine */ static final double MEMORY_PROPORTION = 1.0; /** comparison operator, for fetch from view */ String comparisonOperator = SensitivityEngine.OPERATOR_GTE; /** comparison constant, for fetch from view */ double comparisonConstant = 0.0; /** * Constructor saves network and view references. * @param jobName export job name for reporting * @param view view part for reference */ public SensitivityCalculationJob(String jobName, ViewTree view) { super(jobName,view); } /** * Production method for computations. * @param monitor progress monitor * @return CANCEL or OK */ @SuppressWarnings({ "unchecked", "rawtypes" }) public IStatus produce(IProgressMonitor monitor) { /** network name for messages */ String networkName = "undefined"; //$NON-NLS-1$ try { if ( initialNetwork == null ) { cancel(); CertWareLog.logWarning("Network not defined for calculation"); view.setWarningMessage(CANCEL_MSG); return Status.CANCEL_STATUS; } if ( nodes.isEmpty() ) { cancel(); CertWareLog.logWarning("No variable selections available for sensitivity calculation"); view.setWarningMessage(CANCEL_MSG); return Status.CANCEL_STATUS; } // analysis settings // SensitivityEngine.OPERATOR_EQUALS (=), SensitivityEngine.OPERATOR_GTE (>=), SensitivityEngine.OPERATOR_LTE (<=) Display.getDefault().syncExec(new Runnable() { public void run() { comparisonOperator = view.getSensitivityOperator(); comparisonConstant = view.getSensitivityThreshold(); } }); // set the elimination heuristic used, one of: MIN_FILL or MIN_SIZE // do not create an InferenceEngine optimized for only probability of evidence // set the fraction of full memory to use // create the cache allocation (very important) RCSettings settings = RCEngineGenerator.getSettings( (PropertySuperintendent)initialNetwork ); settings.setEliminationHeuristic( EliminationHeuristic.MIN_FILL ); // TODO fetch from view interface or preferences settings.setPrEOnly( false ); settings.setUserMemoryProportion( MEMORY_PROPORTION ); settings.validateAllocation( initialNetwork ); // network name from loaded file networkName = view.getSelectedFile().getName(); // use the first selected variable value as reference // TODO bail out if more than one selected? VariableNode firstNode = nodes.get(0); FiniteVariable firstVariable = firstNode.getNode(); Object referenceValue = null; for ( VariableNodeState vns : firstNode.states ) { if ( vns.isSelected() ) { referenceValue = firstVariable.instance( vns.getStateName() ); } } // initialize evidence from selections HashMap evidence = new HashMap(); for ( VariableNode vn : nodes ) { for ( VariableNodeState vns : vn.states ) { if ( vns.isSelected() ) { FiniteVariable fv = vn.getNode(); Object ev = fv.instance( vns.getStateName() ); // skip this value if it is the reference value // if we include the reference value as evidence, sensitivity is always satisfied if ( ev != referenceValue ) { evidence.put( fv, fv.instance(vns.getStateName()) ); } } } } // TODO remove System.err.println("using evidence " + evidence); System.err.println("using variable " + firstVariable + " value " + referenceValue ); System.err.println("using operator " + comparisonOperator + " constant " + comparisonConstant); // instantiation formatter VariableImpl.setStringifier( AbstractStringifier.VARIABLE_ID ); monitor.worked(1); if ( monitor.isCanceled() ) { CertWareLog.logWarning(CANCEL_MSG); view.setWarningMessage(CANCEL_MSG); return Status.CANCEL_STATUS; } // create the dynamator(edu.ucla.belief.inference.SynchronizedInferenceEngine$SynchronizedPartialDerivativeEngine) // create the inference engine RCEngineGenerator dynamator = new RCEngineGenerator(); InferenceEngine engine = dynamator.manufactureInferenceEngine( initialNetwork ); // set evidence initialNetwork.getEvidenceController().setObservations( evidence ); monitor.worked(1); if ( monitor.isCanceled() ) { CertWareLog.logWarning(CANCEL_MSG); return Status.CANCEL_STATUS; } // create the sensitivity engine SensitivityEngine sensitivityengine = new SensitivityEngine( initialNetwork, engine, (PartialDerivativeEngine)engine, new PrintWriter( System.out ) ); monitor.worked(1); if ( monitor.isCanceled() ) { CertWareLog.logWarning(CANCEL_MSG); return Status.CANCEL_STATUS; } // get the results SensitivityReport report = sensitivityengine.getResults( /* varY */ firstVariable, /* valueY */ referenceValue, /* varZ */ (FiniteVariable)null, /* valueZ */ (Object)null, /* arithmetic operator */ (Object)null, /* comparison operator */ comparisonOperator, /* epsilon */ comparisonConstant, /* flagSingleParameter */ true, /* flagSingleCPT */ true ); // update results in view LinkedHashMap<String,String> rows = new LinkedHashMap<String,String>(); // whether already satisfied if ( report == null ) { rows.put("Sensitivity result:", "Constraint already satisfied"); } else { List singleParameterSuggestions = report.generateSingleParamSuggestions(); Map mapFiniteVariablesToSingleCPTSuggestions = report.getSingleCPTMap(); // whether constraint cannot be satisfied if( singleParameterSuggestions.isEmpty() && mapFiniteVariablesToSingleCPTSuggestions.isEmpty() ) { rows.put("Sensitivity result:", "Constraint is unsatisfiable"); } else { // constraint satisfaction suggestions for ( int suggestion = 0; suggestion < singleParameterSuggestions.size(); suggestion++ ) { SingleParamSuggestion sps = (SingleParamSuggestion)singleParameterSuggestions.toArray()[suggestion]; rows.put("Suggestion #", Integer.toString(suggestion+1)); rows.put("Variable", sps.getCPTParameter().toString() ); rows.put("Current Value",Double.toString((Double)sps.getCurrentValue())); rows.put("Suggested Value",Double.toString((Double)sps.getSuggestedParameterValue())); rows.put("Log Odds Change", Double.toString(sps.getLogOddsChange())); addBlankRow(rows); } // single CPT multiple parameters rows.put("Single CPT Suggestions", "Multiple Parameter"); for( Iterator iterator = mapFiniteVariablesToSingleCPTSuggestions.keySet().iterator(); iterator.hasNext(); ) { FiniteVariable target = (FiniteVariable)iterator.next(); SingleCPTSuggestion singleSuggestion = (SingleCPTSuggestion) mapFiniteVariablesToSingleCPTSuggestions.get( target ); addSuggestion(rows,target,singleSuggestion); addBlankRow(rows); } // single CPT single parameter rows.put("Single CPT Suggestions", "Single Parameter"); int suggestionCount = mapFiniteVariablesToSingleCPTSuggestions.size(); for ( int suggestion = 0; suggestion < suggestionCount; suggestion++ ) { SingleCPTSuggestion singleSuggestion = (SingleCPTSuggestion) mapFiniteVariablesToSingleCPTSuggestions.get(mapFiniteVariablesToSingleCPTSuggestions.keySet().toArray()[suggestion]); ProbabilityInterval[] c1intervals = singleSuggestion.probabilityIntervals(); FiniteVariable fv = singleSuggestion.getVariable(); CPTShell shell = fv.getCPTShell(); int x = 0; for ( CPTParameter ptp : shell.getCPTParameters() ) { rows.put(""+ptp.getJointInstance().getInstance(), String.format("%f %s", ptp.getValue(), c1intervals[x++].toString())); } addSuggestion(rows,fv,singleSuggestion); addBlankRow(rows); } } } view.addResult(rows); engine.die(); monitor.worked(1); } catch( Exception e ) { CertWareLog.logError(String.format("%s %s","Sensitivity calculation for",networkName), e); return Status.CANCEL_STATUS; } catch (Throwable e) { CertWareLog.logError(String.format("%s %s","Sensitivity calculation for",networkName), e); return Status.CANCEL_STATUS; } String doneMessage = String.format("%s %s %s","Sensitivity calculation for", networkName, "complete."); CertWareLog.logInfo( doneMessage ); return Status.OK_STATUS; } /** * Runs the generator job, reporting to the given progress monitor. * @param monitor progress monitor * @return CANCEL or OK status */ protected IStatus run(IProgressMonitor monitor) { int count = 4; monitor.beginTask("Computing sensitivity", count); IStatus rv = produce(monitor); if ( rv == Status.OK_STATUS ) { view.setInfoMessage("Completed sensitivity calculation"); monitor.done(); } return rv; } /** * Adds a blank row consisting of two empty strings. * @param rows row map for results output */ @SuppressWarnings({ }) private void addBlankRow(HashMap<String,String> rows) { rows.put("", ""); // blank line //$NON-NLS-1$,$NON-NLS-2$ } /** * Adds standard lines for a sensitivity suggestion. * @param rows row map for results output * @param fv finite variable from network * @param suggestion single CPT suggestion */ private void addSuggestion(HashMap<String,String> rows, FiniteVariable fv, SingleCPTSuggestion suggestion) { rows.put(String.format("Suggestion for %s's CPT",fv.getID()), String.format("Log Odds Change %f",suggestion.getLogOddsChange())); rows.put(suggestion.toString(), ""); //$NON-NLS-1$ } }