use of gov.sandia.n2a.language.Operator in project n2a by frothga.
the class Device method getDefinition.
// / Writes an instance-specific use of an internal device.
public String getDefinition(XyceRenderer renderer) {
StringBuilder result = new StringBuilder();
// Nodes that this device connects to
List<String> nodeNames = new ArrayList<String>();
for (int i = 0; i < varnames.size(); i++) {
Variable v = varnames.get(i);
if (v == ground) {
nodeNames.add("0");
} else if (v != empty) {
nodeNames.add(getInstanceVarname(v.reference, renderer.pi));
}
}
// Its specific parameter values will be based on first written part.
if (model == null) {
model = new HashMap<String, String>();
for (Entry<String, Variable> p : paramList.entrySet()) {
// TODO: select() can return null. Should we guard against it?
model.put(p.getKey(), renderer.change(p.getValue().select(renderer.pi).expression));
}
result.append(Xyceisms.defineModel(device.getDeviceTypeName(), device.getDeviceLevel(), modelName, model));
}
// Instance-specific parameters that override model
Map<String, String> instanceParams = new HashMap<String, String>();
for (Entry<String, Variable> p : paramList.entrySet()) {
Variable v = p.getValue();
if (v.global)
continue;
String name = p.getKey();
// TODO: select() can return null. Should we guard against it?
String value = renderer.change(v.select(renderer.pi).expression);
// no need to override model parameter if value is exactly the same
if (model.get(name).equals(value))
continue;
instanceParams.put(name, value);
}
result.append(Xyceisms.defineYDeviceWithModel(device.getDeviceTypeName(), eqSet.name, nodeNames, renderer.pi.hashCode(), modelName, instanceParams));
// Define inputs
for (Integer inputNode : inputs.keySet()) {
// Write a B device definition to update the node associated with the input
// For neurons, this assumes we want to add a current of the form I/C_m
// where the user specifies I and C_m is known.
// Not at all clear what it means to add current to synapse, which doesn't
// have a capacitance of its own, but this will allow adding a user-defined
// current I to a synapse node.
Variable v = inputs.get(inputNode);
AccessVariable av = new AccessVariable(v.reference);
Operator inputEq;
if (device.getDeviceTypeName().equals("neuron")) {
Divide d = new Divide();
d.operand0 = av;
av = new AccessVariable(paramList.get(device.getCapacitanceVar()).reference);
d.operand1 = av;
inputEq = d;
} else {
inputEq = av;
}
String eqName = Xyceisms.referenceVariable(v.name, renderer.pi.hashCode());
String instanceVarName = getInstanceVarname(v.reference, renderer.pi);
result.append(Xyceisms.updateDiffEq(eqName, instanceVarName, renderer.change(inputEq)));
}
return result.toString();
}
use of gov.sandia.n2a.language.Operator in project n2a by frothga.
the class EquationSet method findConnectionMatrix.
/**
* Detects if $p depends on a NonzeroIterable operator.
* Depends on results of: determineTypes() and clearVariables() -- To provide fake values.
*/
public void findConnectionMatrix() {
for (EquationSet s : parts) {
s.findConnectionMatrix();
}
// Only do this on connections
if (connectionBindings == null)
return;
// Only check binary connections
if (connectionBindings.size() != 2)
return;
Variable p = find(new Variable("$p"));
if (p == null)
return;
// Determine which equation fires during connect phase
Instance instance = new Instance() {
public Type get(Variable v) {
if (v.name.equals("$connect"))
return new Scalar(1);
if (v.name.equals("$init"))
return new Scalar(0);
if (v.name.equals("$live"))
return new Scalar(0);
return v.type;
}
};
Operator predicate = null;
for (// Scan for first equation whose condition is nonzero
EquationEntry e : // Scan for first equation whose condition is nonzero
p.equations) {
if (e.condition == null) {
predicate = e.expression;
break;
}
Type doit = e.condition.eval(instance);
if (doit instanceof Scalar && ((Scalar) doit).value != 0) {
predicate = e.expression;
break;
}
}
if (predicate == null)
return;
// Detect if equation or dependency contains a NonzeroIterable.
class ContainsTransformer implements Transformer {
public NonzeroIterable found;
// Number of times a nonzero iterable was found
public int count;
public boolean substituted;
public Operator transform(Operator op) {
if (op instanceof NonzeroIterable) {
found = (NonzeroIterable) op;
count++;
return op;
}
if (op instanceof AccessVariable) {
// Check if this is a local reference to a single equation.
AccessVariable av = (AccessVariable) op;
Variable v = av.reference.variable;
// We only examine local dependencies.
if (v.container != p.container)
return op;
if (v.equations.size() != 1)
return op;
EquationEntry e = v.equations.first();
if (e.condition != null)
return op;
// Substitute the equation into the predicate.
substituted = true;
Operator result = e.expression.deepCopy();
result.parent = op.parent;
return result;
}
// continue descent
return null;
}
}
ContainsTransformer ct = new ContainsTransformer();
Operator p2 = predicate.deepCopy();
// to prevent infinite recursion
int depthLimit = variables.size();
do {
ct.count = 0;
ct.substituted = false;
p2 = p2.transform(ct);
} while (ct.substituted && depthLimit-- > 0);
if (ct.count != 1)
return;
if (!ct.found.hasCorrectForm())
return;
// then only non-zero elements will produce connections.
try {
Type result = p2.eval(instance);
// Any type other than Scalar is treated as "true", so p2 fails the test.
if (!(result instanceof Scalar))
return;
// Any nonzero value is treated as "true".
if (((Scalar) result).value != 0)
return;
} catch (EvaluationException e) {
return;
}
// Construct
// The NonzeroIterable we found above was a deep-copy, not the original.
// We need to locate and work with the original in order to maintain object
// identity in the finished model.
predicate.visit(new Visitor() {
public int depthLimit = variables.size();
public boolean visit(Operator op) {
if (op instanceof NonzeroIterable) {
ConnectionMatrix cm = new ConnectionMatrix((NonzeroIterable) op);
if (cm.rowMapping != null && cm.colMapping != null) {
connectionMatrix = cm;
// Somewhat of a hack. cm is a one-time process, so we shouldn't do polling.
p.metadata.clear("poll");
}
return false;
}
if (op instanceof AccessVariable) {
AccessVariable av = (AccessVariable) op;
Variable v = av.reference.variable;
if (v.container != p.container)
return false;
if (v.equations.size() != 1)
return false;
EquationEntry e = v.equations.first();
if (e.condition != null)
return false;
depthLimit--;
if (depthLimit >= 0)
e.expression.visit(this);
depthLimit++;
}
return true;
}
});
}
use of gov.sandia.n2a.language.Operator in project n2a by frothga.
the class EquationSet method flatten.
/**
* Convert this equation set into an equivalent object where each included part with $n==1
* (and satisfying a few other conditions) is merged into its containing part.
* Equations with combiners (=+, =*, and so on) are joined together into one long equation
* with the appropriate operator.
* Depends on results of: resolveLHS(), resolveRHS() -- Object identity of variables should already be established.
* @param backend Prefix for metadata keys specific to the engine selected to execute this model.
* Where such keys exist, the parts should not be flattened.
*/
public void flatten(String backend) {
TreeSet<EquationSet> temp = new TreeSet<EquationSet>(parts);
for (final EquationSet s : temp) {
s.flatten(backend);
// Check if connection or endpoint. They must remain separate equation sets for code-generation purposes.
if (s.connectionBindings != null)
continue;
if (s.connected)
continue;
// For similar reasons, if the part contains backend-related metadata, it should remain separate.
if (s.metadata.child("backend", backend) != null)
continue;
// Check if $n==1
if (!s.isSingleton(true))
continue;
// We don't want to overwrite our own $n, so remove it from the sub-part. This won't change its singleton status.
s.variables.remove(new Variable("$n"));
// Don't merge if there are any conflicting $variables.
// Regular variables never conflict, because they get a unique prefix when flattened.
// However, $variables cannot be prefixed. Their semantics are strongly bound to their
// current equation set.
boolean conflict = false;
for (Variable v : s.variables) {
if (!v.name.startsWith("$"))
continue;
// Not a conflict if LHS is an up-reference to this equation set. These equations will get merged.
if (v.reference.variable.container == EquationSet.this)
continue;
Variable d = find(v);
// Not a conflict unless v exists in this equation set.
if (d == null)
continue;
// Not a conflict if v is a $variable added by processing.
if (s.source.child(v.nameString()) == null)
continue;
conflict = true;
break;
}
if (conflict)
continue;
// No conflicts. However, there may still be overlapping $variables. Only $variables unique
// to s will be moved up. For those that do overlap, any references will be redirected to
// the equivalent variable in this container.
// Merge
final String prefix = s.name;
parts.remove(s);
// Variables
// In addition to simply moving the variables up to this container, we need to do incremental maintenance of:
// * Display names, for debugging.
// * Resolution paths.
// Of those, resolution paths are the most difficult, because many different objects are affected:
// * References to variables in s
// - from this container or our parents
// - from children of s
// * References to variables in children of s
// * References from s to other sets
// * References from children of s
// - to s
// - to other sets
// And it is necessary to handle both LHS and RHS references.
// Pass 1 -- Change RHS references originating from variables within s so they function within this container instead.
// Need to do this before variables get moved, because we depend on the identity of their current container.
Visitor prefixer = new Visitor() {
public boolean visit(Operator op) {
if (!(op instanceof AccessVariable))
return true;
AccessVariable av = (AccessVariable) op;
if (// internal reference
av.reference.variable.container == s) {
// cosmetic change
if (!av.name.startsWith("$"))
av.name = prefix + "." + av.name;
} else // external reference
{
// cosmetic change
if (av.name.startsWith("$up."))
av.name = av.name.substring(4);
List<Object> r = av.reference.resolution;
// If first step of resolution is parent, then remove it, because variable is about to become part of parent.
if (r.get(0) == EquationSet.this)
r.remove(0);
}
return false;
}
};
for (Variable v : s.variables) {
v.visit(prefixer);
}
// The expectation is that "from" will be forgotten.
class Redirector implements Visitor {
// original reference target, in child part
Variable from;
// new reference target, in parent part
Variable to;
// current variable being processed; the one that makes the reference
Variable user;
public boolean visit(Operator op) {
if (!(op instanceof AccessVariable))
return true;
AccessVariable av = (AccessVariable) op;
if (av.reference.variable != from)
return false;
if (from != to) {
user.removeDependencyOn(from);
user.addDependencyOn(to);
}
adjustReference(av.reference);
return false;
}
public void adjustReference(VariableReference reference) {
reference.variable = to;
List<Object> r = reference.resolution;
int last = r.size() - 1;
if (// The resolution path has something in it.
last >= 0) {
// The last entry should always be from.container. The only other case, of a connection binding, is eliminated by early tests in the flatten() function.
r.remove(last--);
// The problem is how to distinguish these cases ...
if (// Path was more than 1 step, so check if parent is now last entry.
last >= 0) {
// parent was not last entry, so need to add it
if (r.get(last) != to.container)
r.add(to.container);
} else // Path was 1-step, so check whether descending or ascending.
{
// The user's immediate equation set.
EquationSet p = user.container;
// The parent of the user's equation set. Conceptually, this is the value to check to see if we are ascending.
if (p != null)
p = p.container;
// If redirecting to the same variable, then "from" has already been moved up, so we need the grandparent instead.
if (from == to && p != null)
p = p.container;
// ascending, so add parent
if (p == from.container)
r.add(to.container);
}
}
}
public void redirect(Variable from, Variable to) {
this.from = from;
this.to = to;
// Copy list of users, so we can safely modify the original below.
List<Object> usedBy;
if (from.usedBy == null)
usedBy = new ArrayList<Object>();
else
usedBy = new ArrayList<Object>(from.usedBy);
// Check variables that "from" uses. Some of these may be external writers to "from", which don't necessarily show up in usedBy.
if (from.uses != null) {
// copy, in case it gets modified
List<Variable> uses = new ArrayList<Variable>(from.uses.keySet());
for (Variable v : uses) {
// Null container indicates a connection binding, which won't write to "from" and is not a member of the part being flattened.
if (v.container == null)
continue;
// Skip variables that are not external writers of "from".
if (v.reference.variable != from)
continue;
user = v;
if (from != to) {
from.removeDependencyOn(user);
to.addDependencyOn(user);
}
adjustReference(v.reference);
}
}
// Check variables that use "from".
for (Object o : usedBy) {
if (o instanceof Variable) {
user = (Variable) o;
// RHS references
user.visit(this);
if (// "user" has "from" as an external writer
from.reference.variable == user) {
if (from != to) {
user.removeDependencyOn(from);
user.addDependencyOn(to);
}
// Since "from" is going away, we don't bother adjusting its reference to "user".
}
} else if (o instanceof EquationSet) {
// This case should not occur. Currently, only addSpecials() creates such a
// dependency, and it is for a non-singleton.
from.removeUser(o);
if (o == from.container)
to.addUser(to.container);
else
to.addUser(o);
}
}
}
}
Redirector redirector = new Redirector();
// Note that v may have the same name as a variable in this container. Prefixing will make the name unique.
for (Variable v : s.variables) {
// Adjust LHS references to work in this container.
boolean couldNeedMerge = false;
if (// internal reference, which for LHS is exactly same as self-reference
v.reference.variable == v) {
if (v.name.startsWith("$")) {
// Ignore automatically-added $variables that are not used.
if (!v.hasUsers() && s.source.child(v.nameString()) == null)
continue;
// Check if this is a conflicting $variable.
Variable d = find(v);
if (// Already exists in this container, so redirect any references in v's dependents to d.
d != null) {
// Only does something if v has users.
redirector.redirect(v, d);
// Don't process v further
continue;
}
} else {
v.name = prefix + "." + v.name;
}
} else // external reference
{
if (v.name.startsWith("$up."))
v.name = v.name.substring(4);
else if (v.name.startsWith(name + "."))
v.name = v.name.substring(name.length() + 1);
List<Object> r = v.reference.resolution;
if (r.get(0) == EquationSet.this) {
couldNeedMerge = true;
r.remove(0);
}
}
if (// An external reference whose first resolution step is this container.
couldNeedMerge) {
Variable v2 = find(v);
if (// There is a matching variable, so must merge. The match could be (and often is) the direct target of the reference.
v2 != null) {
// This user must be redirected appropriately.
if (// direct up-reference
v2 == v.reference.variable) {
// But don't replace with dependency from v2 to itself.
v2.removeDependencyOn(v);
} else // Some other variable depends on v.
{
// v should only have one user.
redirector.redirect(v, v2);
}
v2.flattenExpressions(v);
continue;
}
// else fall through ...
}
// A distinct variable that needs to be moved up.
// This is either an internal reference (always unique) or an external reference that does not overlap an existing variable.
// Changes v.container, used by redirector.
add(v);
// Adjust resolution paths of v's users.
redirector.redirect(v, v);
}
// These paths do not target s itself, but rather its parents or deeper children.
class Resolver implements Visitor {
// The container being eliminated (same as "s" in outer code).
EquationSet child;
// Holds "child". Remains after child is eliminated. (Same as EquationSet.this in outer code.)
EquationSet parent;
// The variable in "child" that should be the destination of the resolution path. If null, don't filter (process every reference).
Variable target;
public boolean visit(Operator op) {
if (!(op instanceof AccessVariable))
return true;
AccessVariable av = (AccessVariable) op;
if (target != null && av.reference.variable != target)
return true;
adjustResolution(av.reference.resolution);
return false;
}
public void adjustResolution(List<Object> r) {
// If the path has been maintained in simplest form, then it passes through child only once.
// Our task is to find that point (if it exists) and elide child.
int last = r.size() - 1;
for (int i = 0; i <= last; i++) {
Object o = r.get(i);
if (o != child)
continue;
// Found child in path. Now update it.
// Always remove child
r.remove(i);
// The original path targeted child, so it should now target parent.
if (i == last)
r.add(parent);
break;
}
}
public void resolve(EquationSet child, EquationSet parent) {
this.child = child;
this.parent = parent;
// Instead, we immediately descend to child parts.
for (EquationSet p : child.parts) resolveRecursive(p);
}
public void resolveRecursive(EquationSet current) {
for (Variable v : current.variables) {
if (v.reference.variable != v)
adjustResolution(v.reference.resolution);
target = null;
v.visit(this);
if (v.usedBy == null)
continue;
target = v;
for (Object o : v.usedBy) {
if (o instanceof Variable) {
Variable user = (Variable) o;
if (user.reference.variable != user)
adjustResolution(user.reference.resolution);
user.visit(this);
}
// else if (o instanceof EquationSet) // should not be a relevant case.
// A possibility is o==s. However, this should not occur if flatten() is run immediately after resolveRHS().
}
}
for (EquationSet p : current.parts) resolveRecursive(p);
}
}
Resolver resolver = new Resolver();
resolver.resolve(s, this);
// Parts
for (EquationSet sp : s.parts) {
// s was the former container for sp, but s is going away
sp.container = this;
sp.name = prefix + "." + sp.name;
parts.add(sp);
}
// Metadata
if (s.metadata.size() > 0)
metadata.set(s.metadata, prefix);
// Dependent connections (paths that pass through s)
if (s.dependentConnections != null) {
if (dependentConnections == null)
dependentConnections = new ArrayList<ConnectionBinding>();
for (ConnectionBinding c : s.dependentConnections) {
// By construction, this element must exist.
int i = c.resolution.indexOf(s);
// replace s with this
c.resolution.set(i, this);
if (i + 1 < c.resolution.size() && c.resolution.get(i + 1) == this)
c.resolution.remove(i + 1);
if (i > 0 && c.resolution.get(i - 1) == this)
c.resolution.remove(i - 1);
if (!dependentConnections.contains(c))
dependentConnections.add(c);
}
}
}
}
use of gov.sandia.n2a.language.Operator in project n2a by frothga.
the class EquationSet method resolveRHS.
public void resolveRHS(LinkedList<UnresolvedVariable> unresolved) {
for (EquationSet s : parts) {
s.resolveRHS(unresolved);
}
class Resolver implements Visitor {
public Variable from;
public LinkedList<UnresolvedVariable> unresolved;
public String fromName() {
String result = from.container.prefix();
if (!result.isEmpty())
result += ".";
return result + from.nameString();
}
public boolean visit(Operator op) {
if (op instanceof AccessVariable) {
AccessVariable av = (AccessVariable) op;
Variable query = new Variable(av.getName(), av.getOrder());
VariableReference r = new VariableReference();
query.reference = r;
av.reference = r;
// modifies "r" with actual resolution path
EquationSet dest = resolveEquationSet(query, false);
r.removeLoops();
// dependencies from "from" to each part in the resolution path
r.addDependencies(from);
if (dest == null) {
unresolved.add(new UnresolvedVariable(av.name, fromName()));
} else {
// "query" contains the modified variable name, needed for lookup within "dest"
r.variable = dest.find(query);
if (r.variable == null) {
if (query.hasAttribute("instance")) {
// Configure reference to destination container itself.
// Recycle the query variable as a pseudo target (one that doesn't actually exist in the container).
r.variable = query;
query.container = dest;
query.equations = new TreeSet<EquationEntry>();
query.type = new Instance();
// Only for use by Internal backend. It's easier to set this here than to scan for "instance" variables in InternalBackendData.analyze().
query.readIndex = -2;
} else if (// accountable endpoint
query.name.equals("$count")) {
int last = r.resolution.size() - 1;
Object o = null;
if (last >= 0)
o = r.resolution.get(last);
if (!(o instanceof ConnectionBinding)) {
unresolved.add(new UnresolvedVariable(av.name, fromName()));
} else {
ConnectionBinding cb = (ConnectionBinding) o;
if (dest.accountableConnections == null)
dest.accountableConnections = new TreeSet<AccountableConnection>();
AccountableConnection ac = new AccountableConnection(r.penultimateContainer(EquationSet.this), cb.alias);
if (!dest.accountableConnections.add(ac))
ac = dest.accountableConnections.floor(ac);
if (ac.count == null) {
// Create a fully-functional variable.
// However, it never gets formally added to dest, because dest should never evaluate it.
// Rather, it is maintained by the backend's connection system.
ac.count = new Variable(prefix() + ".$count");
ac.count.type = new Scalar(0);
ac.count.container = dest;
ac.count.equations = new TreeSet<EquationEntry>();
ac.count.reference = new VariableReference();
ac.count.reference.variable = ac.count;
}
r.variable = ac.count;
}
} else {
unresolved.add(new UnresolvedVariable(av.name, fromName()));
}
} else {
from.addDependencyOn(r.variable);
}
}
return false;
}
if (op instanceof Split) {
Split split = (Split) op;
split.parts = new ArrayList<EquationSet>(split.names.length);
EquationSet self = from.reference.variable.container;
// Could be null, if self is top-level model.
EquationSet family = self.container;
for (String partName : split.names) {
EquationSet part;
if (// This allows for $type in top-level model, where no higher container is available to search in.
partName.equals(self.name))
// This allows for $type in top-level model, where no higher container is available to search in.
part = self;
else
part = family.findPart(partName);
if (part != null) {
split.parts.add(part);
Variable query = new Variable("$type");
Variable type = part.find(query);
if (type == null) {
type = query;
part.add(type);
// double-buffer it
type.addAttribute("externalWrite");
type.unit = AbstractUnit.ONE;
type.equations = new TreeSet<EquationEntry>();
type.reference = new VariableReference();
type.reference.variable = type;
}
if (type != from)
type.addDependencyOn(from);
} else {
unresolved.add(new UnresolvedVariable(partName, fromName()));
}
}
return false;
}
return true;
}
}
Resolver resolver = new Resolver();
resolver.unresolved = unresolved;
for (Variable v : variables) {
resolver.from = v;
v.visit(resolver);
}
}
use of gov.sandia.n2a.language.Operator in project n2a by frothga.
the class Variable method isEmptyCombiner.
public boolean isEmptyCombiner() {
if (equations.size() != 1 || assignment == Variable.REPLACE)
return false;
Operator e = equations.first().expression;
if (!e.isScalar())
return false;
double value = e.getDouble();
switch(assignment) {
case Variable.ADD:
return value == 0;
case Variable.MULTIPLY:
case Variable.DIVIDE:
return value == 1;
case Variable.MIN:
return value == Double.POSITIVE_INFINITY;
case Variable.MAX:
return value == Double.NEGATIVE_INFINITY;
}
// This statement should never be reached.
return false;
}
Aggregations