package causation.lab;
// packages
import javax.swing.*;
import java.beans.*;
import java.io.*;
import java.awt.Image;
import java.net.*;
import java.util.*;
import exercise.*;
import java.awt.*;
import java.awt.event.*;
import tetrad.graph.*;
import tetrad.model.BayesNetIM;
import tetrad.model.BayesNetPM;
import tetrad.model.Parameter;
// custom classes
import causation.lab.LabPanel;
import causation.lab.DataTableFrame;
import causation.lab.CLExercise;
import causation.lab.CLSubmission;
import causation.lab.CLEvaluation;
import causation.lab.CLEvaluator;
/**
*
* This is the Applet that runs the CausalityLab. It creates the JFrame
* with a JDesktop, and places the various pieces onto the desktop. It
* also acts as a router between the LabPanel and the DataTableFrame.
* Finally, it performs the evaluations of the user's submissions.
*
* Copyright 1999 by David Danks. All rights reserved.
*
*
* @see LabPanel
* @see DataTableFrame
* @version 0.9 Sep 1, 1999
* @author David Danks
*/
public class LabApplet extends JApplet implements
PropertyChangeListener {
/////////////////// Class Variables ///////////////////
protected static String ICON_DIR = "icons/";
// NOTE: DATA_DIR cannot be empty
protected static String DEFAULT_DATA_DIR = "data/";
protected static String LOCK_CURSOR = "lockCursor.gif";
protected static String RAND_CURSOR = "randCursor.gif";
protected static String ARROW_CURSOR = "arrowCursor.gif";
protected static String LATENT_CURSOR = "latentCursor.gif";
protected static String CORRECT_RESPONSE = "You got it right!";
protected static String EQUIVALENT_RESPONSE =
"Not quite, but your graph is observationally equivalent.";
protected static String INCORRECT_RESPONSE =
"Sorry, but that is not correct.";
protected static Font FONT = new Font("Dialog", Font.BOLD, 14);
protected static int DEFAULT_WIDTH = 600;
protected static int DEFAULT_HEIGHT = 600;
/////////////////// Instance Variables ///////////////////
protected JDesktopPane desktop;
protected JInternalFrame labIFrame;
protected LabPanel lp;
protected DataTableFrame dtf;
protected Vector dataExpSetup = new Vector();
protected Hashtable dataExpImage = new Hashtable();
protected Hashtable dataDID = new Hashtable();
protected Hashtable iifHash = new Hashtable();
protected CLEvaluator eval = new CLEvaluator();
protected CLExercise exercise;
protected String data_dir = DEFAULT_DATA_DIR;
/////////////////// Constructors ///////////////////
/**
* This is a null constructor, in case the Applet gets run as an application
*/
public LabApplet() { ; }
/**
* Sets up the CausalityLab when it is run as an Applet
*/
public void init() {
UIManager.put("causation.lab.plaf.WorkbenchUI",
"causation.lab.plaf.basic.BasicWorkbenchUI");
UIManager.put("causation.lab.plaf.WBVariableUI",
"causation.lab.plaf.basic.BasicWBVariableUI");
UIManager.put("causation.lab.plaf.RandomizerUI",
"causation.lab.plaf.basic.BasicRandomizerUI");
UIManager.put("causation.lab.plaf.LatentUI",
"causation.lab.plaf.basic.BasicLatentUI");
UIManager.put("causation.lab.plaf.DistributionChooserUI",
"causation.lab.plaf.concrete.CDistributionChooserUI");
UIManager.put("stats.plaf.BarChartUI",
"stats.plaf.concrete.CBarChartUI");
UIManager.put("stats.plaf.ChartWrapperUI",
"stats.plaf.concrete.CChartWrapperUI");
UIManager.put("stats.plaf.LegendUI",
"stats.plaf.concrete.CLegendUI");
// check to see if the user wants to have a different data dir
String newDir = getParameter("Data directory");
if (newDir != null) data_dir = newDir;
// create and show the desktop so we can show a progress bar
final JFrame frame = new JFrame("Causality Lab");
desktop = new JDesktopPane();
frame.setContentPane(desktop);
frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
frame.show();
// Make sure closing the window stops the Applet
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent w) {
frame.setVisible(false);
frame.dispose();
LabApplet.this.destroy();
}
});
// create the progress bar here
JInternalFrame prog = new JInternalFrame("Loading lesson...",
false, false, false, false);
JPanel progPanel = new JPanel(new BorderLayout());
JProgressBar pbar = new JProgressBar();
// find out what BayesNet the user wants, or create the default
BayesNetIM bn = null;
ObjectInputStream in = null;
/* For now, we're just using the default BayesNetIM, since we can't read
* in others. At some point, we may want to add a method that lets you
* choose from a menu, but we'll stick with this for now.
ObjectInputStream in = getBayesNetInputStream();
*/
// add the progress bar, since the dialog box is gone
progPanel.add(pbar, "Center");
prog.setContentPane(progPanel);
desktop.add(prog);
Dimension pSize = prog.getPreferredSize();
prog.setBounds((desktop.getSize().width-pSize.width)/2,
(desktop.getSize().height-pSize.height)/2, pSize.width,
pSize.height);
prog.show();
// now read in the appropriate BayesNetIM
if (in == null) bn = createDefaultBayesNet();
else {
try {
bn = (BayesNetIM)(in.readObject());
in.close();
}
// just print out the exceptions
catch (Exception e) { System.err.println(e); }
}
pbar.setValue(25); // update the bar
// add the initial components
lp = new LabPanel(bn, getCodeBase());
lp.addPropertyChangeListener(this);
// since we've created the LabPanel, check if we have WBVariable icons
WBVariable[] vars = lp.getVariables();
for (int i=0; i 0) // it's finite, so just add to the data table
dtf.addDataTable(lp.performExperiment(size), name, im,
DataTableFrame.FINITE_DATASET);
else { // it's infinite
size *= -1;
dtf.addDataTable(lp.performExperiment(size), name, im,
DataTableFrame.INFINITE_DATASET);
size *= -1; // since we'll use size later
}
// display a (false) progress bar
// displayProgressBar(name, size);
// de-iconify (if needed), and bring the data table to the front
if (dtf.isIcon()) {
try { dtf.setIcon(false); }
catch (PropertyVetoException pve) {
System.out.println("Can't deiconify data display");
}
}
if (dtf.isShowing()) dtf.show();
else { desktop.add(dtf); dtf.show(); }
// store the ExperimentalSetup and DataIndepDisplay
ExperimentalSetup exp = new
ExperimentalSetup(lp.getWorkbench());
dataExpSetup.addElement(exp);
DSeparationFacts dsf = new
DSeparationFacts(lp.getTrueManipulatedTetradGraph());
dsf.setFilterAndLockedVars(lp.getOnWorkbenchVariableNames(),
lp.getLockedVariableNames());
dataDID.put(exp, new DataIndepDisplay(name, size, dsf));
// check to see if we also need to store the ExpSetupImage
for (int i=dataExpSetup.size()-2; i>=0; i--) {
ExperimentalSetup key =
(ExperimentalSetup)dataExpSetup.elementAt(i);
if (key.isEquivalentTo(exp)) return;
}
dataExpImage.put(exp, lp.getExperimentalSetupImage());
return;
}
// user changed tabs
if (p.getPropertyName().equals("Tab Number")) {
return;
}
// user requested "true" independencies for the given experiment
if (p.getPropertyName().equals("Oracle")) {
ExperimentalSetup exp =
(ExperimentalSetup)dataExpSetup.elementAt
(((Integer)p.getNewValue()).intValue());
DataIndepDisplay did = (DataIndepDisplay)dataDID.get(exp);
// find the original ES that was used as a key to store the IIF
ExperimentalSetup key = null;
Enumeration keys = dataExpImage.keys();
while (keys.hasMoreElements()) {
key = (ExperimentalSetup)keys.nextElement();
if (key.isEquivalentTo(exp)) break;
}
IndepInternalFrame iif = getIndepFrame(key);
if (iif != null) { // if there is already an IIF, add to it
iif.addDataIndeps(did);
if (iif.isIcon()) {
try { iif.setIcon(false); }
catch (PropertyVetoException pve) {
System.out.println("Can't deiconify indep display");
}
}
if (iif.isShowing()) iif.show();
else { desktop.add(iif); iif.show(); }
}
else { // need to create a new frame
Image expImage = (Image)dataExpImage.get(key);
iif = new IndepInternalFrame(expImage, did);
iifHash.put(key, iif);
iif.setOpaque(true);
desktop.add(iif);
iif.setBounds(0, 0, iif.getPreferredSize().width,
iif.getPreferredSize().height);
iif.show();
}
return;
}
// user wants to reset the workbench to a particular state
if (p.getPropertyName().equals("Reset Workbench")) {
lp.loadScreenShot(((Integer)p.getNewValue()).intValue());
return;
}
// user is submitting their DiGraph
if (p.getPropertyName().equals("Submit")) {
receiveResults(submitSolution(exercise,
new CLSubmission(lp.getUserTetradGraph())));
}
if (p.getPropertyName().equals("independence")) {
HypIndepDisplay hid = (HypIndepDisplay)p.getNewValue();
if (hid == null) {
System.out.println("HypIndepDisplay is null");
return;
}
ExperimentalSetup exp = (ExperimentalSetup)p.getOldValue();
if (exp == null) {
System.out.println("ExperimentalSetup is null");
return;
}
IndepInternalFrame iif = getIndepFrame(exp);
if (iif != null) {
hid.setNumber(iif.getNumberHypIndeps()+1);
iif.addHypIndeps(hid);
if (iif.isIcon()) {
try { iif.setIcon(false); }
catch (PropertyVetoException pve) {
System.out.println("Can't deiconify indep display");
}
}
if (iif.isShowing()) iif.show();
else { desktop.add(iif); iif.show(); }
}
else { // need to create a new frame
hid.setNumber(1);
Image expImage = lp.getExperimentalSetupImage();
iif = new IndepInternalFrame(expImage, hid);
iifHash.put(exp, iif);
iif.setOpaque(true);
desktop.add(iif);
iif.setBounds(0, 0, iif.getPreferredSize().width,
iif.getPreferredSize().height);
iif.show();
return;
}
}
}
/**
* A helper method for finding the correct indep frame
* @param exp The ExperimentalSetup whose IIF we want
*/
private IndepInternalFrame getIndepFrame(ExperimentalSetup exp) {
Enumeration keys = iifHash.keys();
while (keys.hasMoreElements()) {
ExperimentalSetup key = (ExperimentalSetup)keys.nextElement();
if (key.isEquivalentTo(exp))
return (IndepInternalFrame)iifHash.get(key);
}
return null;
}
// This method would be used if we could generate a progress bar that faked
// the construction of data. However, we can't do that right now, so it's
// been commented out.
/*
* A helper method for displaying the dummy progress window
* @param name The name of the dataset
* @param size The size of the dataset
*
private void displayProgressBar(String name, int size) {
ProgressBarPanel pbp = new ProgressBarPanel();
JInternalFrame frame =
new JInternalFrame("Generating data...", true, true,
true, true);
frame.setContentPane(pbp);
desktop.add(frame);
frame.setBounds(0, 0, frame.getPreferredSize().width+20,
frame.getPreferredSize().height+20);
frame.show();
for (int i=0; i<5; i++) {
pbp.updateBar(i*20);
try { Thread.sleep(1000); }
catch (Exception e) { System.out.println(e); }
System.out.println("cycle "+i);
}
pbp.updateBar(100);
return;
/*
JOptionPane.showInternalMessageDialog(this,
"Generating data...",
"Error!", JOptionPane.ERROR_MESSAGE);
// JOptionPane.showInternalMessageDialog(this, panel);
System.out.println("break4: labapplet");
/*
JInternalFrame jif = new JInternalFrame("Generating data...",
true, true, true, true);
jif.setContentPane(panel);
desktop.add(jif);
jif.setBounds(0, 0, jif.getPreferredSize().width,
jif.getPreferredSize().height);
jif.show();
/*
// determine total delay, and increment
final int totalTime = 1000 + size*2;
final int TOTAL_STEPS = 5;
for (int i=0; i Growth <- Sunlight
*/
private BayesNetIM createDefaultBayesNet() {
String[] vars = {"Education", "Income", "Happiness"};
String[][] varVals = {
{"High School", "College"}, // Education values
{"Low", "Medium", "High"}, // Income values
{"Low", "High"} // Happiness values
};
String[] edges = {"Education->Income", "Education->Happiness",
"Income->Happiness"};
String[][] probs = {
{"High School||.7", "College||.3"}, // Education probs
{"Low|Education=High School|.6", // Income probs
"Medium|Education=High School|.3",
"High|Education=High School|.1",
"Low|Education=College|.2",
"Medium|Education=College|.6",
"High|Education=College|.2"},
{"Low|Education=High School;Income=Low|.6", // Happiness probs
"High|Education=High School;Income=Low|.4",
"Low|Education=High School;Income=Medium|.5",
"High|Education=High School;Income=Medium|.5",
"Low|Education=High School;Income=High|.4",
"High|Education=High School;Income=High|.6",
"Low|Education=College;Income=Low|.5",
"High|Education=College;Income=Low|.5",
"Low|Education=College;Income=Medium|.4",
"High|Education=College;Income=Medium|.6",
"Low|Education=College;Income=High|.3",
"High|Education=College;Income=High|.7",
}
};
return createBayesNet(vars, varVals, edges, probs);
}
/**
* This is a helper method that creates BayesNetIMs for us. It has a
* number of parameters, but this will (in the long run) make it easy
* for us to hard code BayesNetIMs into the CausalityLab. This version
* of the method randomly assigns probabilities.
* @param vars An array of variable names
* @param varVals An array of Vectors of variable values
* @param edges An array of edge names in the form "FromVar->ToVar"
* @return The BayesNetIM for these parameters
*/
private BayesNetIM createBayesNet(String[] vars, Vector[] varVals,
String[] edges) {
return createBayesNet(vars, varVals, edges, null);
}
/**
* This is a helper method that creates BayesNetIMs for us. It has a
* number of parameters, but this will (in the long run) make it easy
* for us to hard code BayesNetIMs into the CausalityLab. This version
* of the method randomly assigns probabilities.
* @param vars An array of variable names
* @param varVals A 2-d String array of variable values
* @param edges An array of edge names in the form "FromVar->ToVar"
* @return The BayesNetIM for these parameters
*/
private BayesNetIM createBayesNet(String[] vars, String[][] varVals,
String[] edges) {
Vector[] valVect = new Vector[varVals.length];
for (int i=0; iToVar"
* @param probs A 2-d array of Strings in the following form:
* "VAL|par1=val;...;parN=val|PROB", with the array
* structured as [var#][prob]
* @return The BayesNetIM for these parameters
*/
private BayesNetIM createBayesNet(String[] vars, String[][] varVals,
String[] edges, String[][] probs) {
Vector[] valVect = new Vector[varVals.length];
for (int i=0; iToVar"
* @param probs A 2-d array of Strings in the following form:
* "VAL|par1=val;...;parN=val|PROB", with the array
* structured as [var#][prob]
* @return The BayesNetIM for these parameters
*/
private BayesNetIM createBayesNet(String[] vars, Vector[] varVals,
String[] edges, String[][] probs) {
// construct the DAG
DirectedAcyclicGraph dag = new DirectedAcyclicGraph();
for (int i=0; i");
try { dag.addDirectedEdge(st.nextToken(), st.nextToken()); }
catch (PropertyVetoException pve) {
System.out.println("Can't add edge: "+pve.toString());
return null;
}
}
// now construct the BayesNetPM
BayesNetPM bnpm = new BayesNetPM(dag);
for (int i=0; inull, if the file doesn't exist, or the user
* pressed "cancel".
*/
private ObjectInputStream getBayesNetInputStream() {
ObjectInputStream in = null;
// Note that it doesn't matter whether we're local or not, since
// Applets can't use Files, they can only use URLs (which we can use
// to access local information).
URL base = null;
try { base = new URL(getCodeBase(), data_dir); }
catch (MalformedURLException m) {
System.out.println("Bad URL: "+m.toString());
return null;
}
// now read in the directory, and save the file names to a Vector
Vector fileNames = new Vector();
try {
InputStream is = base.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
while (true) {
String line = br.readLine();
if (line == null) break;
int cue = line.indexOf("true, if it passes the check; false
,
* if it fails the check
*/
protected boolean checkFile(String filename) {
// add the checks here
String ext = filename.substring(filename.lastIndexOf("."));
if (!ext.equalsIgnoreCase(".dat")) return false;
// if it passes all of the checks, just return true;
return true;
}
}