#include #include #include #include #include #include #include #include using std::vector; using std::map; using std::string; using std::runtime_error; using std::logic_error; using std::set; NodeArray::NodeArray(string const &name, Index const &dim) : _name(name), _range(Index(dim.size()), dim) { long length = _range.length(); _node_pointers = new Node *[length]; _offsets = new long[length]; for (long i = 0; i < length; i++) { _node_pointers[i] = 0; _offsets[i] = -1; } } NodeArray::~NodeArray() { delete [] _node_pointers; delete [] _offsets; } bool NodeArray::isEmpty(Range const &target_range) const { if (!_range.contains(target_range)) throw logic_error("Range error in NodeArray::isEmpty"); for (RangeIterator i(target_range); !i.atEnd(); i.nextLeft()) { if (_node_pointers[_range.leftOffset(i)] != 0) return false; } return true; } void NodeArray::insert(Node *node, Range const &target_range) { if (!node) { throw logic_error(string("Attempt to insert NULL node at ") + name() + print(target_range)); } if (node->data.dim(true) != target_range.dim(true)) { throw runtime_error(string("Cannot insert node into ") + name() + print(target_range) + ". Dimension mismatch"); } if (!_range.contains(target_range)) { throw runtime_error(string("Cannot insert node into ") + name() + print(target_range) + ". Range out of bounds"); } if (!isEmpty(target_range)) { throw runtime_error(string("Node ") + name() + print(target_range) + " overlaps previously defined nodes"); } /* Set the _node_pointers array and the offset array */ RangeIterator j(target_range); for (unsigned long k = 0; !j.atEnd(); j.nextLeft(), ++k) { long offset = _range.leftOffset(j); _node_pointers[offset] = node; _offsets[offset] = k; } /* Add to the graph */ _graph.add(node); } Node *NodeArray::find(Range const &target_range) const { if (!_range.contains(target_range)) { return 0; } long offset = _range.leftOffset(target_range.lower()); Node *node = _node_pointers[offset]; if (!node) return 0; if (node->data.dim(true) != target_range.dim(true)) return 0; RangeIterator j(target_range); for (long k = 0; !j.atEnd(); j.nextLeft(), ++k) { offset = _range.leftOffset(j); if (_node_pointers[offset] != node || _offsets[offset] != k) return 0; } return node; } Node *NodeArray::getSubset(Range const &target_range) { /* If range corresponds to a set node, then return this */ Node *node = find(target_range); if (node) return node; /* If range corresponds to a previously created subset, then return this */ map::iterator p = _generated_nodes.find(target_range); if (p != _generated_nodes.end()) { return p->second; } /* Otherwise create an aggregate node */ vector nodes; vector offsets; for (RangeIterator i(target_range); !i.atEnd(); i.nextLeft()) { long offset = _range.leftOffset(i); if (_node_pointers[offset] == 0) { return 0; } nodes.push_back(_node_pointers[offset]); offsets.push_back(_offsets[offset]); } node = new AggNode(target_range.dim(false), nodes, offsets); _generated_nodes[target_range] = node; _graph.add(node); return node; } void NodeArray::setValue(SArray const &value, bool observed) { if (!(_range == value.range())) { throw runtime_error(string("Dimension mismatch when setting value of node array ") + name()); } double const *x = value.value(); long j = 0; for (RangeIterator i(_range); !i.atEnd(); i.nextLeft(), ++j) { double fvalue = x[j]; if (fvalue != JAGS_NA) { Node *node = _node_pointers[j]; if (node == 0) { if (observed) { node = new ConstantNode(fvalue); insert(node, i); } else { throw runtime_error(string("Attempt to set value of undefined node ") + name() + print(i)); } } else { if (!isObserved(node)) { node->data.setValue(fvalue, _offsets[j]); } else { throw NodeError(node, "Attempt to change value of data node"); } } } } //Gather all the nodes for which a data value is supplied set setnodes; for (unsigned long i = 0; i < _range.length(); ++i) { if (x[i] != JAGS_NA) { setnodes.insert(_node_pointers[i]); } } // Check to see if Node's value is partially missing for (set::const_iterator p = setnodes.begin(); p != setnodes.end(); ++p) { double const *member_value = (*p)->data.value(); unsigned long member_length = (*p)->data.length(); for (unsigned long j = 0; j < member_length; ++j) { if (member_value[j] == JAGS_NA) { throw NodeError(*p,"Values supplied for node are partially missing"); } } if (observed) { (*p)->data.setFixed(true); } } } void NodeArray::getValue(SArray &value, bool observed) const { if (!(_range == value.range())) { string msg("Dimension mismatch when getting value of node array "); msg.append(name()); throw runtime_error(msg); } long array_length = _range.length(); double *array_value = new double[array_length]; for (int j = 0; j < array_length; ++j) { Node const *node = _node_pointers[j]; if (node && node->isStochastic() && isObserved(node) == observed) { array_value[j] = node->data.value()[_offsets[j]]; } else { array_value[j] = JAGS_NA; } } value.setValue(array_value, array_length); delete [] array_value; } void NodeArray::getAllValues(SArray &value) const { if (!(_range == value.range())) { string msg("Dimension mismatch when getting value of node array "); msg.append(name()); throw runtime_error(msg); } long array_length = _range.length(); double *array_value = new double[array_length]; for (int j = 0; j < array_length; ++j) { Node const *node = _node_pointers[j]; if (node) { array_value[j] = node->data.value()[_offsets[j]]; } else { array_value[j] = JAGS_NA; } } value.setValue(array_value, array_length); delete [] array_value; } string const &NodeArray::name() const { return _name; } Range const &NodeArray::range() const { return _range; } Graph const &NodeArray::graph() const { return _graph; } bool NodeArray::findActiveIndices(Index &ind, unsigned int k, Index const &lower, Index const &dim) const { /* We pay a heavy computational price for the flexibility of allowing users to insert multivariate nodes in arbritary ways into the NodeArray. Suppose we have an array of dimension [3,4,2,5], the lower index is [1,2,1,2] and the dimension of the node is [3,2]. Then the node could be inserted in 5 different ways. [1:3, 2:3, 1, 2] Active indices (0,1) [1:3, 2, 1:2, 2] (0,2) [1:3, 2, 1, 2:3] (0,3) [1, 2:4, 1:2, 2] (1,2) [1, 2:4, 1, 2:3] (1,3) We can't have active indices (2,3) because the node won't fit */ if (k == 0) ind[k] = 0; else ind[k] = ind[k-1] + 1; int m = ind.size(); int M = _range.ndim(false); for (;ind[k] <= M + k - m; ind[k] = ind[k] + 1) { if (k == m - 1) { Index upper(lower); for (unsigned int l = 0; l < m; ++l) { upper[ind[l]] = upper[ind[l]] + dim[l] - 1; } Range test_range(lower, upper); if (_range.contains(test_range)) { Node *node = _node_pointers[_range.leftOffset(lower)]; unsigned int j = 0; bool ok = true; for (RangeIterator i(test_range); !i.atEnd(); i.nextLeft(), ++j) { long offset = _range.leftOffset(i); if (_node_pointers[offset] != node || _offsets[offset] != j) { ok = false; break; } } if (ok) return true; } } else { if (findActiveIndices(ind, k+1, lower, dim)) return true; } } return false; } Range NodeArray::getRange(Node const *node) const { if (!_graph.contains(node)) { return Range(); } //Look in the generated nodes first for (map::const_iterator p = _generated_nodes.begin(); p != _generated_nodes.end(); ++p) { if (node == p->second) return p->first; } /* Find the lower limit of the range. This is easy */ unsigned int ndim = _range.ndim(false); Index lower(ndim), upper(ndim); long j = 0; for (; j < _range.length(); ++j) { if (_node_pointers[j] == node) { lower = _range.leftIndex(j); break; } } if (j == _range.length()) { return Range(); } unsigned int m = node->data.ndim(false); Index ind(m); if (findActiveIndices(ind, 0, lower, node->data.dim(false))) { upper = lower; for (unsigned int l = 0; l < m; ++l) { upper[ind[l]] = upper[ind[l]] + node->data.dim(false)[l] - 1; } return Range(lower, upper); } else { throw logic_error("Unable to find node range"); } }