package water.rapids;
import hex.Model;
import water.*;
import water.fvec.Frame;
import water.rapids.ast.*;
import water.rapids.ast.params.AstConst;
import water.rapids.ast.prims.advmath.*;
import water.rapids.ast.prims.assign.*;
import water.rapids.ast.prims.math.*;
import water.rapids.ast.prims.matrix.*;
import water.rapids.ast.prims.misc.*;
import water.rapids.ast.prims.mungers.*;
import water.rapids.ast.prims.operators.*;
import water.rapids.ast.prims.reducers.*;
import water.rapids.ast.prims.repeaters.*;
import water.rapids.ast.prims.search.*;
import water.rapids.ast.prims.string.*;
import water.rapids.ast.prims.time.*;
import water.rapids.ast.prims.timeseries.*;
import water.rapids.vals.ValFrame;
import water.rapids.vals.ValFun;
import water.rapids.vals.ValModel;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Execute a set of instructions in the context of an H2O cloud.
* <p/>
* An Env (environment) object is a classic stack of values used during
* execution of an AstRoot. The stack is hidden in the normal Java execution
* stack and is not explicit.
* <p/>
* For efficiency, reference counting is employed to recycle objects already
* in use rather than creating copies upon copies (a la R). When a Frame is
* `pushed` on to the stack, its reference count is incremented by 1. When a
* Frame is `popped` off of the stack, its reference count is decremented by
* 1. When the reference count is 0, the Env instance will dispose of the
* object. All objects live and die by the Env's that create them. That
* means that any object not created by an Env instance shalt not be
* DKV.removed.
* <p/>
* Therefore, the Env class is a stack of values + an API for reference counting.
*/
public class Env extends Iced {
// Session holds the ref-counts across multiple executions.
public final Session _ses;
// Current lexical scope lookup
public AstFunction _scope;
// Frames that are alive in mid-execution; usually because we have evaluated
// some first expression and need to hang onto it while evaluating the next
// expression.
private ArrayList<Frame> _stk = new ArrayList<>();
// Built-in constants, checked before other namespace lookups happen
private static final HashMap<String, AstPrimitive> PRIMS = new HashMap<>();
// Built-in primitives, done after other namespace lookups happen
private static final HashMap<String, AstParameter> CONSTS = new HashMap<>();
static void init(AstPrimitive ast) {
PRIMS.put(ast.str(), ast);
}
static void init(AstPrimitive ast, String name) {
PRIMS.put(name, ast);
}
static {
// Constants
CONSTS.put("FALSE", AstConst.FALSE);
CONSTS.put("False", AstConst.FALSE);
CONSTS.put("false", AstConst.FALSE);
CONSTS.put("TRUE", AstConst.TRUE);
CONSTS.put("True", AstConst.TRUE);
CONSTS.put("true", AstConst.TRUE);
CONSTS.put("NaN", AstConst.NAN);
CONSTS.put("NA", AstConst.NAN);
CONSTS.put("nan", AstConst.NAN);
CONSTS.put("PI", AstConst.PI);
CONSTS.put("Pi", AstConst.PI);
CONSTS.put("null", null);
// Standard math functions
init(new AstAbs());
init(new AstAcos());
init(new AstAcosh());
init(new AstAsin());
init(new AstAsinh());
init(new AstAtan());
init(new AstAtanh());
init(new AstCeiling());
init(new AstCos());
init(new AstCosh());
init(new AstCosPi());
init(new AstDiGamma());
init(new AstExp());
init(new AstExpm1());
init(new AstFloor());
init(new AstGamma());
init(new AstLGamma());
init(new AstLog());
init(new AstLog1P());
init(new AstLog2());
init(new AstLog10());
init(new AstNoOp());
init(new AstNot());
init(new AstNot(), "!!");
init(new AstRound());
init(new AstSgn());
init(new AstSignif());
init(new AstSin());
init(new AstSinh());
init(new AstSinPi());
init(new AstSqrt());
init(new AstTan());
init(new AstTanh());
init(new AstTanPi());
init(new AstTriGamma());
init(new AstTrunc());
// Math binary operators
init(new AstAnd());
init(new AstDiv());
init(new AstEq());
init(new AstGe());
init(new AstGt());
init(new AstIntDiv());
init(new AstIntDivR());
init(new AstLAnd());
init(new AstLe());
init(new AstLOr());
init(new AstLt());
init(new AstMod());
init(new AstModR());
init(new AstMul());
init(new AstNe());
init(new AstOr());
init(new AstPlus());
init(new AstPow());
init(new AstSub());
init(new AstIfElse());
init(new AstIfElse()); // this one is ternary
// Reducers
init(new AstAll());
init(new AstAny());
init(new AstAnyNa());
init(new AstCumMax());
init(new AstCumMin());
init(new AstCumProd());
init(new AstCumSum());
init(new AstMad());
init(new AstMax());
init(new AstMaxNa());
init(new AstMean());
init(new AstMedian());
init(new AstMin());
init(new AstMinNa());
init(new AstNaCnt());
init(new AstProd());
init(new AstProdNa());
init(new AstSdev());
init(new AstSum());
init(new AstSumAxis());
init(new AstSumNa());
// Time
init(new AstAsDate());
init(new AstDay());
init(new AstDayOfWeek());
init(new AstGetTimeZone());
init(new AstHour());
init(new AstListTimeZones());
init(new AstMillis());
init(new AstMinute());
init(new AstMktime());
init(new AstMoment());
init(new AstMonth());
init(new AstSecond());
init(new AstSetTimeZone());
init(new AstWeek());
init(new AstYear());
// Time Series
init(new AstDiffLag1());
init(new AstIsax());
// Advanced Math
init(new AstCorrelation());
init(new AstDistance());
init(new AstHist());
init(new AstImpute());
init(new AstKFold());
init(new AstMode());
init(new AstSkewness());
init(new AstKurtosis());
init(new AstModuloKFold());
init(new AstQtile());
init(new AstRunif());
init(new AstSort());
init(new AstStratifiedKFold());
init(new AstStratifiedSplit());
init(new AstTable());
init(new AstUnique());
init(new AstVariance());
// Generic data mungers
init(new AstAnyFactor());
init(new AstApply());
init(new AstAsFactor());
init(new AstAsCharacter());
init(new AstAsNumeric());
init(new AstCBind());
init(new AstColNames());
init(new AstColPySlice());
init(new AstColSlice());
init(new AstCut());
init(new AstDdply());
init(new AstFilterNaCols());
init(new AstFlatten());
init(new AstGetrow());
init(new AstGroup());
init(new AstGroupedPermute());
init(new AstIsCharacter());
init(new AstIsFactor());
init(new AstIsNa());
init(new AstIsNumeric());
init(new AstLevels());
init(new AstMerge());
init(new AstNaOmit());
init(new AstColumnsByType());
init(new AstNcol());
init(new AstNLevels());
init(new AstNrow());
init(new AstRBind());
init(new AstReLevel());
init(new AstRename());
init(new AstRowSlice());
init(new AstScale());
init(new AstSetDomain());
init(new AstSetLevel());
init(new AstPivot());
// Assignment; all of these lean heavily on Copy-On-Write optimizations.
init(new AstAppend()); // Add a column
init(new AstAssign()); // Overwrite a global
init(new AstRectangleAssign()); // Overwrite a rectangular slice
init(new AstRm()); // Remove a frame, but maintain internal sharing
init(new AstTmpAssign()); // Create a new immutable tmp frame
// Matrix functions
init(new AstTranspose());
init(new AstMMult());
// String functions
init(new AstCountMatches());
init(new AstCountSubstringsWords());
init(new AstEntropy());
init(new AstLStrip());
init(new AstGrep());
init(new AstReplaceAll());
init(new AstReplaceFirst());
init(new AstRStrip());
init(new AstStrLength());
init(new AstStrSplit());
init(new AstTokenize());
init(new AstSubstring());
init(new AstToLower());
init(new AstToUpper());
init(new AstTrim());
// Miscellaneous
init(new AstComma());
init(new AstLs());
// Search
init(new AstMatch());
init(new AstWhich());
init(new AstWhichMax());
init(new AstWhichMin());
// Repeaters
init(new AstRepLen());
init(new AstSeq());
init(new AstSeqLen());
// Custom (eg. algo-specific)
for (AstPrimitive prim : PrimsService.INSTANCE.getAllPrims())
init(prim);
}
public Env(Session ses) {
_ses = ses;
}
public int sp() {
return _stk.size();
}
private Frame peek(int x) {
return _stk.get(sp() + x);
}
// Deletes dead Frames & forces good stack cleanliness at opcode end. One
// per Opcode implementation. Track frames that are alive mid-execution, but
// dead at Opcode end.
public StackHelp stk() {
return new StackHelp();
}
public class StackHelp implements Closeable {
final int _sp = sp();
// Push & track. Called on every Val that spans a (nested) exec call.
// Used to track Frames with lifetimes spanning other AstRoot executions.
public Val track(Val v) {
if (v instanceof ValFrame) track(v.getFrame());
return v;
}
public Frame track(Frame fr) {
_stk.add(sp(), new Frame(fr._names, fr.vecs().clone())); // Push and track a defensive copy
return fr;
}
// Pop-all and remove dead. If a Frame was not "tracked" above, then if it
// goes dead it will leak on function exit. If a Frame is returned from a
// function and not declared "returning", any Vecs it shares with Frames
// that are dying in this opcode will be deleted out from under it.
@Override
public void close() {
Futures fs = null;
int sp = sp();
while (sp > _sp) {
Frame fr = _stk.remove(--sp); // Pop and stop tracking
fs = _ses.downRefCnt(fr, fs); // Refcnt -1 all Vecs, and delete if zero refs
}
if (fs != null) fs.blockForPending();
}
// Pop last element and lower refcnts - but do not delete. Lifetime is
// responsibility of the caller.
public Val untrack(Val vfr) {
if (!vfr.isFrame()) return vfr;
Frame fr = vfr.getFrame();
_ses.addRefCnt(fr, -1); // Lower counts, but do not delete on zero
return vfr;
}
}
// If an opcode is returning a Frame, it must call "returning(frame)" to
// track the returned Frame. Otherwise shared input Vecs who's last use is
// in this opcode will get deleted as the opcode exits - even if they are
// shared in the returning output Frame.
public <V extends Val> V returning(V val) {
if (val instanceof ValFrame)
_ses.addRefCnt(val.getFrame(), 1);
return val;
}
// ----
// Variable lookup
public Val lookup(String id) {
// Lexically scoped functions first
Val val = _scope == null ? null : _scope.lookup(id);
if (val != null) return val;
// disallow TRUE/FALSE/NA to be overwritten by keys in the DKV... just way way saner this way
if (CONSTS.containsKey(id)) {
return CONSTS.get(id).exec(this);
}
// Now the DKV
Value value = DKV.get(Key.make(expand(id)));
if (value != null) {
if (value.isFrame())
return addGlobals((Frame) value.get());
if (value.isModel())
return new ValModel((Model) value.get());
// Only understand Frames right now
throw new IllegalArgumentException("DKV name lookup of " + id + " yielded an instance of type " + value.className() + ", but only Frame & Model are supported");
}
// Now the built-ins
AstPrimitive ast = PRIMS.get(id);
if (ast != null)
return new ValFun(ast);
throw new IllegalArgumentException("Name lookup of '" + id + "' failed");
}
public String expand(String id) {
return id.startsWith("$")? id.substring(1) + "~" + _ses.id() : id;
}
// Add these Vecs to the global list, and make a new defensive copy of the
// frame - so we can hack it without changing the global frame view.
ValFrame addGlobals(Frame fr) {
_ses.addGlobals(fr);
return new ValFrame(new Frame(fr._names.clone(), fr.vecs().clone()));
}
/*
* Utility & Cleanup
*/
@Override
public String toString() {
String s = "{";
for (int i = 0, sp = sp(); i < sp; i++) s += peek(-sp + i).toString() + ",";
return s + "}";
}
}