Minsky
interpolateHypercube.cc
Go to the documentation of this file.
1 /*
2  @copyright Russell Standish 2020
3  @author Russell Standish
4  This file is part of Civita.
5 
6  Civita is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Civita is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Civita. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "interpolateHypercube.h"
21 #include <ecolab_epilogue.h>
22 
23 namespace
24 {
25  bool sorted(civita::XVector::const_iterator begin, civita::XVector::const_iterator end)
26  {
27  civita::AnyLess less;
28  if (begin==end-1) return true;
29  for (; begin!=end-1; ++begin)
30  if (!less(*begin,*(begin+1)))
31  return false;
32  return true;
33  }
34 }
35 
36 namespace civita
37 {
38 
39  vector<size_t> InterpolateHC::splitAndRotate(size_t hcIndex) const
40  {
41  vector<size_t> r(rank());
42  const auto& h=hypercube();
43  for (size_t dim=0; dim<rank(); ++dim)
44  r[rotation[dim]] = (hcIndex/strides[dim]) % h.xvectors[dim].size();
45  return r;
46  }
47 
48 
49  void InterpolateHC::setArgument(const TensorPtr& a, const Args&)
50  {
51  arg=a;
52  if (rank()!=arg->rank())
53  throw runtime_error("Rank of interpolated tensor doesn't match its argument");
54  // reorder hypercube for type and name
55  interimHC.xvectors.clear();
56  size_t stride=1;
57  const auto& targetHC=hypercube().xvectors;
58  rotation.clear();
59  rotation.resize(rank(), rank());
60  // ensure rotation vector will contain unique indices
61  std::map<size_t,size_t> tmpRotation;
62  // construct interim hypercube and its rotation permutation
63  for (size_t i=0; i<rank(); ++i)
64  {
65  const auto& src=arg->hypercube().xvectors[i];
66  sortAndAdd(src);
67  if (src.name.empty())
68  {
69  const auto& dst=targetHC[i];
70  if (!dst.name.empty() || src.dimension.type!=dst.dimension.type ||
71  src.dimension.units!=dst.dimension.units) //TODO handle conversion between different units
72  throw runtime_error("mismatch between unnamed dimensions");
73  interimHC.xvectors.push_back(dst);
74  tmpRotation.emplace(make_pair(i,i));
75  }
76  else // find matching names dimension
77  {
78  auto dst=find_if(targetHC.begin(), targetHC.end(),
79  [&](const XVector& i){return i.name==src.name;});
80  if (dst==targetHC.end())
81  {
82  // possible alternative when targetHC has no xvectors or undefined ones. for feature 147
83  interimHC.xvectors.push_back(src);
84  tmpRotation.emplace(make_pair(i,i));
85  }
86  else
87  {
88  interimHC.xvectors.push_back(*dst);
89  tmpRotation.emplace(make_pair(dst-targetHC.begin(),i));
90  }
91  }
92  strides.push_back(stride);
93  stride*=targetHC[i].size();
94  }
95  if (tmpRotation.size()!=rank()) throw runtime_error("rotation of indices is not a permutation");
96  for (auto& i: tmpRotation) rotation[i.first]=i.second;
97 #ifndef NDEBUG
98  for (auto& i: rotation) assert(i<rank()); // check that no indices have been doubly assigned.
99  // Now we're sure rotation is a permutation
100 #endif
101  if (index().empty())
102  for (size_t i=0; i<size(); ++i)
104  else
105  for (auto i: index())
107  }
108 
109  void InterpolateHC::sortAndAdd(const XVector& xv)
110  {
111  sortedArgHC.emplace_back();
112  auto& s=sortedArgHC.back();
113  for (size_t i=0; i<xv.size(); ++i)
114  s.second.push_back(i);
115  AnyLess less;
116  sort(s.second.begin(), s.second.end(),
117  [&](size_t i, size_t j){return less(xv[i],xv[j]);});
118  for (auto i: s.second)
119  s.first.push_back(xv[i]);
120  assert(sorted(s.first.begin(), s.first.end()));
121  }
122 
123 
124  double InterpolateHC::operator[](size_t idx) const
125  {
126  if (idx<weightedIndices.size())
127  {
128  if (weightedIndices[idx].empty()) return nan("");
129  double r=0;
130  for (const auto& i: weightedIndices[idx])
131  {
132  assert(i.index<arg->hypercube().numElements());
133  r+=i.weight * arg->atHCIndex(i.index);
134  }
135  return r;
136  }
137  return nan("");
138  }
139 
141  {
142  // note this agorithm is limited in rank (typically 32 dims on 32bit machine, or 64 dims on 64bit)
143  if (rank()>sizeof(size_t)*8)
144  throw runtime_error("Ranks > "+to_string(sizeof(size_t)*8)+" not supported");
145  size_t numNeighbours=size_t(1)<<rank();
146  vector<size_t> iIdx=splitAndRotate(destIdx);
147  const auto& argHC=arg->hypercube();
148  // loop over the nearest neighbours in argument hypercube space of
149  // this point in interimHypercube space
150  double sumWeight=0;
152  auto argIndexVector=arg->index();
153  // multivariate interpolation - eg see Abramowitz & Stegun 25.2.66
154  for (size_t nbr=0; nbr<numNeighbours; ++nbr)
155  {
156  size_t argIdx=0;
157  double weight=1;
158  size_t idx=0;
159  for (size_t dim=0, stride=1; dim<rank(); stride*=argHC.xvectors[dim].size(), ++dim)
160  {
161  const auto& x=sortedArgHC[dim].first;
162  assert(!x.empty());
163  assert(sorted(x.begin(),x.end()));
164  auto v=interimHC.xvectors[dim][iIdx[dim]];
165  auto lesserIt=std::upper_bound(x.begin(), x.end(), v, AnyLess());
166  if (lesserIt!=x.begin()) --lesserIt; // find greatest value <= v,
167  boost::any lesser=*lesserIt, greater;
168  if (diff(lesser, v)>=0)
169  greater=lesser; // one sided interpolation for beginning or exact match
170  else if (lesserIt+1==x.end())
171  greater=x.back(); // one sided interpolation for end
172  else
173  greater=*(lesserIt+1);
174 
175  bool greaterSide = nbr&(size_t(1)<<dim); // on higher side of hypercube
176  double d=diff(greater,lesser);
177  if (d==0 && greaterSide) goto nextNeighbour; // already taken lesserVal at weight 1.
178 
179  idx += sortedArgHC[dim].second[(lesserIt-x.begin()+greaterSide)]*stride;
180  assert(idx<argHC.numElements());
181  if (d>0)
182  weight *= greaterSide? diff(v,lesser): d-diff(v,lesser);
183  }
184  if (index().empty())
185  {
186  r.emplace_back(WeightedIndex{idx,weight});
187  sumWeight+=weight;
188  }
189  else
190  {
191  auto indexV=index();
192  if (binary_search(indexV.begin(), indexV.end(), idx))
193  {
194  r.emplace_back(WeightedIndex{idx,weight});
195  sumWeight+=weight;
196  }
197  }
198  nextNeighbour:;
199  }
200 
201  // normalise weights
202  for (auto& i: r) i.weight/=sumWeight;
203  return r;
204  }
205 
206 }
std::vector< std::size_t > rotation
permutation of axes of interimHC and this->hypercube()
virtual const Hypercube & hypercube() const
information describing the axes, types and labels of this tensor
std::vector< std::size_t > strides
strides along each dimension of this->hypercube()
std::size_t rank() const
structure for referring to an argument index and its weight
virtual const Index & index() const
the index vector - assumed to be ordered and unique
virtual std::size_t size() const
return number of elements in tensor - maybe less than hypercube.numElements if sparse ...
void setArgument(const TensorPtr &a, const ITensor::Args &args={"", 0}) override
std::size_t hcIndex(const std::initializer_list< T > &indices) const
vector< WeightedIndexVector > weightedIndices
map from this tensor&#39;s index into the argument tensor
arguments relevant for tensor expressions, not always meaningful. Exception thrown if not...
std::vector< std::pair< XVector, std::vector< std::size_t > > > sortedArgHC
vector< std::size_t > splitAndRotate(std::size_t) const
std::shared_ptr< ITensor > TensorPtr
double operator[](std::size_t) const override
return or compute data at a location
WeightedIndexVector bodyCentredNeighbourhood(std::size_t idx) const
computes the neighbourhood around a target argument index when the target index is not on the hypercu...
string to_string(CONST84 char *x)
Definition: minskyTCLObj.h:33
Hypercube interimHC
hypercube that&#39;s been rotated to match the arguments hypercube
bool sorted(civita::XVector::const_iterator begin, civita::XVector::const_iterator end)
void sortAndAdd(const XVector &)