package beast.core;
import java.io.PrintStream;
import org.w3c.dom.Node;
/**
* This class represents a node of the state. Concrete classes include Parameters and Trees.
* StateNodes differ from CalculationNodes in that they
* 1. Do not calculate anything, with the exception of initialisation time
* 2. can be changed by Operators
*
* @author Alexei Drummond
*/
@Description("A node that can be part of the state.")
public abstract class StateNode extends CalculationNode implements Loggable, Cloneable, Function {
/**
* Flag to indicate the StateNode is not constant.
* This is particularly useful for Beauti *
*/
final public Input<Boolean> isEstimatedInput = new Input<>("estimate", "whether to estimate this item or keep constant to its initial value", true);
/**
* @return this StateNode if it is not in the State.
* If it is in the State, return the version that is currently valid
* (i.e. not the stored one).
*/
public StateNode getCurrent() {
if (state == null) {
return this;
}
return state.getStateNode(index);
}
/**
* @param operator explain here why operator is useful
* @return StateNode for an operation to do its magic on.
* The State will make a copy first, if there is not already
* one available.
*/
public StateNode getCurrentEditable(final Operator operator) {
startEditing(operator);
return this;
}
/**
* Getting/setting global dirtiness state for this StateNode.
* Every StateNode has a flag (somethingIsDirty) that represents whether anything
* in the state has changed. StateNode implementations like Parameters and Trees
* have their own internal flag to represent which part of a StateNode (e.g.
* an element in an array, or a node in a tree) has changed.
* *
*/
public boolean somethingIsDirty() {
return this.hasStartedEditing;
}
public void setSomethingIsDirty(final boolean isDirty) {
this.hasStartedEditing = isDirty;
}
/**
* mark every internal element of a StateNode as isDirty.
* So both the global flag for this StateNode (somethingIsDirty) should be set as
* well as all the local flags.
*
* @param isDirty
*/
abstract public void setEverythingDirty(final boolean isDirty);
/**
* @return a deep copy of this node in the state.
* This will generally be called only for stochastic nodes.
*/
public abstract StateNode copy();
/**
* other := this
* Assign all values of this to other
* NB: Should only be used for initialisation!
*/
public abstract void assignTo(StateNode other);
/**
* this := other
* Assign all values of other to this
* NB: Should only be used for initialisation!
*/
public abstract void assignFrom(StateNode other);
/**
* As assignFrom, but without copying the ID
* NB: Should only be used for initialisation!
*/
public void assignFromWithoutID(StateNode other) {
final String id = getID();
assignFrom(other);
setID(id);
}
/**
* As assignFrom, but only those parts are assigned that
* are variable, for instance for parameters bounds and dimension
* do not need to be copied.
*/
public abstract void assignFromFragile(StateNode other);
/**
* for storing a state *
*/
final public void toXML(PrintStream out) {
out.print("<statenode id='" + normalise(getID()) + "'>");
out.print(normalise(toString()));
out.print("</statenode>\n");
}
/**
* stores a state node in XML format, to be restored by fromXML() *
*/
final public String toXML() {
return "<statenode id='" + normalise(getID()) + "'>" +
normalise(toString()) +
"</statenode>\n";
}
/** ensure XML identifiers get proper escape sequences **/
private String normalise(String str) {
if (str == null) {
return null;
}
str = str.replaceAll("&", "&");
str = str.replaceAll("'", "'");
str = str.replaceAll("\"", """);
str = str.replaceAll("<", "<");
str = str.replaceAll(">", ">");
return str;
}
/**
* for restoring a state that was stored using toXML() above
* from a DOM Node. *
*/
public abstract void fromXML(Node node);
// /**
// * @return true if this node is acting as a random variable, false if this node is fixed and effectively data.
// */
// public final boolean isStochastic() {
// return this.isStochastic;
// }
//
// /**
// * @param isStochastic true if this need should be treated as stochastic, false if this node should be fixed
// * and treated as data
// */
// final void setStochastic(boolean isStochastic) {
// this.isStochastic = isStochastic;
// }
//
// boolean isStochastic = true;
/**
* Scale StateNode with amount scale and
*
* @param scale scaling factor
* @return the number of degrees of freedom used in this operation. This number varies
* for the different types of StateNodes. For example, for real
* valued n-dimensional parameters, it is n, for a tree it is the
* number of internal nodes being scaled.
* @throws IllegalArgumentException when StateNode become not valid, e.g. has
* values outside bounds or negative branch lengths.
*/
abstract public int scale(double scale);
/**
* Pointer to state, null if not part of a State.
*/
protected State state = null;
public State getState() {
return state;
}
/**
* flag to indicate some value has changed after operation is performed on state
* For multidimensional parameters, there is an internal flag to indicate which
* dimension is dirty
*/
protected boolean hasStartedEditing = false;
/**
* The index of the parameter for identifying this StateNode
* in the State.
*/
public int index = -1;
public int getIndex() {
return index;
}
/**
* should be called before an Operator proposes a new State *
*
* @param operator
*/
public void startEditing(final Operator operator) {
assert (isCalledFromOperator(4));
if (hasStartedEditing) {
// we are already editing
return;
}
hasStartedEditing = true;
// notify the state
state.getEditableStateNode(this.index, operator);
store();
}
private boolean isCalledFromOperator(int level) {
// TODO: sun.reflect.Reflection.getCallerClass is not available in JDK7
// and alternative methods are really slow according to
// http://stackoverflow.com/questions/421280/in-java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection
// Class<?> caller = sun.reflect.Reflection.getCallerClass(level);
// while (caller != null) {
// if (Operator.class.isAssignableFrom(caller)) {
// return true;
// }
// caller = sun.reflect.Reflection.getCallerClass(++level);
// }
// return false;
return true;
}
@Override
abstract protected void store();
@Override
abstract public void restore();
} // class StateNode