Hi Eric,
On Sun, Jul 15, 2012 at 6:06 PM, eaeast <
eae...@gmail.com> wrote:
> Hi,
>
> Is there a way to know if a variable is supposed to be a vector iin an
> expression?
>
> ex expression:
> x=sum(y);
>
> How can I determine that y is a vector?
>
> ex2 expression: Declare a vectory type and size?
> # declare x=vector(size=10]; ?
> y=x+2;
>
> Thanks
> Eric
The following two classes may be of use DimensionVisitor walks through
a parse tree calculating the dimensions on each node. Dimensions is
used to store the size of each item, which can be scalers, vectors or
matrices. Jep has limited suport for vectors and matrices I'm working
on an extension to jep which has improved support.
The following indicate typical usage
@Test
public void testDimensions() throws JepException {
Jep jep=new Jep();
DimensionVisitor dv=new DimensionVisitor(jep);
Node n1=jep.parse("v=[1,2,3]"); // Assignment of variable
Dimensions d1=dv.visit(n1);
assertEquals(Dimensions.THREE,d1);
d1=dv.getVarDim(jep.getVariable("v"));
assertEquals(Dimensions.THREE,d1);
Node n2=jep.parse("3 v"); // Scalar product
Dimensions d2=dv.visit(n2);
assertEquals(Dimensions.THREE,d2);
Node n3=jep.parse("v+v");
Dimensions d3=dv.visit(n3);
assertEquals(Dimensions.THREE,d3);
Node n4=jep.parse("v . v"); // Dot product
Dimensions d4=dv.visit(n4);
assertEquals(Dimensions.ONE,d4);
Node n5=jep.parse("v^^v"); // Cross product
Dimensions d5=dv.visit(n5);
assertEquals(Dimensions.THREE,d5);
Variable w=jep.addVariable("w"); // Set dimensions of a variable
dv.setVarDim(w, Dimensions.THREE);
Node n6=jep.parse("v+w");
Dimensions d6=dv.visit(n6);
assertEquals(Dimensions.THREE,d6);
}
***************************** Dimensions ******************************
/* @author rich
* Created on 25-Oct-2003
*/
package com.singularsys.jeptests.system.dimensions;
import java.io.Serializable;
/**
* A class to represent a set of dimensions.
* Might be 1 for 0-dimensional numbers.
* [3] for a 3D vector
* [3,3] for a matrix
* @author rich
* Created on 25-Oct-2003
*/
public class Dimensions implements Serializable
{
private static final long serialVersionUID = 4492131381699627649L;
public static final Dimensions UNKNOWN = new Dimensions(-1);
public static final Dimensions ONE = new Dimensions(1);
public static final Dimensions TWO = new Dimensions(2);
public static final Dimensions THREE = new Dimensions(3);
private int dims[];
private Dimensions() { /* empty constructor to prevent creation */ }
/** Sets the dimension to a single number. Implies its a Scaler or
MVector. */
private Dimensions(int d) { dims = new int[] {d}; }
/** Use this method for matrices. */
private Dimensions(int d1,int d2) { dims = new int[] {d1,d2}; }
/** Construct a dimension set from an array. */
private Dimensions(int d[])
{
dims = new int[d.length];
System.arraycopy(d,0,dims,0,d.length);
}
/**
* Factory method returns a Dimension for vector of given length.
* For 1,2,3 returns ONE,TWO or THREE otherwise create a new object.
*/
public static Dimensions valueOf(int d)
{
switch(d) {
case 1: return Dimensions.ONE;
case 2: return Dimensions.TWO;
case 3: return Dimensions.THREE;
default:
return new Dimensions(d);
}
}
/** returns dimensions for a matrix. **/
public static Dimensions valueOf(int rows,int cols)
{
return new Dimensions(rows,cols);
}
/** return a dimension [d,inDim[0],...,inDim[n]] */
public static Dimensions valueOf(int d,Dimensions inDim)
{
Dimensions res = new Dimensions();
res.dims = new int[inDim.rank()+1];
res.dims[0]=d;
for(int i=0;i<inDim.rank();++i)
res.dims[i+1]=inDim.dims[i];
return res;
}
/** return a dimension [inDim[0],...,inDim[n],d] */
public static Dimensions valueOf(Dimensions inDim,int d)
{
Dimensions res = new Dimensions();
res.dims = new int[inDim.rank()+1];
for(int i=0;i<inDim.rank();++i)
res.dims[i]=inDim.dims[i];
res.dims[inDim.rank()+1]=d;
return res;
}
/** returns a dimensions with given dimensions. */
public static Dimensions valueOf(int dims[])
{
if(dims.length == 1) return valueOf(dims[0]);
if(dims.length == 2) return valueOf(dims[0],dims[1]);
return new Dimensions(dims);
}
/** get the first dimension, 1 for numbers,
or the length of a vector.
for a matrix [[1,2,3],[4,5,6]] first dim is number of rows eg 2 */
public int getFirstDim() { return dims[0]; }
/** get the last dimension, 1 for numbers,
or the length of a vector.
for a matrix [[1,2,3],[4,5,6]] last dim is number of cols eg 3
*/
public int getLastDim() { return dims[dims.length-1]; }
public int getIthDim(int i) { return dims[i]; }
/** Is it 0D, ie a simple number. **/
public boolean is0D() { return dims.length == 1 && dims[0] == 1; }
/** Is it 1D, ie a vector [1,2,3]. **/
public boolean is1D() { return dims.length == 1 && dims[0] != 1; }
/** Is it 2D, ie a matrix [[1,2,3],[4,5,6]]. **/
public boolean is2D() { return dims.length == 2; }
/**
* The total number of elements.
* Produce of all the dimensions.
*/
public int numEles()
{
int res=1;
for(int i=0;i<dims.length;++i) res *= dims[i];
return res;
}
/** rank of dimensions 0 for numbers, 1 for vectors, 2 for matrices */
public int rank()
{
if(is0D()) return 0;
return dims.length;
}
/** A string representation.
* Either 1,n,[m,n],[l,m,n] etc.
*/
@Override
public String toString()
{
if(is0D()) return String.valueOf(dims[0]);
if(is1D()) return String.valueOf(dims[0]);
StringBuffer sb = new StringBuffer("["+dims[0]);
for(int i=1;i<dims.length;++i)
sb.append(","+dims[i]);
sb.append("]");
return sb.toString();
}
/** Two dimensions are equal if the element of dims are the same. */
public boolean equalsDim(Dimensions dims2)
{
if(dims2 == null) return false;
if( dims.length != dims2.dims.length) return false;
for(int i=0;i<dims.length;++i)
{ if(dims[i] != dims2.dims[i]) return false;}
return true;
}
/** apparently your should always override hashcode when you
* override equals (Effective Java, Bloch).
*/
@Override
public int hashCode()
{
int res =17;
for(int i=0;i<dims.length;++i)
res = 37*res + dims[i];
return res;
}
@Override
public boolean equals(Object arg)
{
if(arg == null) return false;
if(arg instanceof Dimensions) return equalsDim((Dimensions) arg);
return false;
}
private Object readResolve() {
if(this.dims.length == 1)
{
if(this.dims[0]==1) return ONE;
else if(this.dims[0]==2) return TWO;
else if(this.dims[0]==3) return THREE;
}
return this;
}
}
***************************** DimensionVisitor ******************************
package com.singularsys.jeptests.system.dimensions;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import com.singularsys.jep.Jep;
import com.singularsys.jep.JepComponent;
import com.singularsys.jep.JepException;
import com.singularsys.jep.Operator;
import com.singularsys.jep.OperatorTableI;
import com.singularsys.jep.ParseException;
import com.singularsys.jep.ParserVisitor;
import com.singularsys.jep.Variable;
import com.singularsys.jep.parser.ASTConstant;
import com.singularsys.jep.parser.ASTFunNode;
import com.singularsys.jep.parser.ASTOpNode;
import com.singularsys.jep.parser.ASTVarNode;
import com.singularsys.jep.parser.Node;
import com.singularsys.jep.parser.Node.HookKey;
/**
* Annotates nodes with the dimension of the vector or matrix
* @author Richard Morris
* @since Jep Extensions 2.0
*/
public class DimensionVisitor implements ParserVisitor, JepComponent {
private static final long serialVersionUID = 350L;
public static final HookKey DIM_KEY = new HookKey() {
@Override
public String toString() {
return "DIM_KEY";
}};
Map<Variable,Dimensions> varDims = new HashMap<Variable,Dimensions>();
Jep jep;
OperatorTableI ot;
public DimensionVisitor(Jep jep1) {
jep = jep1;
ot = jep.getOperatorTable();
}
public static final Dimensions getDimByValue(Object val)
throws ParseException
{
if(val instanceof Number || val instanceof Boolean) {
return Dimensions.ONE;
}
else if(val instanceof Vector<?>) {
int len = ((Vector<?>) val).size();
Object o = ((Vector<?>) val).get(0);
if(o instanceof Vector<?>) {
int len2 = ((Vector<?>) o).size();
return Dimensions.valueOf(len, len2);
}
return Dimensions.valueOf(len);
}
throw new ParseException("Sorry, can only handle scaler,
2, 3 and 4 dimensional vectors and matrices");
}
/**
* Annotate every node in the tree.
* @param node root node of the expression
* @return dimensions of the root node
* @throws JepException
*/
public Dimensions visit(Node node) throws JepException {
Dimensions res = (Dimensions) node.jjtAccept(this, null);
return res;
}
@Override
public Dimensions visit(ASTConstant node, Object data) throws
JepException {
Dimensions dims = DimensionVisitor.getDimByValue(node.getValue());
node.setHook(DIM_KEY, dims);
return dims;
}
@Override
public Dimensions visit(ASTVarNode node, Object data) throws
JepException {
Variable var = node.getVar();
Dimensions vdim = varDims.get(var);
if(var.hasValidValue()) {
Object val = var.getValue();
Dimensions dim = DimensionVisitor.getDimByValue(val);
if(vdim==null) {
varDims.put(var, dim);
node.setHook(DIM_KEY, dim);
return dim;
} else if(vdim.equalsDim(dim)) {
node.setHook(DIM_KEY, dim);
return dim;
} else {
System.out.println("DimensionVisitor: variable
dimensions differ "+vdim+" "+dim);
varDims.put(var, dim);
node.setHook(DIM_KEY, dim);
return dim;
}
// throw new
JepException("DimensionVisitor: variable dimensions differ "+vdim+"
"+dim);
}
if(vdim==null)
throw new JepException("DimensionVisitor: unknown
dimension for variable "+var.toString());
node.setHook(DIM_KEY, vdim);
return vdim;
}
private Dimensions visitAssign(ASTOpNode node, Object data)
throws JepException {
Dimensions rdim = (Dimensions)
node.jjtGetChild(1).jjtAccept(this, data);
Variable var = node.jjtGetChild(0).getVar();
Dimensions vdim = varDims.get(var);
varDims.put(var, rdim);
node.setHook(DIM_KEY, rdim);
node.jjtGetChild(0).setHook(DIM_KEY, rdim);
if(!(vdim==null) && !vdim.equalsDim(rdim)) {
System.out.println("DimensionVisitor: assign variable
dimensions differ "+vdim+" "+rdim);
var.setValidValue(false);
}
return rdim;
}
@Override
public Dimensions visit(ASTFunNode node, Object data) throws
JepException {
Dimensions[] childDim = new Dimensions[node.jjtGetNumChildren()];
for(int i=0;i<childDim.length;++i)
childDim[i]= (Dimensions)
node.jjtGetChild(i).jjtAccept(this, data);
for(int i=0;i<childDim.length;++i)
if(!childDim[i].is0D())
throw new ParseException("RpEval: Sorry only
functions with scalar arguments are supported");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
@Override
public Dimensions visit(ASTOpNode node, Object data) throws
JepException {
if(node.getOperator() == ot.getAssign()) {
return visitAssign(node,data);
}
Dimensions[] childDim = new Dimensions[node.jjtGetNumChildren()];
for(int i=0;i<childDim.length;++i)
childDim[i]= (Dimensions)
node.jjtGetChild(i).jjtAccept(this, data);
Operator op = node.getOperator();
int nChild = node.jjtGetNumChildren();
Dimensions ldims = null,rdims=null;
if(op.isBinary())
if(nChild!=2)
throw new ParseException("DimensionVisitor: binary
operator must have two children, but it has "+nChild);
if(op.isUnary())
if(nChild!=1)
throw new ParseException("DimensionVisitor: unary
operator must have one child, but it has "+nChild);
if(nChild==1)
{
ldims = childDim[0];
if(op == ot.getUMinus())
{
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(op == ot.getNot())
{
if(!ldims.is0D())throw new
ParseException("Dimension of operand for not operator must be one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
}
else if(nChild>=2)
{
ldims = childDim[0];
rdims = childDim[1];
if(op == ot.getAdd())
{
if(!ldims.equalsDim(rdims))
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(op == ot.getSubtract())
{
if(!ldims.equalsDim(rdims))
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(op == ot.getMultiply())
{
Dimensions res = this.calcMultiplyDim(ldims, rdims);
node.setHook(DIM_KEY, res);
return res;
}
else if(op == ot.getDot())
{
if( ! ldims.is1D() || ! rdims.is1D() )
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getCross())
{
if(ldims.equalsDim(Dimensions.THREE) &&
rdims.equalsDim(Dimensions.THREE))
{
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(ldims.equalsDim(Dimensions.TWO) &&
rdims.equalsDim(Dimensions.TWO))
{
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else throw new ParseException("Bad dimensions for
cross product "+ldims+" "+rdims);
}
else if(op == ot.getAssign())
{
if(!ldims.equalsDim(rdims))
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(op == ot.getEQ())
{
if(!ldims.equalsDim(rdims))
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getNE())
{
if(!ldims.equalsDim(rdims))
throw new ParseException("DimensionVisitor:
dims for add must be equal");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getLT())
{
if(!ldims.is0D() || !rdims.is0D()) throw new
ParseException("Dimensions of operands for < operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getGT())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for > operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getLE())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for <= operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getGE())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for >= operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getAnd())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for && operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getOr())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for || operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getDivide())
{
if(!rdims.is0D())throw new ParseException("RHS
operands of / operator must be a Scaler");
node.setHook(DIM_KEY, ldims);
return ldims;
}
else if(op == ot.getMod())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for % operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
else if(op == ot.getPower())
{
if(!ldims.is0D() || !rdims.is0D())throw new
ParseException("Dimensions of operands for ^ operator must both be
one");
node.setHook(DIM_KEY, Dimensions.ONE);
return Dimensions.ONE;
}
}
if(op == ot.getList())
{
Dimensions res =
Dimensions.valueOf(childDim.length,childDim[0]);
node.setHook(DIM_KEY, res);
return res;
}
throw new ParseException("DimensionVisitor: Sorry
unsupported operator/function: "+ node.getName());
}
public Dimensions calcMultiplyDim(Dimensions l,Dimensions r)
throws ParseException
{
int lrank = l.rank();
int rrank = r.rank();
switch(lrank)
{
case 0: // Scaler res
return r;
case 1: // Vector * ?
switch(rrank)
{
case 0: // Vector * Scaler -> Vector
return l;
case 1: // Vector * Vector -> Matrix
return Dimensions.valueOf(l.getFirstDim(),r.getFirstDim());
case 2: // Vector * Matrix -> Vector
if(l.getLastDim() == r.getFirstDim())
return Dimensions.valueOf(r.getLastDim());
break;
default: // Tensor res
throw new ParseException("Sorry I don't know how
to multiply a vector by a tensor");
}
break;
case 2: // Matrix * ?
switch(rrank)
{
case 0: // Matrix * Scaler -> Matrix
return l;
case 1: // Matrix * Vector -> Vector
if(l.getLastDim() == r.getFirstDim())
return Dimensions.valueOf(l.getFirstDim());
break;
case 2: // Matrix * Matrix -> Matrix
if(l.getLastDim() == r.getFirstDim()) return
Dimensions.valueOf(l.getFirstDim(),r.getLastDim());
break;
default: // Tensor res
//throw new ParseException("Sorry I don't know how
to multiply a matrix by a tensor");
}
break;
default: // Tensor res
switch(rrank)
{
case 0: // Scaler res
return l;
// case 1: // Vector res
// throw new ParseException("Sorry I don't know
how to multiply a tensor by a vector");
// case 2: // Matrix res
// throw new ParseException("Sorry I don't know
how to multiply a tensor by a matrix");
// default: // Tensor res
// throw new ParseException("Sorry I don't know
how to multiply a tensor by a tensor");
}
}
throw new ParseException("Dimensions for multiply do not
match: "+l+" "+r);
}
@Override
public void init(Jep jep1) {
this.jep = jep1;
this.ot = jep.getOperatorTable();
}
@Override
public JepComponent getLightWeightInstance() {
return null;
}
/**
* Clear record of variable dimensions.
*/
public void clear() {
varDims.clear();
}
/**
* Get the dimensions of a given variable.
* @param key
* @return
*/
public Dimensions getVarDim(Variable key) {
return varDims.get(key);
}
/**
* Set the dimensions of a variable
* @param key
* @param dims
* @return
*/
public Dimensions setVarDim(Variable key,Dimensions dims) {
return varDims.put(key,dims);
}
}