xerus
a general purpose tensor library
tensor.cpp
Go to the documentation of this file.
1 // Xerus - A General Purpose Tensor Library
2 // Copyright (C) 2014-2017 Benjamin Huber and Sebastian Wolf.
3 //
4 // Xerus is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as published
6 // by the Free Software Foundation, either version 3 of the License,
7 // or (at your option) any later version.
8 //
9 // Xerus is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
13 //
14 // You should have received a copy of the GNU Affero General Public License
15 // along with Xerus. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // For further information on Xerus visit https://libXerus.org
18 // or contact us at contact@libXerus.org.
19 
25 #include <xerus/tensor.h>
26 #include <xerus/misc/check.h>
27 
30 #include <xerus/misc/math.h>
31 #include <xerus/misc/internal.h>
32 
34 #include <xerus/cholmod_wrapper.h>
36 #include <xerus/index.h>
37 #include <xerus/tensorNetwork.h>
38 
39 #include <xerus/cholmod_wrapper.h>
40 
41 #include <fstream>
42 #include <iomanip>
43 
44 namespace xerus {
45  size_t Tensor::sparsityFactor = 4;
46 
47  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Constructors - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
48 
49  Tensor::Tensor(const Representation _representation) : Tensor(DimensionTuple({}), _representation) { }
50 
51 
52  Tensor::Tensor(DimensionTuple _dimensions, const Representation _representation, const Initialisation _init)
53  : dimensions(std::move(_dimensions)), size(misc::product(dimensions)), representation(_representation)
54  {
55  REQUIRE(size != 0, "May not create tensors with an dimension == 0.");
56 
58  denseData.reset(new value_t[size], internal::array_deleter_vt);
59  if(_init == Initialisation::Zero) {
60  misc::set_zero(denseData.get(), size);
61  }
62  } else {
63  sparseData.reset(new std::map<size_t, value_t>());
64  }
65  }
66 
67 
68  Tensor::Tensor(DimensionTuple _dimensions, std::unique_ptr<value_t[]>&& _data)
69  : dimensions(std::move(_dimensions)), size(misc::product(dimensions)), representation(Representation::Dense), denseData(_data.release()) {
70  REQUIRE(size != 0, "May not create tensors with an dimension == 0.");
71  }
72 
73 
74  Tensor::Tensor(DimensionTuple _dimensions, const std::function<value_t()>& _f) : Tensor(std::move(_dimensions), Representation::Dense, Initialisation::None) {
75  value_t* const realData = denseData.get();
76  for (size_t i=0; i < size; ++i) {
77  realData[i] = _f();
78  }
79  }
80 
81 
82  Tensor::Tensor(DimensionTuple _dimensions, const std::function<value_t(const size_t)>& _f) : Tensor(std::move(_dimensions), Representation::Dense, Initialisation::None) {
83  value_t* const realData = denseData.get();
84  for (size_t i=0; i < size; ++i) {
85  realData[i] = _f(i);
86  }
87  }
88 
89 
90  Tensor::Tensor(DimensionTuple _dimensions, const std::function<value_t(const MultiIndex&)>& _f) : Tensor(std::move(_dimensions), Representation::Dense, Initialisation::None) {
91  value_t* const realData = denseData.get();
92  MultiIndex multIdx(degree(), 0);
93  size_t idx = 0;
94  while (true) {
95  realData[idx] = _f(multIdx);
96  // Increasing indices
97  idx++;
98  size_t changingIndex = degree()-1;
99  multIdx[changingIndex]++;
100  while(multIdx[changingIndex] == dimensions[changingIndex]) {
101  multIdx[changingIndex] = 0;
102  changingIndex--;
103  // Return on overflow
104  if(changingIndex >= degree()) { return; }
105  multIdx[changingIndex]++;
106  }
107  }
108  }
109 
110 
111  Tensor::Tensor(DimensionTuple _dimensions, const size_t _N, const std::function<std::pair<size_t, value_t>(size_t, size_t)>& _f) : Tensor(std::move(_dimensions), Representation::Sparse, Initialisation::Zero) {
112  REQUIRE(_N <= size, "Cannot create more non zero entries that the dimension of the Tensor.");
113  for (size_t i = 0; i < _N; ++i) {
114  std::pair<size_t, value_t> entry = _f(i, size);
115  REQUIRE(entry.first < size, "Postion is out of bounds " << entry.first);
116  REQUIRE(!misc::contains(*sparseData, entry.first), "Allready contained " << entry.first);
117  sparseData->insert(std::move(entry));
118  }
119  }
120 
121 
123  Tensor ret(std::move(_dimensions), Representation::Dense, Initialisation::None);
124  value_t* const data = ret.get_dense_data();
125  for(size_t i = 0; i < ret.size; ++i) {
126  data[i] = 1.0;
127  }
128  return ret;
129  }
130 
132  REQUIRE(_dimensions.size()%2 == 0, "Identity tensor must have even degree, here: " << _dimensions.size());
133  const size_t d = _dimensions.size();
134 
135  Tensor ret(std::move(_dimensions), Representation::Sparse);
136  MultiIndex position(d, 0);
137 
138  if(d == 0) {
139  ret[position] = 1.0;
140  } else {
141  bool notMultiLevelBreak = true;
142  while(notMultiLevelBreak) {
143  ret[position] = 1.0;
144 
145  position[0]++; position[d/2]++;
146  size_t node = 0;
147  while(position[node] == std::min(ret.dimensions[node], ret.dimensions[d/2+node])) {
148  position[node] = position[d/2+node] = 0;
149  node++;
150  if(node == d/2) {notMultiLevelBreak = false; break;}
151  position[node]++; position[d/2+node]++;
152  }
153  }
154  }
155 
156  return ret;
157  }
158 
159 
161  Tensor ret(std::move(_dimensions), Representation::Sparse);
162  if(ret.degree() == 0) {
163  ret[{}] = 1.0;
164  } else {
165  for(size_t i = 0; i < misc::min(ret.dimensions); ++i) {
166  ret[MultiIndex(ret.degree(), i)] = 1.0;
167  }
168  }
169  return ret;
170  }
171 
172 
173  Tensor Tensor::dirac(DimensionTuple _dimensions, const MultiIndex& _position) {
174  Tensor ret(std::move(_dimensions), Representation::Sparse);
175  ret[_position] = 1.0;
176  return ret;
177  }
178 
179 
180  Tensor Tensor::dirac(DimensionTuple _dimensions, const size_t _position) {
181  Tensor ret(std::move(_dimensions), Representation::Sparse);
182  ret[_position] = 1.0;
183  return ret;
184  }
185 
186 
188  Tensor ret(*this);
190  return ret;
191  }
192 
193 
195  Tensor ret(*this);
197  return ret;
198  }
199 
201  return operator=(Tensor(_network));
202  }
203 
204 
205  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Information - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
206  size_t Tensor::degree() const {
207  return dimensions.size();
208  }
209 
210 
211  bool Tensor::has_factor() const {
212 // return std::abs(1.0-factor) > std::numeric_limits<double>::epsilon();
213  #pragma GCC diagnostic push
214  #pragma GCC diagnostic ignored "-Wfloat-equal"
215  return (factor != 1.0);
216  #pragma GCC diagnostic pop
217  }
218 
219 
220  bool Tensor::is_dense() const {
221  INTERNAL_CHECK((representation == Representation::Dense && denseData && !sparseData) || (representation == Representation::Sparse && sparseData && !denseData), "Internal Error: " << bool(representation) << bool(denseData) << bool(sparseData));
223  }
224 
225 
226  bool Tensor::is_sparse() const {
227  INTERNAL_CHECK((representation == Representation::Dense && denseData && !sparseData) || (representation == Representation::Sparse && sparseData && !denseData), "Internal Error: " << bool(representation) << bool(denseData) << bool(sparseData));
229  }
230 
231 
232  size_t Tensor::sparsity() const {
233  if(is_sparse()) {
234  return sparseData->size();
235  }
236  return size;
237  }
238 
239 
240  size_t Tensor::count_non_zero_entries(const value_t _eps) const {
241  if(is_dense()) {
242  size_t count = 0;
243  for(size_t i = 0; i < size; ++i) {
244  if(std::abs(denseData.get()[i]) > _eps ) { count++; }
245  }
246  return count;
247  }
248  size_t count = 0;
249  for(const auto& entry : *sparseData) {
250  if(std::abs(entry.second) > _eps) { count++; }
251  }
252  return count;
253  }
254 
255 
257  if(is_dense()) {
258  for(size_t i = 0; i < size; ++i) {
259  if(!std::isfinite(denseData.get()[i])) { return false; }
260  }
261  } else {
262  for(const auto& entry : *sparseData) {
263  if(!std::isfinite(entry.second)) {return false; }
264  }
265  }
266  return true;
267  }
268 
269 
270  size_t Tensor::reorder_cost() const {
271  return is_sparse() ? 10*sparsity() : size;
272  }
273 
274 
276  if(is_dense()) {
277  return std::abs(factor)*blasWrapper::one_norm(denseData.get(), size);
278  }
279  value_t norm = 0;
280  for(const auto& entry : *sparseData) {
281  norm += std::abs(entry.second);
282  }
283  return std::abs(factor)*norm;
284  }
285 
287  if(is_dense()) {
288  return std::abs(factor)*blasWrapper::two_norm(denseData.get(), size);
289  }
290  value_t norm = 0;
291  for(const auto& entry : *sparseData) {
292  norm += misc::sqr(entry.second);
293  }
294  return std::abs(factor)*sqrt(norm);
295  }
296 
297 
298  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Basic arithmetics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
299  Tensor& Tensor::operator+=(const Tensor& _other) {
300  plus_minus_equal<1>(*this, _other);
301  return *this;
302  }
303 
304 
305  Tensor& Tensor::operator-=(const Tensor& _other) {
306  plus_minus_equal<-1>(*this, _other);
307  return *this;
308  }
309 
310 
312  factor *= _factor;
313  return *this;
314  }
315 
316 
317  Tensor& Tensor::operator/=(const value_t _divisor) {
318  factor /= _divisor;
319  return *this;
320  }
321 
322 
323  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Access - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
324  value_t& Tensor::operator[](const size_t _position) {
325  REQUIRE(_position < size, "Position " << _position << " does not exist in Tensor of dimensions " << dimensions);
326 
328 
329  if(is_dense()) {
330  return denseData.get()[_position];
331  }
332  value_t& result = (*sparseData)[_position];
334  if (is_dense()) {
335  return denseData.get()[_position];
336  }
337  return result;
338  }
339 
340 
341  value_t Tensor::operator[](const size_t _position) const {
342  REQUIRE(_position < size, "Position " << _position << " does not exist in Tensor of dimensions " << dimensions);
343 
344  if(is_dense()) {
345  return factor*denseData.get()[_position];
346  }
347  const std::map<size_t, value_t>::const_iterator entry = sparseData->find(_position);
348  if(entry == sparseData->end()) {
349  return 0.0;
350  }
351  return factor*entry->second;
352  }
353 
354 
355  value_t& Tensor::operator[](const MultiIndex& _positions) {
356  return operator[](multiIndex_to_position(_positions, dimensions));
357  }
358 
359 
360  value_t Tensor::operator[](const MultiIndex& _positions) const {
361  return operator[](multiIndex_to_position(_positions, dimensions));
362  }
363 
364 
365  value_t& Tensor::at(const size_t _position) {
366  REQUIRE(_position < size, "Position " << _position << " does not exist in Tensor of dimensions " << dimensions);
367  REQUIRE(!has_factor(), "at() must not be called if there is a factor.");
368  REQUIRE((is_dense() && denseData.unique()) || (is_sparse() && sparseData.unique()) , "Data must be unique to call at().");
369 
370  if(is_dense()) {
371  return denseData.get()[_position];
372  }
373  value_t& result = (*sparseData)[_position];
375  if (is_dense()) {
376  return denseData.get()[_position];
377  }
378  return result;
379 
380 
381  }
382 
383 
384  value_t Tensor::cat(const size_t _position) const {
385  REQUIRE(_position < size, "Position " << _position << " does not exist in Tensor of dimensions " << dimensions);
386  REQUIRE(!has_factor(), "at() must not be called if there is a factor.");
387 
388  if(is_dense()) {
389  return denseData.get()[_position];
390  }
391  const std::map<size_t, value_t>::const_iterator entry = sparseData->find(_position);
392  if(entry == sparseData->end()) {
393  return 0.0;
394  }
395  return entry->second;
396 
397 
398  }
399 
400 
404  return denseData.get();
405  }
406 
407 
409  REQUIRE(is_dense(), "Unsanitized dense data requested, but representation is not dense!");
410  return denseData.get();
411  }
412 
413 
415  REQUIRE(is_dense(), "Unsanitized dense data requested, but representation is not dense!");
416  return denseData.get();
417  }
418 
419 
421  factor = 1.0;
422  if(!denseData.unique()) {
423  sparseData.reset();
424  denseData.reset(new value_t[size], internal::array_deleter_vt);
426  }
427  return denseData.get();
428  }
429 
430 
431  const std::shared_ptr<value_t>& Tensor::get_internal_dense_data() {
432  REQUIRE(is_dense(), "Internal dense data requested, but representation is not dense!");
433  return denseData;
434  }
435 
436 
437  std::map<size_t, value_t>& Tensor::get_sparse_data() {
438  CHECK(is_sparse(), warning, "Request for sparse data although the Tensor is not sparse.");
441  return *sparseData.get();
442  }
443 
444 
445  std::map<size_t, value_t>& Tensor::get_unsanitized_sparse_data() {
446  REQUIRE(is_sparse(), "Unsanitized sparse data requested, but representation is not sparse!");
447  return *sparseData.get();
448  }
449 
450 
451  const std::map<size_t, value_t>& Tensor::get_unsanitized_sparse_data() const {
452  REQUIRE(is_sparse(), "Unsanitized sparse data requested, but representation is not sparse!");
453  return *sparseData.get();
454  }
455 
456 
457  std::map<size_t, value_t>& Tensor::override_sparse_data() {
458  factor = 1.0;
459  if(sparseData.unique()) {
460  INTERNAL_CHECK(is_sparse(), "Internal Error");
461  sparseData->clear();
462  } else {
463  denseData.reset();
464  sparseData.reset(new std::map<size_t, value_t>());
466  }
467 
468  return *sparseData.get();
469  }
470 
471 
472  const std::shared_ptr<std::map<size_t, value_t>>& Tensor::get_internal_sparse_data() {
473  REQUIRE(is_sparse(), "Internal sparse data requested, but representation is not sparse!");
474  return sparseData;
475  }
476 
477 
478  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Indexing - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
479  internal::IndexedTensor<Tensor> Tensor::operator()(const std::vector<Index>& _indices) {
480  return internal::IndexedTensor<Tensor>(this, _indices, false);
481  }
482 
483 
484  internal::IndexedTensor<Tensor> Tensor::operator()( std::vector<Index>&& _indices) {
485  return internal::IndexedTensor<Tensor>(this, std::move(_indices), false);
486  }
487 
488 
489  internal::IndexedTensorReadOnly<Tensor> Tensor::operator()(const std::vector<Index>& _indices) const {
490  return internal::IndexedTensorReadOnly<Tensor>(this, _indices);
491  }
492 
493 
494  internal::IndexedTensorReadOnly<Tensor> Tensor::operator()( std::vector<Index>&& _indices) const {
495  return internal::IndexedTensorReadOnly<Tensor>(this, std::move(_indices));
496  }
497 
498 
499  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Modififiers - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
500  void Tensor::reset(DimensionTuple _newDim, const Representation _representation, const Initialisation _init) {
501  const size_t oldDataSize = size;
502  dimensions = std::move(_newDim);
503  size = misc::product(dimensions);
504  factor = 1.0;
505 
506  if(_representation == Representation::Dense) {
508  if(oldDataSize != size || !denseData.unique()) {
509  denseData.reset(new value_t[size], internal::array_deleter_vt);
510  }
511  } else {
512  sparseData.reset();
513  denseData.reset(new value_t[size], internal::array_deleter_vt);
514  representation = _representation;
515  }
516 
517  if(_init == Initialisation::Zero) {
518  memset(denseData.get(), 0, size*sizeof(value_t));
519  }
520  } else {
522  if(sparseData.unique()) {
523  sparseData->clear();
524  } else {
525  sparseData.reset(new std::map<size_t, value_t>());
526  }
527  } else {
528  denseData.reset();
529  sparseData.reset(new std::map<size_t, value_t>());
530  representation = _representation;
531  }
532  }
533  }
534 
535 
536  void Tensor::reset(DimensionTuple _newDim, const Initialisation _init) {
537  const size_t oldDataSize = size;
538  dimensions = std::move(_newDim);
539  size = misc::product(dimensions);
540  factor = 1.0;
541 
543  if(oldDataSize != size || !denseData.unique()) {
544  denseData.reset(new value_t[size], internal::array_deleter_vt);
545  }
546 
547  if(_init == Initialisation::Zero) {
548  memset(denseData.get(), 0, size*sizeof(value_t));
549  }
550  } else {
551  if(sparseData.unique()) {
552  sparseData->clear();
553  } else {
554  sparseData.reset(new std::map<size_t, value_t>());
555  }
556  }
557  }
558 
559  void Tensor::reset() {
560  dimensions.clear();
561  factor = 1.0;
562 
564  if(size != 1 || !denseData.unique()) {
565  denseData.reset(new value_t[1], internal::array_deleter_vt);
566  }
567  *denseData.get() = 0.0;
568  } else {
569  if(sparseData.unique()) {
570  sparseData->clear();
571  } else {
572  sparseData.reset(new std::map<size_t, value_t>());
573  }
574  }
575  size = 1;
576  }
577 
578 
579  void Tensor::reset(DimensionTuple _newDim, const std::shared_ptr<value_t>& _newData) {
580  dimensions = std::move(_newDim);
581  size = misc::product(dimensions);
582  factor = 1.0;
583 
585  sparseData.reset();
587  }
588 
589  denseData = _newData;
590  }
591 
592 
593  void Tensor::reset(DimensionTuple _newDim, std::unique_ptr<value_t[]>&& _newData) {
594  dimensions = std::move(_newDim);
595  size = misc::product(dimensions);
596  factor = 1.0;
597 
599  sparseData.reset();
601  }
602 
603  denseData.reset(_newData.release());
604  }
605 
606  void Tensor::reset(DimensionTuple _newDim, std::map<size_t, value_t>&& _newData) {
607  dimensions = std::move(_newDim);
608  size = misc::product(dimensions);
609  factor = 1.0;
610 
612  denseData.reset();
614  }
615 
616  std::shared_ptr<std::map<size_t, value_t>> newD(new std::map<size_t, value_t>(std::move(_newData)));
617  sparseData = std::move(newD);
618  }
619 
621  REQUIRE(misc::product(_newDimensions) == size, "New dimensions must not change the size of the tensor in reinterpretation: " << misc::product(_newDimensions) << " != " << size);
622  dimensions = std::move(_newDimensions); // NOTE pass-by-value
623  }
624 
625 
626  void Tensor::resize_mode(const size_t _mode, const size_t _newDim, size_t _cutPos) {
627  REQUIRE(_mode < degree(), "Can't resize mode " << _mode << " as the tensor is only order " << degree());
628 
629  if (dimensions[_mode] == _newDim) { return; } // Trivial case: Nothing to do
630 
631  const size_t oldDim = dimensions[_mode];
632  _cutPos = std::min(_cutPos, oldDim);
633 
634  REQUIRE(_newDim > 0, "Dimension must be larger than 0! Is " << _newDim);
635  REQUIRE(_newDim > oldDim || _cutPos >= oldDim -_newDim, "Cannot remove " << oldDim -_newDim << " slates starting (exclusivly) at position " << _cutPos);
636 
637  const size_t dimStepSize = misc::product(dimensions, _mode+1, degree());
638  const size_t oldStepSize = oldDim*dimStepSize;
639  const size_t newStepSize = _newDim*dimStepSize;
640  const size_t blockCount = size / oldStepSize; // == misc::product(dimensions, 0, _n);
641  const size_t newsize = blockCount*newStepSize;
642 
643  if(is_dense()) {
644  std::unique_ptr<value_t[]> tmpData(new value_t[newsize]);
645 
646  if (_newDim > oldDim) { // Add new slates
647  const size_t insertBlockSize = (_newDim-oldDim)*dimStepSize;
648  if(_cutPos == 0) {
649  for ( size_t i = 0; i < blockCount; ++i ) {
650  value_t* const currData = tmpData.get()+i*newStepSize;
651  misc::set_zero(currData, insertBlockSize);
652  misc::copy(currData+insertBlockSize, denseData.get()+i*oldStepSize, oldStepSize);
653  }
654  } else if(_cutPos == oldDim) {
655  for ( size_t i = 0; i < blockCount; ++i ) {
656  value_t* const currData = tmpData.get()+i*newStepSize;
657  misc::copy(currData, denseData.get()+i*oldStepSize, oldStepSize);
658  misc::set_zero(currData+oldStepSize, insertBlockSize);
659  }
660  } else {
661  const size_t preBlockSize = _cutPos*dimStepSize;
662  const size_t postBlockSize = (oldDim-_cutPos)*dimStepSize;
663  for (size_t i = 0; i < blockCount; ++i) {
664  value_t* const currData = tmpData.get()+i*newStepSize;
665  misc::copy(currData, denseData.get()+i*oldStepSize, preBlockSize);
666  misc::set_zero(currData+preBlockSize, insertBlockSize);
667  misc::copy(currData+preBlockSize+insertBlockSize, denseData.get()+i*oldStepSize+preBlockSize, postBlockSize);
668  }
669  }
670  } else { // Remove slates
671  if (_cutPos < oldDim) {
672  const size_t removedSlates = (oldDim-_newDim);
673  const size_t removedBlockSize = removedSlates*dimStepSize;
674  const size_t preBlockSize = (_cutPos-removedSlates)*dimStepSize;
675  const size_t postBlockSize = (oldDim-_cutPos)*dimStepSize;
676 
677  INTERNAL_CHECK(removedBlockSize+preBlockSize+postBlockSize == oldStepSize && preBlockSize+postBlockSize == newStepSize, "IE");
678 
679  for (size_t i = 0; i < blockCount; ++i) {
680  value_t* const currData = tmpData.get()+i*newStepSize;
681  misc::copy(currData, denseData.get()+i*oldStepSize, preBlockSize);
682  misc::copy(currData+preBlockSize, denseData.get()+i*oldStepSize+preBlockSize+removedBlockSize, postBlockSize);
683  }
684  } else {
685  for (size_t i = 0; i < blockCount; ++i) {
686  misc::copy(tmpData.get()+i*newStepSize, denseData.get()+i*oldStepSize, newStepSize);
687  }
688  }
689  }
690  denseData.reset(tmpData.release(), internal::array_deleter_vt);
691 
692  } else {
693  std::unique_ptr<std::map<size_t, value_t>> tmpData(new std::map<size_t, value_t>());
694 
695  if (_newDim > oldDim) { // Add new slates
696  const size_t slatesAdded = _newDim-oldDim;
697  for(const auto& entry : *sparseData.get()) {
698  // Decode the position as i*oldStepSize + j*DimStepSize + k
699  const size_t k = entry.first%dimStepSize;
700  const size_t j = (entry.first%oldStepSize)/dimStepSize;
701  const size_t i = entry.first/oldStepSize;
702 
703  if(j < _cutPos) { // Entry remains at current position j
704  tmpData->emplace(i*newStepSize+j*dimStepSize+k, entry.second);
705  } else { // Entry moves to position j+slatesAdded
706  tmpData->emplace(i*newStepSize+(j+slatesAdded)*dimStepSize+k, entry.second);
707  }
708  }
709  } else { // Remove slates
710  const size_t slatesRemoved = oldDim-_newDim;
711  for(const auto& entry : *sparseData.get()) {
712  // Decode the position as i*oldStepSize + j*DimStepSize + k
713  const size_t k = entry.first%dimStepSize;
714  const size_t j = (entry.first%oldStepSize)/dimStepSize;
715  const size_t i = entry.first/oldStepSize;
716 
717  if(j < _cutPos-slatesRemoved) { // Entry remains at current position j
718  tmpData->emplace(i*newStepSize+j*dimStepSize+k, entry.second);
719  } else if(j >= _cutPos) { // Entry advances in position j
720  tmpData->emplace(i*newStepSize+(j-slatesRemoved)*dimStepSize+k, entry.second);
721  }
722  }
723  }
724  sparseData.reset(tmpData.release());
725  }
726 
727  dimensions[_mode] = _newDim;
728  size = newsize;
729  }
730 
731 
732  void Tensor::fix_mode(const size_t _mode, const size_t _slatePosition) {
733  REQUIRE(_slatePosition < dimensions[_mode], "The given slatePosition must be smaller than the corresponding dimension. Here " << _slatePosition << " >= " << dimensions[_mode] << ", dim = " << dimensions << "=" << size << " mode " << _mode);
734 
735  const size_t stepCount = misc::product(dimensions, 0, _mode);
736  const size_t blockSize = misc::product(dimensions, _mode+1, degree());
737  const size_t stepSize = dimensions[_mode]*blockSize;
738  const size_t slateOffset = _slatePosition*blockSize;
739 
740  if(is_dense()) {
741  std::unique_ptr<value_t[]> tmpData(new value_t[stepCount*blockSize]);
742 
743  // Copy data
744  for(size_t i = 0; i < stepCount; ++i) {
745  misc::copy(tmpData.get()+i*blockSize, denseData.get()+i*stepSize+slateOffset, blockSize);
746  }
747 
748  denseData.reset(tmpData.release(), &internal::array_deleter_vt);
749  } else {
750  std::unique_ptr<std::map<size_t, value_t>> tmpData(new std::map<size_t, value_t>());
751 
752  for(const auto& entry : *sparseData.get()) {
753  // Decode the position as i*stepSize + j*blockSize + k
754  const size_t k = entry.first%blockSize;
755  const size_t j = (entry.first%stepSize)/blockSize;
756  const size_t i = entry.first/stepSize;
757 
758  if(j == _slatePosition) {
759  tmpData->emplace(i*blockSize+k, entry.second);
760  }
761  }
762 
763  sparseData.reset(tmpData.release());
764  }
765 
766  // Adjust dimensions
767  dimensions.erase(dimensions.begin()+long(_mode));
768  size = stepCount*blockSize;
769  }
770 
771 
772  void Tensor::remove_slate(const size_t _mode, const size_t _pos) {
773  REQUIRE(_mode < degree(), "");
774  REQUIRE(_pos < dimensions[_mode], _pos << " " << dimensions[_mode]);
775  REQUIRE(dimensions[_mode] > 1, "");
776 
777  resize_mode(_mode, dimensions[_mode]-1, _pos+1);
778  }
779 
780 
781  void Tensor::perform_trace(size_t _firstMode, size_t _secondMode) {
782  REQUIRE(_firstMode != _secondMode, "Given indices must not coincide");
783  REQUIRE(dimensions[_firstMode] == dimensions[_secondMode], "Dimensions of trace indices must coincide.");
784 
785  if(_firstMode > _secondMode) { std::swap(_firstMode, _secondMode); }
786 
787 
788 
789  const size_t front = misc::product(dimensions, 0, _firstMode);
790  const size_t mid = misc::product(dimensions, _firstMode+1, _secondMode);
791  const size_t back = misc::product(dimensions, _secondMode+1, degree());
792  const size_t traceDim = dimensions[_firstMode];
793  const size_t frontStepSize = traceDim*mid*traceDim*back;
794  const size_t traceStepSize = mid*traceDim*back+back;
795  const size_t midStepSize = traceDim*back;
796 
797  size = front*mid*back;
798 
799  if(is_dense()) {
800  std::unique_ptr<value_t[]> newData(new value_t[size]);
801  misc::set_zero(newData.get(), size);
802 
803  for(size_t f = 0; f < front; ++f) {
804  for(size_t t = 0; t < traceDim; ++t) {
805  for(size_t m = 0; m < mid; ++m) {
806  misc::add_scaled(newData.get()+(f*mid+m)*back, factor, denseData.get()+f*frontStepSize+t*traceStepSize+m*midStepSize, back);
807  }
808  }
809  }
810 
811  denseData.reset(newData.release(), internal::array_deleter_vt);
812  } else {
813  std::unique_ptr<std::map<size_t, value_t>> newData( new std::map<size_t, value_t>());
814 
815  for(const auto& entry : *sparseData) {
816  size_t pos = entry.first;
817  const size_t backIdx = pos%back;
818  pos /= back;
819  const size_t traceBackIdx = pos%traceDim;
820  pos /= traceDim;
821  const size_t midIdx = pos%mid;
822  pos /= mid;
823  const size_t traceFrontIdx = pos%traceDim;
824  pos /= traceDim;
825  const size_t frontIdx = pos;
826 
827  if(traceFrontIdx == traceBackIdx) {
828  (*newData)[(frontIdx*mid + midIdx)*back + backIdx] += factor*entry.second;
829  }
830  }
831 
832  sparseData.reset(newData.release());
833  }
834 
835  dimensions.erase(dimensions.begin()+_secondMode);
836  dimensions.erase(dimensions.begin()+_firstMode);
837  factor = 1.0;
838  }
839 
840 
841  void Tensor::modify_diagonal_entries(const std::function<void(value_t&)>& _f) {
843 
844  if(degree() == 0) {
845  _f(at(0));
846  } else {
847  size_t stepSize = 1;
848  for(size_t i = 1; i < degree(); ++i) {
849  stepSize *= dimensions[i];
850  stepSize += 1;
851  }
852 
853  const size_t numDiags = misc::min(dimensions);
854 
855  for(size_t i = 0; i < numDiags; ++i){
856  _f(at(i*stepSize));
857  }
858  }
859  }
860 
861 
862  void Tensor::modify_diagonal_entries(const std::function<void(value_t&, const size_t)>& _f) {
864 
865  if(degree() == 0) {
866  _f(at(0), 0);
867  } else {
868  size_t stepSize = 1;
869  for(size_t i = 1; i < degree(); ++i) {
870  stepSize *= dimensions[i];
871  stepSize += 1;
872  }
873 
874  const size_t numDiags = misc::min(dimensions);
875 
876  for(size_t i = 0; i < numDiags; ++i){
877  _f(at(i*stepSize), i);
878  }
879  }
880  }
881 
882 
883  void Tensor::modify_entries(const std::function<void(value_t&)>& _f) {
885  if(is_dense()) {
886  for(size_t i = 0; i < size; ++i) { _f(at(i)); }
887  } else {
888  for(size_t i = 0; i < size; ++i) {
889  value_t val = cat(i);
890  const value_t oldVal = val;
891  _f(val);
892  if(misc::hard_not_equal(val, 0.0)) {
893  at(i) = val;
894  } else if( misc::hard_not_equal(oldVal, 0.0)) {
895  IF_CHECK( size_t succ =) sparseData->erase(i);
896  INTERNAL_CHECK(succ == 1, "Internal Error");
897  }
898  }
899  }
900  }
901 
902 
903  void Tensor::modify_entries(const std::function<void(value_t&, const size_t)>& _f) {
905  if(is_dense()) {
906  for(size_t i = 0; i < size; ++i) { _f(at(i), i); }
907  } else {
908  for(size_t i = 0; i < size; ++i) {
909  value_t val = cat(i);
910  const value_t oldVal = val;
911  _f(val, i);
912  if(misc::hard_not_equal(val, 0.0)) {
913  at(i) = val;
914  } else if( misc::hard_not_equal(oldVal, 0.0)) {
915  IF_CHECK( size_t succ =) sparseData->erase(i);
916  INTERNAL_CHECK(succ == 1, "Internal Error");
917  }
918  }
919  }
920  }
921 
922 
923  void Tensor::modify_entries(const std::function<void(value_t&, const MultiIndex&)>& _f) {
925 
926  MultiIndex multIdx(degree(), 0);
927  size_t idx = 0;
928  while (true) {
929  if(is_dense()) {
930  _f(at(idx), multIdx);
931  } else {
932  value_t val = cat(idx);
933  const value_t oldVal = val;
934  _f(val, multIdx);
935  if(misc::hard_not_equal(val, 0.0)) {
936  at(idx) = val;
937  } else if( misc::hard_not_equal(oldVal, 0.0)) {
938  IF_CHECK( size_t succ =) sparseData->erase(idx);
939  INTERNAL_CHECK(succ == 1, "Internal Error");
940  }
941  }
942 
943  // Increase indices
944  idx++;
945  size_t changingIndex = degree()-1;
946  multIdx[changingIndex]++;
947  while(multIdx[changingIndex] == dimensions[changingIndex]) {
948  multIdx[changingIndex] = 0;
949  changingIndex--;
950  // Return on overflow
951  if(changingIndex >= degree()) { return; }
952  multIdx[changingIndex]++;
953  }
954  }
955  }
956 
957 
958  inline std::vector<size_t> get_step_sizes(const Tensor::DimensionTuple& _dimensions) {
959  std::vector<size_t> stepSizes(_dimensions.size());
960  if(!_dimensions.empty()) {
961  stepSizes.back() = 1;
962  for(size_t i = stepSizes.size(); i > 1; --i) {
963  stepSizes[i-2] = stepSizes[i-1]*_dimensions[i-1];
964  }
965  }
966  return stepSizes;
967  }
968 
969  void Tensor::offset_add(const Tensor& _other, const std::vector<size_t>& _offsets) {
970  IF_CHECK(
971  REQUIRE(degree() == _other.degree(), "Degrees of the this and the given tensor must coincide. Here " << degree() << " vs " << _other.degree());
972  REQUIRE(degree() == _offsets.size(), "Degrees of the this tensor and number of given offsets must coincide. Here " << degree() << " vs " << _offsets.size());
973  for(size_t d = 0; d < degree(); ++d) {
974  REQUIRE(dimensions[d] >= _offsets[d]+_other.dimensions[d], "Invalid offset/tensor dimension for dimension " << d << " because this dimension is " << dimensions[d] << " but other tensor dimension + offset is " << _offsets[d]+_other.dimensions[d]);
975  }
976  )
977 
978  if(_other.is_dense()) {
980 
981  const std::vector<size_t> stepSizes = get_step_sizes(dimensions);
982 
983  // Calculate the actual offset
984  size_t offset = 0;
985  for(size_t d = 0; d < degree(); ++d) {
986  offset += _offsets[d]*stepSizes[d];
987  }
988 
989  const size_t blockSize = _other.dimensions.back();
990  const value_t* inPosition = _other.get_unsanitized_dense_data();
991  value_t* outPosition = get_dense_data() + offset;
992 
993  misc::add_scaled(outPosition, _other.factor, inPosition, blockSize);
994 
995  for(size_t i = 1; i < _other.size/blockSize; ++i) {
996  size_t index = degree()-2;
997  size_t multStep = _other.dimensions[index];
998  inPosition += blockSize;
999  outPosition += stepSizes[index];
1000  while(i%multStep == 0) {
1001  outPosition -= dimensions[index]*stepSizes[index]; // "reset" current index to 0
1002  --index; // Advance to next index
1003  outPosition += stepSizes[index]; // increase next index
1004  multStep *= dimensions[index]; // next stepSize
1005  }
1006 
1007  misc::add_scaled(outPosition, _other.factor, inPosition, blockSize);
1008  }
1009  } else {
1010  const size_t offset = multiIndex_to_position(_offsets, dimensions);
1011  if(is_dense()) {
1012  value_t* const dataPtr = get_dense_data();
1013  for(const auto& entry : _other.get_unsanitized_sparse_data()) {
1014  const size_t newPos = multiIndex_to_position(position_to_multiIndex(entry.first, _other.dimensions), dimensions) + offset;
1015  dataPtr[newPos] += _other.factor*entry.second;
1016  }
1017  } else {
1018  std::map<size_t, value_t>& data = get_sparse_data();
1019  for(const auto& entry : _other.get_unsanitized_sparse_data()) {
1020  const size_t newPos = multiIndex_to_position(position_to_multiIndex(entry.first, _other.dimensions), dimensions) + offset;
1021  data[newPos] += _other.factor*entry.second;
1022  }
1023  }
1024  }
1025  }
1026 
1027 
1029  if(is_sparse()) {
1030  denseData.reset(new value_t[size], internal::array_deleter_vt);
1031  misc::set_zero(denseData.get(), size);
1032  for(const auto& entry : *sparseData) {
1033  denseData.get()[entry.first] = factor*entry.second;
1034  }
1035 
1036  factor = 1.0;
1037  sparseData.reset();
1039  }
1040  }
1041 
1042 
1044  if (is_sparse() && sparsity() * sparsityFactor >= size) {
1046  }
1047  }
1048 
1049 
1051  if(is_dense()) {
1052  sparseData.reset(new std::map<size_t, value_t>());
1053  for(size_t i = 0; i < size; ++i) {
1054  if(std::abs(factor*denseData.get()[i]) >= _eps) {
1055  sparseData->emplace_hint(sparseData->end(), i, factor*denseData.get()[i]);
1056  }
1057  }
1058 
1059  factor = 1.0;
1060  denseData.reset();
1062  }
1063  }
1064 
1065 
1066  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Miscellaneous - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1067  std::string Tensor::to_string() const {
1068  if (degree() == 0) { return xerus::misc::to_string(operator[](0)); }
1069 
1070  std::string result;
1071  for (size_t i = 0; i < size; ++i) {
1072  result += xerus::misc::to_string(operator[](i)) + " ";
1073  if ((i+1) % (size / dimensions[0]) == 0) {
1074  result += '\n';
1075  } else if (degree() > 1 && (i+1) % (size / dimensions[0] / dimensions[1]) == 0) {
1076  result += '\t';
1077  } else if (degree() > 2 && (i+1) % (size / dimensions[0] / dimensions[1] / dimensions[2]) == 0) {
1078  result += "/ ";
1079  }
1080  }
1081  return result;
1082  }
1083 
1084 
1085  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Internal Helper functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1086  template<int sign>
1087  void Tensor::plus_minus_equal(Tensor& _me, const Tensor& _other) {
1088  REQUIRE(_me.dimensions == _other.dimensions, "In Tensor sum the dimensions must coincide.");
1089 
1090  if(_me.is_dense()) {
1092  if(_other.is_dense()) {
1093  misc::add_scaled(_me.denseData.get(), sign*_other.factor, _other.denseData.get(), _me.size);
1094  } else {
1095  add_sparse_to_full(_me.denseData, sign*_other.factor, _other.sparseData);
1096  }
1097  } else {
1098  if(_other.is_dense()) {
1099  _me.denseData.reset(new value_t[_me.size], internal::array_deleter_vt);
1100  misc::copy_scaled(_me.denseData.get(), sign*_other.factor, _other.denseData.get(), _me.size);
1101  add_sparse_to_full(_me.denseData, _me.factor, _me.sparseData);
1102  _me.factor = 1.0;
1104  _me.sparseData.reset();
1105  } else {
1107  add_sparse_to_sparse(_me.sparseData, sign*_other.factor, _other.sparseData);
1109  }
1110  }
1111  }
1112 
1113 
1114  void Tensor::add_sparse_to_full(const std::shared_ptr<value_t>& _denseData, const value_t _factor, const std::shared_ptr<const std::map<size_t, value_t>>& _sparseData) {
1115  for(const auto& entry : *_sparseData) {
1116  _denseData.get()[entry.first] += _factor*entry.second;
1117  }
1118  }
1119 
1120 
1121  void Tensor::add_sparse_to_sparse(const std::shared_ptr<std::map<size_t, value_t>>& _sum, const value_t _factor, const std::shared_ptr<const std::map<size_t, value_t>>& _summand) {
1122  for(const auto& entry : *_summand) {
1123  std::pair<std::map<size_t, value_t>::iterator, bool> result = _sum->emplace(entry.first, _factor*entry.second);
1124  if(!result.second) {
1125  result.first->second += _factor*entry.second;
1126  }
1127  }
1128  }
1129 
1130 
1131  size_t Tensor::multiIndex_to_position(const MultiIndex& _multiIndex, const DimensionTuple& _dimensions) {
1132  REQUIRE(_multiIndex.size() == _dimensions.size(), "MultiIndex has wrong degree. Given " << _multiIndex.size() << ", expected " << _dimensions.size());
1133 
1134  size_t finalIndex = 0;
1135  for(size_t i = 0; i < _multiIndex.size(); ++i) {
1136  REQUIRE(_multiIndex[i] < _dimensions[i], "Index "<< i <<" out of bounds " << _multiIndex[i] << " >=! " << _dimensions[i]);
1137  finalIndex *= _dimensions[i];
1138  finalIndex += _multiIndex[i];
1139  }
1140 
1141  return finalIndex;
1142  }
1143 
1144  Tensor::MultiIndex Tensor::position_to_multiIndex(size_t _position, const DimensionTuple& _dimensions) {
1145  REQUIRE(_position < misc::product(_dimensions), "Invalid position " << _position << " given. Max size is : " << misc::product(_dimensions));
1146  MultiIndex index(_dimensions.size());
1147 
1148  for(size_t i = 0; i < _dimensions.size(); ++i) {
1149  const size_t k = _dimensions.size() - 1 - i;
1150  index[k] = _position%_dimensions[k];
1151  _position /= _dimensions[k];
1152  }
1153 
1154  return index;
1155  }
1156 
1157 
1159  if(is_dense()) {
1160  if(!denseData.unique()) {
1161  value_t* const oldDataPtr = denseData.get();
1162  denseData.reset(new value_t[size], internal::array_deleter_vt);
1163  misc::copy(denseData.get(), oldDataPtr, size);
1164  }
1165  } else {
1166  if(!sparseData.unique()) {
1167  sparseData.reset(new std::map<size_t, value_t>(*sparseData));
1168  }
1169  }
1170  }
1171 
1172 
1174  if(is_dense()) {
1175  if(!denseData.unique()) {
1176  denseData.reset(new value_t[size], internal::array_deleter_vt);
1177  }
1178  } else {
1179  if(!sparseData.unique()) {
1180  sparseData.reset(new std::map<size_t, value_t>());
1181  }
1182  }
1183  }
1184 
1185 
1187  if(has_factor()) {
1188  if(is_dense()) {
1189  if(denseData.unique()) {
1190  misc::scale(denseData.get(), factor, size);
1191  } else {
1192  value_t* const oldDataPtr = denseData.get();
1193  denseData.reset(new value_t[size], internal::array_deleter_vt);
1194  misc::copy_scaled(denseData.get(), factor, oldDataPtr, size);
1195  }
1196  } else {
1197  if(!sparseData.unique()) {
1198  sparseData.reset(new std::map<size_t, value_t>(*sparseData));
1199  }
1200 
1201  for(std::pair<const size_t, value_t>& entry : *sparseData) {
1202  entry.second *= factor;
1203  }
1204  }
1205 
1206  factor = 1.0;
1207  }
1208  }
1209 
1211  if(has_factor()) {
1212  apply_factor();
1213  } else {
1214  ensure_own_data();
1215  }
1216  }
1217 
1218 
1219 
1220  /*- - - - - - - - - - - - - - - - - - - - - - - - - - Basic arithmetics - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1221  Tensor operator+(Tensor _lhs, const Tensor& _rhs) {
1222  _lhs += _rhs;
1223  return _lhs;
1224  }
1225 
1226 
1227  Tensor operator-(Tensor _lhs, const Tensor& _rhs) {
1228  _lhs -= _rhs;
1229  return _lhs;
1230  }
1231 
1232 
1233  Tensor operator*(const value_t _factor, Tensor _tensor) {
1234  _tensor *= _factor;
1235  return _tensor;
1236  }
1237 
1238 
1239  Tensor operator*(Tensor _tensor, const value_t _factor) {
1240  _tensor *= _factor;
1241  return _tensor;
1242  }
1243 
1244 
1245  Tensor operator/(Tensor _tensor, const value_t _divisor) {
1246  _tensor /= _divisor;
1247  return _tensor;
1248  }
1249 
1250 
1251  /*- - - - - - - - - - - - - - - - - - - - - - - - - - External functions - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
1252  void contract(Tensor& _result, const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes) {
1253  REQUIRE(_numModes <= _lhs.degree() && _numModes <= _rhs.degree(), "Cannot contract more indices than both tensors have. we have: "
1254  << _lhs.degree() << " and " << _rhs.degree() << " but want to contract: " << _numModes);
1255 
1256  const size_t lhsRemainOrder = _lhs.degree() - _numModes;
1257  const size_t lhsRemainStart = _lhsTrans ? _numModes : 0;
1258  const size_t lhsContractStart = _lhsTrans ? 0 : lhsRemainOrder;
1259  const size_t lhsRemainEnd = lhsRemainStart + lhsRemainOrder;
1260 
1261  const size_t rhsRemainOrder = _rhs.degree() - _numModes;
1262  const size_t rhsRemainStart = _rhsTrans ? 0 : _numModes;
1263  IF_CHECK(const size_t rhsContractStart = _rhsTrans ? rhsRemainOrder : 0;)
1264  const size_t rhsRemainEnd = rhsRemainStart + rhsRemainOrder;
1265 
1266  REQUIRE(std::equal(_lhs.dimensions.begin() + lhsContractStart, _lhs.dimensions.begin() + lhsContractStart + _numModes, _rhs.dimensions.begin() + rhsContractStart),
1267  "Dimensions of the be contracted indices do not coincide. " <<_lhs.dimensions << " ("<<_lhsTrans<<") and " << _rhs.dimensions << " ("<<_rhsTrans<<") with " << _numModes);
1268 
1269  const size_t leftDim = misc::product(_lhs.dimensions, lhsRemainStart, lhsRemainEnd);
1270  const size_t midDim = misc::product(_lhs.dimensions, lhsContractStart, lhsContractStart+_numModes);
1271  const size_t rightDim = misc::product(_rhs.dimensions, rhsRemainStart, rhsRemainEnd);
1272 
1273 
1274  const size_t finalSize = leftDim*rightDim;
1275  const size_t sparsityExpectation = size_t(double(finalSize)*(1.0 - misc::pow(1.0 - double(_lhs.sparsity()*_rhs.sparsity())/(double(_lhs.size)*double(_rhs.size)), midDim)));
1276  REQUIRE(sparsityExpectation <= std::min(leftDim*_rhs.sparsity(), rightDim*_lhs.sparsity()), "IE");
1277  // TODO allow Sparse*sparse --> Full
1278  const bool sparseResult = (_lhs.is_sparse() && _rhs.is_sparse()) || (finalSize > 64 && Tensor::sparsityFactor*sparsityExpectation < finalSize*2) ;
1279  const Tensor::Representation resultRepresentation = sparseResult ? Tensor::Representation::Sparse : Tensor::Representation::Dense;
1280 
1281 
1282  // Check whether _result has to be reset and prevent deletion of _lhs or _rhs due to being the same object as _result.
1283  std::unique_ptr<Tensor> tmpResult;
1284  Tensor* usedResult;
1285  if( &_result == &_lhs || &_result == &_rhs
1286  || _result.representation != resultRepresentation
1287  || _result.degree() != lhsRemainOrder + rhsRemainOrder
1288  || _result.size != leftDim*rightDim
1289  || !std::equal(_lhs.dimensions.begin() + lhsRemainStart, _lhs.dimensions.begin() + lhsRemainEnd, _result.dimensions.begin())
1290  || !std::equal(_rhs.dimensions.begin() + rhsRemainStart, _rhs.dimensions.begin() + rhsRemainEnd, _result.dimensions.begin() + (lhsRemainEnd-lhsRemainStart))
1291  ) {
1292  Tensor::DimensionTuple resultDim;
1293  resultDim.reserve(lhsRemainOrder + rhsRemainOrder);
1294  resultDim.insert(resultDim.end(), _lhs.dimensions.begin() + lhsRemainStart, _lhs.dimensions.begin() + lhsRemainEnd);
1295  resultDim.insert(resultDim.end(), _rhs.dimensions.begin() + rhsRemainStart, _rhs.dimensions.begin() + rhsRemainEnd);
1296 
1297  if(&_result == &_lhs || &_result == &_rhs) {
1298  tmpResult.reset(new Tensor(std::move(resultDim), resultRepresentation, Tensor::Initialisation::None));
1299  usedResult = tmpResult.get();
1300  } else {
1301  _result.reset(std::move(resultDim), resultRepresentation, Tensor::Initialisation::None);
1302  usedResult = &_result;
1303  }
1304  } else {
1305  usedResult = &_result;
1306  }
1307 
1308 
1309  if(!_lhs.is_sparse() && !_rhs.is_sparse()) { // Full * Full => Full
1310  blasWrapper::matrix_matrix_product(usedResult->override_dense_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1311  _lhs.get_unsanitized_dense_data(), _lhsTrans, midDim,
1312  _rhs.get_unsanitized_dense_data(), _rhsTrans);
1313 
1314  } else if(_lhs.is_sparse() && _rhs.is_sparse() ) { // Sparse * Sparse => Sparse
1315  internal::CholmodSparse::matrix_matrix_product(usedResult->override_sparse_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1316  _lhs.get_unsanitized_sparse_data(), _lhsTrans, midDim,
1317  _rhs.get_unsanitized_sparse_data(), _rhsTrans);
1318  } else {
1319  if(sparseResult) {
1320  if(_lhs.is_sparse() && !_rhs.is_sparse() && usedResult->is_sparse()) { // Sparse * Full => Sparse
1321  matrix_matrix_product(usedResult->override_sparse_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1322  _lhs.get_unsanitized_sparse_data(), _lhsTrans, midDim,
1323  _rhs.get_unsanitized_dense_data(), _rhsTrans);
1324 
1325  } else { // Full * Sparse => Sparse
1326  matrix_matrix_product(usedResult->override_sparse_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1327  _lhs.get_unsanitized_dense_data(), _lhsTrans, midDim,
1328  _rhs.get_unsanitized_sparse_data(), _rhsTrans);
1329  }
1330  } else {
1331  if(_lhs.is_sparse() && !_rhs.is_sparse()) { // Sparse * Full => Full
1332  matrix_matrix_product(usedResult->override_dense_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1333  _lhs.get_unsanitized_sparse_data(), _lhsTrans, midDim,
1334  _rhs.get_unsanitized_dense_data(), _rhsTrans);
1335 
1336  } else { // Full * Sparse => Full
1337  matrix_matrix_product(usedResult->override_dense_data(), leftDim, rightDim, _lhs.factor*_rhs.factor,
1338  _lhs.get_unsanitized_dense_data(), _lhsTrans, midDim,
1339  _rhs.get_unsanitized_sparse_data(), _rhsTrans);
1340 
1341  }
1342  }
1343  }
1344 
1345  if (sparseResult) {
1347  }
1348 
1349  if(tmpResult) {
1350  _result = std::move(*tmpResult);
1351  }
1352  }
1353 
1354  Tensor contract(const Tensor& _lhs, const bool _lhsTrans, const Tensor& _rhs, const bool _rhsTrans, const size_t _numModes) {
1355  Tensor result;
1356  contract(result, _lhs, _lhsTrans, _rhs, _rhsTrans, _numModes);
1357  return result;
1358  }
1359 
1360 
1361  XERUS_force_inline std::tuple<size_t, size_t, size_t> calculate_factorization_sizes(const Tensor& _input, const size_t _splitPos) {
1362  REQUIRE(_splitPos <= _input.degree(), "Split position must be in range.");
1363 
1364  const size_t lhsSize = misc::product(_input.dimensions, 0, _splitPos);
1365  const size_t rhsSize = misc::product(_input.dimensions, _splitPos, _input.degree());
1366  const size_t rank = std::min(lhsSize, rhsSize);
1367 
1368  return std::make_tuple(lhsSize, rhsSize, rank);
1369  }
1370 
1371  XERUS_force_inline void prepare_factorization_output(Tensor& _lhs, Tensor& _rhs, const Tensor& _input, const size_t _splitPos, const size_t _rank, Tensor::Representation _rep) {
1372  _lhs.factor = 1.0;
1373  _rhs.factor = 1.0;
1374 
1375  if (_lhs.representation != _rep
1376  || _lhs.degree() != _splitPos+1
1377  || _lhs.dimensions.back() != _rank
1378  || !std::equal(_input.dimensions.begin(), _input.dimensions.begin() + _splitPos, _lhs.dimensions.begin()))
1379  {
1380  Tensor::DimensionTuple newDimU;
1381  newDimU.insert(newDimU.end(), _input.dimensions.begin(), _input.dimensions.begin() + _splitPos);
1382  newDimU.push_back(_rank);
1383  _lhs.reset(std::move(newDimU), _rep, Tensor::Initialisation::None);
1384  }
1385 
1386  if (_rhs.representation != _rep
1387  || _rhs.degree() != _input.degree()-_splitPos+1
1388  || _rank != _rhs.dimensions.front()
1389  || !std::equal(_input.dimensions.begin() + _splitPos, _input.dimensions.end(), _rhs.dimensions.begin()+1))
1390  {
1391  Tensor::DimensionTuple newDimVt;
1392  newDimVt.push_back(_rank);
1393  newDimVt.insert(newDimVt.end(), _input.dimensions.begin() + _splitPos, _input.dimensions.end());
1394  _rhs.reset(std::move(newDimVt), _rep, Tensor::Initialisation::None);
1395  }
1396  }
1397 
1398  XERUS_force_inline void set_factorization_output(Tensor& _lhs, std::unique_ptr<value_t[]>&& _lhsData, Tensor& _rhs,
1399  std::unique_ptr<value_t[]>&& _rhsData, const Tensor& _input, const size_t _splitPos, const size_t _rank) {
1400  Tensor::DimensionTuple newDim;
1401  newDim.insert(newDim.end(), _input.dimensions.begin(), _input.dimensions.begin() + _splitPos);
1402  newDim.push_back(_rank);
1403  _lhs.reset(std::move(newDim), std::move(_lhsData));
1404 
1405  newDim.clear();
1406  newDim.push_back(_rank);
1407  newDim.insert(newDim.end(), _input.dimensions.begin() + _splitPos, _input.dimensions.end());
1408  _rhs.reset(std::move(newDim), std::move(_rhsData));
1409  }
1410 
1411  XERUS_force_inline void set_factorization_output(Tensor& _lhs, std::map<size_t, value_t>&& _lhsData, Tensor& _rhs,
1412  std::map<size_t, value_t>&& _rhsData, const Tensor& _input, const size_t _splitPos, const size_t _rank) {
1413  Tensor::DimensionTuple newDim;
1414  newDim.insert(newDim.end(), _input.dimensions.begin(), _input.dimensions.begin() + _splitPos);
1415  newDim.push_back(_rank);
1416  _lhs.reset(std::move(newDim), std::move(_lhsData));
1417 
1418  newDim.clear();
1419  newDim.push_back(_rank);
1420  newDim.insert(newDim.end(), _input.dimensions.begin() + _splitPos, _input.dimensions.end());
1421  _rhs.reset(std::move(newDim), std::move(_rhsData));
1422  }
1423 
1424  void calculate_svd(Tensor& _U, Tensor& _S, Tensor& _Vt, Tensor _input, const size_t _splitPos, const size_t _maxRank, const value_t _eps) {
1425  REQUIRE(0 <= _eps && _eps < 1, "Epsilon must be fullfill 0 <= _eps < 1.");
1426 
1427  size_t lhsSize, rhsSize, rank;
1428  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, _splitPos);
1429 
1430  std::unique_ptr<value_t[]> tmpS(new value_t[rank]);
1431 
1432  // sparse SVD becomes inefficient when the matrix is not sparse enough
1433  // sparse SVD is about equally fast to dense SVD when there are about N = 1.55*(min(m,n)+(max-min)/5) entries set
1434  // will calculate with 2 instead of 1.55 to make sure that we will certainly be slower with sparse
1435  // (note that the algorithm is quadratic in the sparsity)
1436  size_t min = std::min(lhsSize, rhsSize);
1437  size_t max = std::max(lhsSize, rhsSize);
1438  if (_input.is_sparse() && _input.sparsity() > 2*(min+(max-min)/5)) {
1439  _input.use_dense_representation();
1440  }
1441 
1442  // Calculate the actual SVD
1443  if(_input.is_sparse()) {
1444  // calculate QC in both directions
1445  calculate_qc(_U, _input, _input, _splitPos);
1446  calculate_cq(_input, _Vt, _input, 1);
1447  _input.use_dense_representation(); // in the very unlikely case that is it not already dense
1448 
1449  // then calculate SVD only of remaining (hopefully small) core
1450  Tensor UPrime, VPrime;
1451  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, 1);
1452  prepare_factorization_output(UPrime, VPrime, _input, 1, rank, Tensor::Representation::Dense);
1453  blasWrapper::svd(UPrime.override_dense_data(), tmpS.get(), VPrime.override_dense_data(), _input.get_unsanitized_dense_data(), lhsSize, rhsSize);
1454 
1455  // contract U*UPrime and VPrime*V to obtain SVD (UU', S, V'V) from orthogonal U and V as wel as the SVD (U', S, V')
1456  contract(_U, _U, UPrime, 1);
1457  contract(_Vt, VPrime, _Vt, 1);
1458  } else {
1459  prepare_factorization_output(_U, _Vt, _input, _splitPos, rank, Tensor::Representation::Dense);
1460  blasWrapper::svd(_U.override_dense_data(), tmpS.get(), _Vt.override_dense_data(), _input.get_unsanitized_dense_data(), lhsSize, rhsSize);
1461  }
1462 
1463  // Account for hard threshold
1464  if(_maxRank != 0) {
1465  rank = std::min(rank, _maxRank);
1466  }
1467 
1468  // Find rank due to the Epsilon (NOTE the scaling factor can be ignored, as it does not change the ratios).
1469  for(size_t j = 1; j < rank; ++j) {
1470  if (tmpS[j] <= _eps*tmpS[0]) {
1471  rank = j;
1472  break;
1473  }
1474  }
1475 
1476  // Create tensor from diagonal values
1478  for(size_t i = 0; i < rank; ++i) {
1479  _S[i*rank+i] = std::abs(_input.factor)*tmpS[i];
1480  }
1481 
1482  // Account for a negative factor
1483  if(_input.factor < 0.0) {
1484  _Vt *= -1;
1485  }
1486 
1487  _U.resize_mode(_U.degree()-1, rank);
1488  _Vt.resize_mode(0, rank);
1489  }
1490 
1491 
1492  void calculate_qr(Tensor& _Q, Tensor& _R, Tensor _input, const size_t _splitPos) {
1493  size_t lhsSize, rhsSize, rank;
1494  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, _splitPos);
1495  if (_input.is_sparse()) {
1496  std::map<size_t, double> qdata, rdata;
1497  std::tie(qdata, rdata, rank) = internal::CholmodSparse::qc(_input.get_unsanitized_sparse_data(), false, lhsSize, rhsSize, true);
1498  INTERNAL_CHECK(rank == std::min(lhsSize, rhsSize), "IE, sparse qr reduced rank");
1499  set_factorization_output(_Q, std::move(qdata), _R, std::move(rdata), _input, _splitPos, rank);
1502  } else {
1503  prepare_factorization_output(_Q, _R, _input, _splitPos, rank, _input.representation);
1505  }
1506 
1507  _R.factor = _input.factor;
1508  }
1509 
1510 
1511  void calculate_rq(Tensor& _R, Tensor& _Q, Tensor _input, const size_t _splitPos) {
1512  size_t lhsSize, rhsSize, rank;
1513  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, _splitPos);
1514  prepare_factorization_output(_R, _Q, _input, _splitPos, rank, Tensor::Representation::Dense);
1515 
1516  if(_input.is_sparse()) {
1517  LOG_ONCE(warning, "Sparse RQ not yet implemented. falling back to the dense variant"); // TODO
1518  _input.use_dense_representation();
1520  } else {
1522  }
1523 
1524  _R.factor = _input.factor;
1525  }
1526 
1527 
1528  void calculate_qc(Tensor& _Q, Tensor& _C, Tensor _input, const size_t _splitPos) {
1529  size_t lhsSize, rhsSize, rank;
1530  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, _splitPos);
1531 
1532  if(_input.is_sparse()) {
1533  std::map<size_t, double> qdata, cdata;
1534  std::tie(qdata, cdata, rank) = internal::CholmodSparse::qc(_input.get_unsanitized_sparse_data(), false, lhsSize, rhsSize, false);
1535  set_factorization_output(_Q, std::move(qdata), _C, std::move(cdata), _input, _splitPos, rank);
1538  } else {
1539  std::unique_ptr<double[]> QData, CData;
1540  std::tie(QData, CData, rank) = blasWrapper::qc(_input.get_unsanitized_dense_data(), lhsSize, rhsSize);
1541  set_factorization_output(_Q, std::move(QData), _C, std::move(CData), _input, _splitPos, rank);
1542  }
1543 
1544  _C.factor = _input.factor;
1545  }
1546 
1547 
1548  void calculate_cq(Tensor& _C, Tensor& _Q, Tensor _input, const size_t _splitPos) {
1549  size_t lhsSize, rhsSize, rank;
1550  std::tie(lhsSize, rhsSize, rank) = calculate_factorization_sizes(_input, _splitPos);
1551 
1552  if(_input.is_sparse()) {
1553  std::map<size_t, double> qdata, cdata;
1554  std::tie(cdata, qdata, rank) = internal::CholmodSparse::cq(_input.get_unsanitized_sparse_data(), false, rhsSize, lhsSize, false);
1555  set_factorization_output(_C, std::move(cdata), _Q, std::move(qdata), _input, _splitPos, rank);
1558  } else {
1559  std::unique_ptr<double[]> CData, QData;
1560  std::tie(CData, QData, rank) = blasWrapper::cq(_input.get_unsanitized_dense_data(), lhsSize, rhsSize);
1561  set_factorization_output(_C, std::move(CData), _Q, std::move(QData), _input, _splitPos, rank);
1562  }
1563 
1564  _C.factor = _input.factor;
1565  }
1566 
1567 
1568  void pseudo_inverse(Tensor& _inverse, const Tensor& _input, const size_t _splitPos) {
1569  Tensor U, S, Vt;
1570  calculate_svd(U, S, Vt, _input, _splitPos, 0, EPSILON);
1571  S.modify_diagonal_entries([](value_t& _a){ _a = 1/_a;});
1572  contract(_inverse, Vt, true, S, false, 1);
1573  contract(_inverse, _inverse, false, U, true, 1);
1574  }
1575 
1576  Tensor pseudo_inverse(const Tensor& _input, const size_t _splitPos) {
1577  Tensor result;
1578  pseudo_inverse(result, _input, _splitPos);
1579  return result;
1580  }
1581 
1582 
1583  void solve_least_squares(Tensor& _X, const Tensor& _A, const Tensor& _B, const size_t _extraDegree) {
1584  REQUIRE(&_X != &_B && &_X != &_A, "Not supportet yet");
1585  const size_t degM = _B.degree() - _extraDegree;
1586  const size_t degN = _A.degree() - degM;
1587 
1588  REQUIRE(_A.degree() == degM+degN, "Inconsistent dimensions.");
1589  REQUIRE(_B.degree() == degM+_extraDegree, "Inconsistent dimensions.");
1590 
1591  // Make sure X has right dimensions
1592  if( _X.degree() != degN + _extraDegree
1593  || !std::equal(_X.dimensions.begin(), _X.dimensions.begin() + degN, _A.dimensions.begin() + degM)
1594  || !std::equal(_X.dimensions.begin()+ degN, _X.dimensions.end(), _B.dimensions.begin() + degM))
1595  {
1596  Tensor::DimensionTuple newDimX;
1597  newDimX.insert(newDimX.end(), _A.dimensions.begin()+degM, _A.dimensions.end());
1598  newDimX.insert(newDimX.end(), _B.dimensions.begin()+degM, _B.dimensions.end());
1600  }
1601 
1602 
1603  // Calculate multDimensions
1604  const size_t m = misc::product(_A.dimensions, 0, degM);
1605  const size_t n = misc::product(_A.dimensions, degM, degM+degN);
1606  const size_t p = misc::product(_B.dimensions, degM, degM+_extraDegree);
1607 
1608  if (_A.is_sparse()) {
1609  REQUIRE( p == 1, "Matrix least squares not supported in sparse");
1610  if (_B.is_sparse()) {
1612  _X.override_sparse_data(),
1613  n,
1615  false,
1617  m);
1618  } else {
1620  _X.override_dense_data(),
1621  n,
1623  false,
1625  m);
1626  }
1627 
1628  } else { // Dense A
1629  if(_B.is_dense()) {
1631  _X.override_dense_data(),
1632  _A.get_unsanitized_dense_data(), m, n,
1633  _B.get_unsanitized_dense_data(), p);
1634  } else {
1635  LOG_ONCE(warning, "Sparse RHS not yet implemented (casting to dense)"); //TODO
1636 
1637  Tensor Bcpy(_B);
1638  Bcpy.factor = 1.0;
1639  Bcpy.use_dense_representation();
1640 
1642  _X.override_dense_data(),
1643  _A.get_unsanitized_dense_data(), m, n,
1644  Bcpy.get_unsanitized_dense_data(), p);
1645  }
1646  }
1647 
1648  // Propagate the constant factor
1649  _X.factor = _B.factor / _A.factor;
1650  }
1651 
1652 
1653 
1654  void solve(Tensor& _X, const Tensor& _A, const Tensor& _B, const size_t _extraDegree) {
1655  if (_A.is_sparse()) { // TODO
1656  solve_least_squares(_X, _A, _B, _extraDegree);
1657  return;
1658  }
1659  REQUIRE(&_X != &_B && &_X != &_A, "Not supportet yet");
1660  const size_t degM = _B.degree() - _extraDegree;
1661  const size_t degN = _A.degree() - degM;
1662 
1663  REQUIRE(_A.degree() == degM+degN, "Inconsistent dimensions.");
1664  REQUIRE(_B.degree() == degM+_extraDegree, "Inconsistent dimensions.");
1665 
1666  // Make sure X has right dimensions
1667  if( _X.degree() != degN + _extraDegree
1668  || !std::equal(_X.dimensions.begin(), _X.dimensions.begin() + degN, _A.dimensions.begin() + degM)
1669  || !std::equal(_X.dimensions.begin()+ degN, _X.dimensions.end(), _B.dimensions.begin() + degM))
1670  {
1671  Tensor::DimensionTuple newDimX;
1672  newDimX.insert(newDimX.end(), _A.dimensions.begin()+degM, _A.dimensions.end());
1673  newDimX.insert(newDimX.end(), _B.dimensions.begin()+degM, _B.dimensions.end());
1675  }
1676 
1677  // Calculate multDimensions
1678  const size_t m = misc::product(_A.dimensions, 0, degM);
1679  const size_t n = misc::product(_A.dimensions, degM, degM+degN);
1680  const size_t p = misc::product(_B.dimensions, degM, degM+_extraDegree);
1681 
1682 
1683  // Note that A isdense here
1684  if(_B.is_dense()) {
1686  _X.override_dense_data(),
1687  _A.get_unsanitized_dense_data(), m, n,
1688  _B.get_unsanitized_dense_data(), p);
1689  } else {
1690  LOG_ONCE(warning, "Sparse RHS not yet implemented (casting to dense)"); //TODO
1691 
1692  Tensor Bcpy(_B);
1693  Bcpy.factor = 1.0;
1694  Bcpy.use_dense_representation();
1695 
1697  _X.override_dense_data(),
1698  _A.get_unsanitized_dense_data(), m, n,
1699  Bcpy.get_unsanitized_dense_data(), p);
1700  }
1701 
1702  // Propagate the constant factor
1703  _X.factor = _B.factor / _A.factor;
1704  }
1705 
1706 
1707 
1708  Tensor entrywise_product(const Tensor &_A, const Tensor &_B) {
1709  REQUIRE(_A.dimensions == _B.dimensions, "Entrywise product ill-defined for non-equal dimensions.");
1710  if(_A.is_dense() && _B.is_dense()) {
1711  Tensor result(_A);
1712 
1713  result *= _B.factor;
1714  value_t* const dataPtrA = result.get_dense_data();
1715  const value_t* const dataPtrB = _B.get_unsanitized_dense_data();
1716  for(size_t i = 0; i < result.size; ++i) {
1717  dataPtrA[i] *= dataPtrB[i];
1718  }
1719  return result;
1720  }
1721  if(_A.is_sparse()) {
1722  Tensor result(_A);
1723 
1724  for(std::pair<const size_t, value_t>& entry : result.get_sparse_data()) {
1725  entry.second *= _B[entry.first];
1726  }
1727  return result;
1728  }
1729  // _B.is_sparse()
1730  Tensor result(_B);
1731 
1732  for(std::pair<const size_t, value_t>& entry : result.get_sparse_data()) {
1733  entry.second *= _A[entry.first];
1734  }
1735  return result;
1736  }
1737 
1738  bool approx_equal(const xerus::Tensor& _a, const xerus::Tensor& _b, const xerus::value_t _eps) {
1739  REQUIRE(_a.dimensions == _b.dimensions,
1740  "The dimensions of the compared tensors don't match: " << _a.dimensions <<" vs. " << _b.dimensions << " and " << _a.size << " vs. " << _b.size);
1741 
1742  return frob_norm(_a - _b) <= _eps*(_a.frob_norm() + _b.frob_norm())/2.0;
1743  }
1744 
1745  bool approx_entrywise_equal(const Tensor& _a, const Tensor& _b, const value_t _eps) {
1746  REQUIRE(_a.dimensions == _b.dimensions,
1747  "The dimensions of the compared tensors don't match: " << _a.dimensions <<" vs. " << _b.dimensions << " and " << _a.size << " vs. " << _b.size);
1748 
1749  if (_a.is_sparse() && _b.is_sparse()) { // Special treatment if both are sparse, because better asyptotic is possible.
1750  for(const auto& entry : _a.get_unsanitized_sparse_data()) {
1751  if(!misc::approx_equal(_a.factor*entry.second, _b[entry.first], _eps)) { return false; }
1752  }
1753 
1754  for(const auto& entry : _b.get_unsanitized_sparse_data()) {
1755  if(!misc::approx_equal(_a[entry.first], _b.factor*entry.second, _eps)) { return false; }
1756  }
1757  } else {
1758  for(size_t i=0; i < _a.size; ++i) {
1759  if(!misc::approx_equal(_a[i], _b[i], _eps)) { return false; }
1760  }
1761  }
1762  return true;
1763  }
1764 
1765  bool approx_entrywise_equal(const xerus::Tensor& _tensor, const std::vector<value_t>& _values, const xerus::value_t _eps) {
1766  if(_tensor.size != _values.size()) { return false; }
1767  for(size_t i = 0; i < _tensor.size; ++i) {
1768  if(!misc::approx_equal(_tensor[i], _values[i], _eps)) { return false; }
1769  }
1770  return true;
1771  }
1772 
1773  std::ostream& operator<<(std::ostream& _out, const Tensor& _tensor) {
1774  _out << _tensor.to_string();
1775  return _out;
1776  }
1777 
1778  namespace misc {
1779 
1780 
1781  void stream_writer(std::ostream &_stream, const Tensor &_obj, const FileFormat _format) {
1782  if(_format == FileFormat::TSV) {
1783  _stream << std::setprecision(std::numeric_limits<value_t>::digits10 + 1);
1784  }
1785 
1786  // storage version number
1787  write_to_stream<size_t>(_stream, 1, _format);
1788 
1789  write_to_stream(_stream, _obj.dimensions, _format);
1790 
1792  write_to_stream<size_t>(_stream, 1, _format);
1793  for (size_t i = 0; i < _obj.size; ++i) {
1794  write_to_stream<value_t>(_stream, _obj[i], _format);
1795  }
1796  } else {
1797  write_to_stream<size_t>(_stream, 2, _format);
1798  write_to_stream<size_t>(_stream, _obj.get_unsanitized_sparse_data().size(), _format);
1799  for (const auto &d : _obj.get_unsanitized_sparse_data()) {
1800  write_to_stream<size_t>(_stream, d.first, _format);
1801  write_to_stream<value_t>(_stream, _obj.factor*d.second, _format);
1802  }
1803  }
1804  }
1805 
1806 
1807  void stream_reader(std::istream& _stream, Tensor &_obj, const FileFormat _format) {
1808  IF_CHECK(size_t ver = )read_from_stream<size_t>(_stream, _format);
1809  REQUIRE(ver == 1, "Unknown stream version to open (" << ver << ")");
1810 
1811  // Load dimensions
1812  std::vector<size_t> dims;
1813  read_from_stream(_stream, dims, _format);
1814 
1815  // Load representation
1816  const size_t rep = read_from_stream<size_t>(_stream, _format);
1817 
1818  // Load data
1819  if (rep == 1) { // Dense
1821  if(_format == FileFormat::TSV) {
1822  for (size_t i = 0; i < _obj.size; ++i) {
1823  _stream >> (_obj.get_unsanitized_dense_data()[i]);
1824  }
1825  } else {
1826  _stream.read(reinterpret_cast<char*>(_obj.get_unsanitized_dense_data()), std::streamoff(_obj.size*sizeof(value_t)));
1827  }
1828  REQUIRE(_stream, "Unexpected end of stream in reading dense Tensor.");
1829  } else { // Sparse
1830  REQUIRE(rep == 2, "Unknown tensor representation " << rep << " in stream");
1831  _obj.reset(std::move(dims), Tensor::Representation::Sparse);
1832 
1833  const uint64 num = read_from_stream<size_t>(_stream, _format);
1834  REQUIRE(num < std::numeric_limits<size_t>::max(), "The stored Tensor is to large to be loaded using 32 Bit xerus.");
1835 
1836  for (size_t i = 0; i < num; ++i) {
1837  REQUIRE(_stream, "Unexpected end of stream in reading sparse Tensor.");
1838  // NOTE inline function calls can be called in any order by the compiler, so we have to cache the results to ensure correct order
1839  uint64 pos = read_from_stream<size_t>(_stream, _format);
1840  value_t val = read_from_stream<value_t>(_stream, _format);
1841  _obj.get_unsanitized_sparse_data().emplace(pos, val);
1842  }
1843  REQUIRE(_stream, "Unexpected end of stream in reading dense Tensor.");
1844  }
1845  }
1846  } // namespace misc
1847 } // namespace xerus
Header file for some additional math functions.
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > cq(const double *const _A, const size_t _m, const size_t _n)
: splits A = C*Q, with _C an rxm matrix (where r is the rank of _A) and _Q orthogonal.
Initialisation
Flags determining the initialisation of the data of Tensor objects.
Definition: tensor.h:81
Header file for CHECK and REQUIRE macros.
void use_dense_representation_if_desirable()
Converts the Tensor to a dense representation if sparsity * sparsityFactor >= size.
Definition: tensor.cpp:1043
std::tuple< std::unique_ptr< double[]>, std::unique_ptr< double[]>, size_t > qc(const double *const _A, const size_t _m, const size_t _n)
: splits A = Q*C, with _C an rxn matrix (where r is the rank of _A) and _Q orthogonal.
#define CHECK
Definition: internal.h:34
Header file for the blas and lapack wrapper functions.
Tensor sparse_copy() const
Returns a copy of this Tensor that uses a sparse representation.
Definition: tensor.cpp:194
value_t frob_norm() const
Calculates the frobenious norm of the tensor.
Definition: tensor.cpp:286
value_t factor
Single value representing a constant scaling factor.
Definition: tensor.h:105
static size_t sparsityFactor
Definition: tensor.h:72
size_t degree() const
Returns the degree of the tensor.
Definition: tensor.cpp:206
void ensure_own_data()
Ensures that this tensor is the sole owner of its data. If needed new space is allocated and all entr...
Definition: tensor.cpp:1158
void pseudo_inverse(Tensor &_inverse, const Tensor &_input, const size_t _splitPos)
Low-Level calculation of the pseudo inverse of a given Tensor.
Definition: tensor.cpp:1568
static void add_sparse_to_sparse(const std::shared_ptr< std::map< size_t, value_t >> &_sum, const value_t _factor, const std::shared_ptr< const std::map< size_t, value_t >> &_summand)
Adds the given sparse data to the given sparse data.
Definition: tensor.cpp:1121
void qr(double *const _Q, double *const _R, const double *const _A, const size_t _m, const size_t _n)
: Performs (Q,R) = QR(A)
static Tensor XERUS_warn_unused identity(DimensionTuple _dimensions)
: Returns a Tensor representation of the identity operator with the given dimensions.
Definition: tensor.cpp:131
Header file for the Index class.
Header file for the standard contaienr support functions.
uint64_t uint64
Definition: standard.h:63
std::string to_string() const
Creates a string representation of the Tensor.
Definition: tensor.cpp:1067
#define REQUIRE
Definition: internal.h:33
XERUS_force_inline std::tuple< size_t, size_t, size_t > calculate_factorization_sizes(const Tensor &_input, const size_t _splitPos)
Definition: tensor.cpp:1361
Very general class used to represent arbitary tensor networks.
Definition: tensorNetwork.h:42
size_t size
Size of the Tensor – always equal to the product of the dimensions.
Definition: tensor.h:102
Internal representation of an readable indexed Tensor or TensorNetwork.
Tensor & operator=(const Tensor &_other)=default
Standard assignment operator.
void stream_reader(std::istream &_stream, Tensor &_obj, const FileFormat _format)
tries to restore the tensor from a stream of data.
Definition: tensor.cpp:1807
value_t one_norm() const
Calculates the 1-norm of the tensor.
Definition: tensor.cpp:275
void matrix_matrix_product(double *const _C, const size_t _leftDim, const size_t _rightDim, const double _alpha, const double *const _A, const size_t _lda, const bool _transposeA, const size_t _middleDim, const double *const _B, const size_t _ldb, const bool _transposeB)
: Performs the Matrix-Matrix product C = alpha*OP(A) * OP(B)
DimensionTuple dimensions
Vector containing the individual dimensions of the tensor.
Definition: tensor.h:99
void calculate_rq(Tensor &_R, Tensor &_Q, Tensor _input, const size_t _splitPos)
Low-Level RQ calculation of a given Tensor _input = _R _Q.
Definition: tensor.cpp:1511
#define IF_CHECK
Definition: internal.h:35
void calculate_qr(Tensor &_Q, Tensor &_R, Tensor _input, const size_t _splitPos)
Low-Level QR calculation of a given Tensor _input = _Q _R.
Definition: tensor.cpp:1492
Tensor(const Representation _representation=Representation::Sparse)
Constructs an order zero Tensor with the given inital representation.
Definition: tensor.cpp:49
bool approx_equal(T _a, T _b, T _eps=4 *std::numeric_limits< T >::epsilon()) noexcept
: Checks whether the relative difference between _a and _b (i.e. |a-b|/(|a|/2+|b|/2)) is smaller than...
Definition: math.h:91
The main namespace of xerus.
Definition: basic.h:37
Class that handles simple (non-decomposed) tensors in a dense or sparse representation.
Definition: tensor.h:70
value_t * get_dense_data()
Returns a pointer for direct access to the dense data array in row major order.
Definition: tensor.cpp:401
void resize_mode(const size_t _mode, const size_t _newDim, size_t _cutPos=~0ul)
Resizes a specific mode of the Tensor.
Definition: tensor.cpp:626
value_t cat(const size_t _position) const
Unsanitized read access to a single entry.
Definition: tensor.cpp:384
value_t & operator[](const size_t _position)
Read/Write access a single entry.
Definition: tensor.cpp:324
STL namespace.
void fix_mode(const size_t _mode, const size_t _slatePosition)
Fixes a specific mode to a specific value, effectively reducing the order by one. ...
Definition: tensor.cpp:732
std::map< size_t, value_t > & get_unsanitized_sparse_data()
Gives access to the internal sparse map, without any checks.
Definition: tensor.cpp:445
constexpr bool hard_not_equal(const T _a, const T _b) noexcept
: Checks for hard inequality ( != operator) without compiler warnings.
Definition: math.h:115
internal::IndexedTensor< Tensor > operator()(args... _args)
Indexes the Tensor for read/write use.
Definition: tensor.h:613
XERUS_force_inline void prepare_factorization_output(Tensor &_lhs, Tensor &_rhs, const Tensor &_input, const size_t _splitPos, const size_t _rank, Tensor::Representation _rep)
Definition: tensor.cpp:1371
const std::shared_ptr< value_t > & get_internal_dense_data()
Gives access to the internal shared data pointer, without any checks.
Definition: tensor.cpp:431
void ensure_own_data_and_apply_factor()
Checks whether there is a non-trivial factor and applies it. Even if no factor is applied ensure_own_...
Definition: tensor.cpp:1210
Tensor operator*(const value_t _factor, Tensor _tensor)
Calculates the entrywise multiplication of the Tensor _tensor with the constant _factor.
Definition: tensor.cpp:1233
Tensor & operator/=(const value_t _divisor)
Performs the entrywise divison by a constant _divisor.
Definition: tensor.cpp:317
std::vector< size_t > DimensionTuple
: Represention of the dimensions of a Tensor.
Definition: tensor.h:92
void ensure_own_data_no_copy()
Ensures that this tensor is the sole owner of its data space. If needed new space is allocated with e...
Definition: tensor.cpp:1173
std::vector< size_t > get_step_sizes(const Tensor::DimensionTuple &_dimensions)
Definition: tensor.cpp:958
void apply_factor()
Checks whether there is a non-trivial scaling factor and applies it if nessecary. ...
Definition: tensor.cpp:1186
void array_deleter_vt(value_t *const _toDelete)
Internal deleter function, needed because std::shared_ptr misses an array overload.
Definition: basic.cpp:31
void modify_diagonal_entries(const std::function< void(value_t &)> &_f)
Modifies the diagonal entries according to the given function.
Definition: tensor.cpp:841
XERUS_force_inline void read_from_stream(std::istream &_stream, T &_obj, const FileFormat _format)
Definition: fileIO.h:71
static Tensor XERUS_warn_unused ones(DimensionTuple _dimensions)
: Returns a Tensor with all entries equal to one.
Definition: tensor.cpp:122
std::ostream & operator<<(std::ostream &_out, const xerus::Index &_idx)
Allows to pretty print indices, giving the valueId and span.
Definition: index.cpp:175
void reinterpret_dimensions(DimensionTuple _newDimensions)
Reinterprets the dimensions of the tensor.
Definition: tensor.cpp:620
void solve(internal::IndexedTensorWritable< Tensor > &&_x, internal::IndexedTensorReadOnly< Tensor > &&_A, internal::IndexedTensorReadOnly< Tensor > &&_b)
static Tensor XERUS_warn_unused kronecker(DimensionTuple _dimensions)
: Returns a Tensor representation of the kronecker delta.
Definition: tensor.cpp:160
void rq(double *const _R, double *const _Q, const double *const _A, const size_t _m, const size_t _n)
: Performs (R,Q) = RQ(A)
size_t reorder_cost() const
Approximates the cost to reorder the tensor.
Definition: tensor.cpp:270
Header file for the Tensor class.
Internal representation of an readable and writeable indexed Tensor or TensorNetwork.
Definition: indexedTensor.h:37
void calculate_cq(Tensor &_C, Tensor &_Q, Tensor _input, const size_t _splitPos)
Low-Level CQ calculation of a given Tensor _input = _C _Q.
Definition: tensor.cpp:1548
static Tensor XERUS_warn_unused dirac(DimensionTuple _dimensions, const MultiIndex &_position)
: Returns a Tensor with a single entry equals one and all other zero.
Definition: tensor.cpp:173
double one_norm(const double *const _x, const size_t _n)
: Computes the one norm =||x||_1
void contract(Tensor &_result, const Tensor &_lhs, const bool _lhsTrans, const Tensor &_rhs, const bool _rhsTrans, const size_t _numModes)
Low-level contraction between Tensors.
Definition: tensor.cpp:1252
void reset()
Resets the tensor as if default initialized.
Definition: tensor.cpp:559
static void add_sparse_to_full(const std::shared_ptr< value_t > &_denseData, const value_t _factor, const std::shared_ptr< const std::map< size_t, value_t >> &_sparseData)
Adds the given sparse data to the given full data.
Definition: tensor.cpp:1114
FileFormat
possible file formats for tensor storage
Definition: fileIO.h:43
Tensor & operator*=(const value_t _factor)
Performs the entrywise multiplication with a constant _factor.
Definition: tensor.cpp:311
size_t count_non_zero_entries(const value_t _eps=std::numeric_limits< value_t >::epsilon()) const
Determines the number of non-zero entries.
Definition: tensor.cpp:240
void copy_scaled(T *const __restrict _x, const T _alpha, const T *const _y, const size_t _n) noexcept
Copys _n entries from _y to _x, simulationously scaling each entry by the factor _alpha. I.e x = alpha*y.
static std::tuple< std::map< size_t, double >, std::map< size_t, double >, size_t > cq(const std::map< size_t, double > &_A, const bool _transposeA, size_t _m, size_t _n, bool _fullrank)
calculates the sparse CQ decomposition
bool has_factor() const
Checks whether the tensor has a non-trivial global scaling factor.
Definition: tensor.cpp:211
void solve(double *_x, const double *_A, size_t _m, size_t _n, const double *_b, size_t _p)
: Solves Ax = b for x
Tensor operator-(Tensor _lhs, const Tensor &_rhs)
Calculates the entrywise difference between _lhs and _rhs.
Definition: tensor.cpp:1227
Tensor & operator+=(const Tensor &_other)
Adds the _other Tensor entrywise to this one.
Definition: tensor.cpp:299
void reset(DimensionTuple _newDim, const Representation _representation, const Initialisation _init=Initialisation::Zero)
Resets the tensor to the given dimensions and representation.
Definition: tensor.cpp:500
static size_t multiIndex_to_position(const MultiIndex &_multiIndex, const DimensionTuple &_dimensions)
Definition: tensor.cpp:1131
Header file for the low level array support function.
Header file for comfort functions and macros that should not be exported in the library.
Header file for sparse matrix times dense matrix wrapper functions.
constexpr const value_t EPSILON
The default epsilon value in xerus.
Definition: basic.h:50
Tensor operator+(Tensor _lhs, const Tensor &_rhs)
Calculates the entrywise sum of _lhs and _rhs.
Definition: tensor.cpp:1221
void scale(T *const __restrict _x, const T _alpha, const size_t _n) noexcept
Scales _n entries of _x by the factor _alpha. I.e. x = alpha*x.
std::map< size_t, value_t > & override_sparse_data()
Returns a pointer to the internal sparse data map for complete rewrite purpose ONLY.
Definition: tensor.cpp:457
bool all_entries_valid() const
Checks the tensor for illegal entries, e.g. nan, inf,...
Definition: tensor.cpp:256
static void solve_dense_rhs(double *_x, size_t _xDim, const std::map< size_t, double > &_A, const bool _transposeA, const double *_b, size_t _bDim)
solve operator / for dense right hand sites
const std::shared_ptr< std::map< size_t, value_t > > & get_internal_sparse_data()
Gives access to the internal shared sparse data pointer, without any checks.
Definition: tensor.cpp:472
double two_norm(const double *const _x, const size_t _n)
: Computes the two norm =||x||_2
constexpr T sqr(const T &_a) noexcept
: Calculates _a*_a.
Definition: math.h:43
value_t & at(const size_t _position)
Unsanitized access to a single entry.
Definition: tensor.cpp:365
Header file for suitesparse wrapper functions.
static void matrix_matrix_product(std::map< size_t, double > &_C, const size_t _leftDim, const size_t _rightDim, const double _alpha, const std::map< size_t, double > &_A, const bool _transposeA, const size_t _midDim, const std::map< size_t, double > &_B, const bool _transposeB)
Calculates the Matrix Matrix product between two sparse matrices.
double value_t
The type of values to be used by xerus.
Definition: basic.h:43
void use_sparse_representation(const value_t _eps=std::numeric_limits< value_t >::epsilon())
Converts the Tensor to a sparse representation.
Definition: tensor.cpp:1050
XERUS_force_inline void write_to_stream(std::ostream &_stream, const T &_value, FileFormat _format)
Definition: fileIO.h:47
void calculate_svd(Tensor &_U, Tensor &_S, Tensor &_Vt, Tensor _input, const size_t _splitPos, const size_t _maxRank, const value_t _eps)
Low-Level SVD calculation of a given Tensor _input = _U _S _Vt.
Definition: tensor.cpp:1424
void svd(double *const _U, double *const _S, double *const _Vt, const double *const _A, const size_t _m, const size_t _n)
: Performs (U,S,V) = SVD(A)
void stream_writer(std::ostream &_stream, const Tensor &_obj, const FileFormat _format)
pipes all information necessary to restore the current tensor into _stream.
Definition: tensor.cpp:1781
bool approx_entrywise_equal(const xerus::Tensor &_a, const xerus::Tensor &_b, const xerus::value_t _eps=EPSILON)
Checks whether two Tensors are approximately entrywise equal.
Definition: tensor.cpp:1745
bool is_dense() const
Returns whether the current representation is dense.
Definition: tensor.cpp:220
bool approx_equal(const xerus::Tensor &_a, const xerus::Tensor &_b, const xerus::value_t _eps=EPSILON)
Checks whether two tensors are approximately equal.
Definition: tensor.cpp:1738
void solve_least_squares(Tensor &_X, const Tensor &_A, const Tensor &_B, const size_t _extraDegree=0)
Solves the least squares problem ||_A _X - _B||_F.
Definition: tensor.cpp:1583
std::map< size_t, value_t > & get_sparse_data()
Returns a reference for direct access to the sparse data map.
Definition: tensor.cpp:437
XERUS_force_inline void set_factorization_output(Tensor &_lhs, std::unique_ptr< value_t[]> &&_lhsData, Tensor &_rhs, std::unique_ptr< value_t[]> &&_rhsData, const Tensor &_input, const size_t _splitPos, const size_t _rank)
Definition: tensor.cpp:1398
void offset_add(const Tensor &_other, const std::vector< size_t > &_offsets)
Adds the given Tensor with the given offsets to this one.
Definition: tensor.cpp:969
void copy(T *const __restrict _dest, const T *const __restrict _src, const size_t _n) noexcept
Copys _n entries from _y to _x, where _y and _x must be disjunkt memory regions.
static std::tuple< std::map< size_t, double >, std::map< size_t, double >, size_t > qc(const std::map< size_t, double > &_A, const bool _transposeA, size_t _m, size_t _n, bool _fullrank)
calculates the sparse QR decomposition (or qc if fullrank = false)
static XERUS_force_inline std::string to_string(const bool obj)
Definition: stringFromTo.h:41
bool is_sparse() const
Returns whether the current representation is sparse.
Definition: tensor.cpp:226
void set_zero(T *const __restrict _x, const size_t _n) noexcept
Sets all entries equal to zero.
static void plus_minus_equal(Tensor &_me, const Tensor &_other)
Definition: tensor.cpp:1087
static MultiIndex position_to_multiIndex(size_t _position, const DimensionTuple &_dimensions)
Definition: tensor.cpp:1144
#define INTERNAL_CHECK
Definition: internal.h:37
Tensor dense_copy() const
Returns a copy of this Tensor that uses a dense representation.
Definition: tensor.cpp:187
void add_scaled(T *const __restrict _x, const T _alpha, const T *const __restrict _y, const size_t _n) noexcept
Adds _n entries of _y, scaled by _alpha to the ones of _x. I.e. x += alpha*y.
void remove_slate(const size_t _mode, const size_t _pos)
Removes a single slate from the Tensor, reducing dimension[_mode] by one.
Definition: tensor.cpp:772
void perform_trace(size_t _firstMode, size_t _secondMode)
Performs the trace over the given modes.
Definition: tensor.cpp:781
value_t * get_unsanitized_dense_data()
Gives access to the internal data pointer, without any checks.
Definition: tensor.cpp:408
void modify_entries(const std::function< void(value_t &)> &_f)
Modifies every entry according to the given function.
Definition: tensor.cpp:883
Tensor & operator-=(const Tensor &_other)
Subtracts the _other Tensor entrywise from this one.
Definition: tensor.cpp:305
#define LOG_ONCE
Definition: internal.h:40
Header file for the TensorNetwork class.
static void solve_sparse_rhs(std::map< size_t, double > &_x, size_t _xDim, const std::map< size_t, double > &_A, const bool _transposeA, const std::map< size_t, double > &_b, size_t _bDim)
solve operator / for sparse right hand sites
Tensor entrywise_product(const Tensor &_A, const Tensor &_B)
calculates the entrywise product of two Tensors
Definition: tensor.cpp:1708
#define XERUS_force_inline
Collection of attributes to force gcc to inline a specific function.
Definition: standard.h:75
void matrix_matrix_product(double *const _C, const size_t _leftDim, const size_t _rightDim, const double _alpha, const std::map< size_t, double > &_A, const bool _transposeA, const size_t _midDim, const double *const _B, const bool _transposeB)
std::vector< size_t > MultiIndex
: Represention of a MultiIndex, i.e. the tuple of positions for each dimension determining a single p...
Definition: tensor.h:95
value_t * override_dense_data()
Returns a pointer to the internal dense data array for complete rewrite purpose ONLY.
Definition: tensor.cpp:420
Representation representation
The current representation of the Tensor (i.e Dense or Sparse)
Definition: tensor.h:108
void use_dense_representation()
Converts the Tensor to a dense representation.
Definition: tensor.cpp:1028
constexpr T pow(const T &_base, const uint32 _exp) noexcept
: Calculates _base^_exp by binary exponentiation
Definition: math.h:50
size_t sparsity() const
Returns the number currently saved entries.
Definition: tensor.cpp:232
internal::IndexedTensorMoveable< Tensor > operator/(internal::IndexedTensorReadOnly< Tensor > &&_b, internal::IndexedTensorReadOnly< Tensor > &&_A)
Representation
Flags indicating the internal representation of the data of Tensor objects.
Definition: tensor.h:89
void calculate_qc(Tensor &_Q, Tensor &_C, Tensor _input, const size_t _splitPos)
Low-Level QC calculation of a given Tensor _input = _Q _C.
Definition: tensor.cpp:1528
void solve_least_squares(double *const _x, const double *const _A, const size_t _m, const size_t _n, const double *const _b, const size_t _p)
: Solves min ||Ax - b||_2 for x