CASM  1.1.0
A Clusters Approach to Statistical Mechanics
BasisSet.cc
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <functional>
5 
18 #include "casm/misc/CASM_math.hh"
24 
25 namespace CASM {
26 namespace BasisSet_impl {
27 
28 void ArgList::add(std::vector<BasisSet> const &BB) {
29  for (auto const &B : BB) push_back(&B);
30 }
31 
32 void ArgList::add(std::vector<BasisSet const *> const &BB) {
33  for (auto B : BB) push_back(B);
34 }
35 
36 void ArgList::add(Array<BasisSet> const &BB) {
37  for (auto const &B : BB) push_back(&B);
38 }
39 
41  for (auto B : BB) push_back(B);
42 }
43 
44 } // namespace BasisSet_impl
45 //*******************************************************************************************
46 
47 BasisSet::BasisSet(const BasisSet &init_basis)
48  : Array<Function *>(0),
49  m_basis_symrep_ID(init_basis.basis_symrep_ID()),
50  m_name(init_basis.name()),
51  m_basis_ID(init_basis.m_basis_ID),
52  m_min_poly_order(init_basis.m_min_poly_order),
53  m_max_poly_order(init_basis.m_max_poly_order),
54  // m_eval_cache & m_deval_cache are taken care of by BasisSet::push_back
55  // m_eval_cache(init_basis.m_eval_cache),
56  // m_deval_cache(init_basis.m_deval_cache),
57  m_dof_IDs(init_basis.m_dof_IDs),
58  m_dof_subbases(init_basis.m_dof_subbases),
59  m_min_poly_constraints(init_basis.min_poly_constraints()),
60  m_max_poly_constraints(init_basis.max_poly_constraints()) {
61  // std::cout << "Constructing B BSet " << this << ", copy of " << &init_basis
62  // << std::endl;
63 
64  for (Index i = 0; i < init_basis.m_argument.size(); i++) {
65  m_argument.push_back(init_basis.m_argument[i]->shared_copy());
66  }
67 
68  for (Index i = 0; i < init_basis.size(); i++) {
69  if (!init_basis[i])
70  push_back(nullptr);
71  else {
72  push_back(init_basis[i]->copy());
73  }
74  }
75 }
76 
77 //*******************************************************************************************
78 
80  // std::cout << "Copying to C BSet " << this << ", copy of " << &RHS <<
81  // std::endl;
82  if (this == &RHS) {
83  return *this;
84  }
85  clear();
89  // m_eval_cache & m_deval_cache are taken care of by BasisSet::push_back
90  // m_eval_cache = RHS.m_eval_cache;
91  // m_deval_cache = RHS.m_deval_cache;
92  m_eval_cache.clear();
93  m_deval_cache.clear();
94  m_name = RHS.name();
95  m_dof_IDs = RHS.m_dof_IDs;
99 
100  m_argument.clear();
101  for (Index i = 0; i < RHS.m_argument.size(); i++) {
102  m_argument.push_back(RHS.m_argument[i]->shared_copy());
103  }
104 
105  for (Index i = 0; i < RHS.size(); i++) {
106  if (!RHS[i])
107  push_back(nullptr);
108  else {
109  push_back(RHS[i]->copy());
110  }
111  }
112  return *this;
113 }
114 
115 //*******************************************************************************************
116 
118  // std::cout << "Deleting BSet " << this << std::endl;
119  clear();
120 }
121 
122 //*******************************************************************************************
123 
125  for (Index i = 0; i < size(); i++) {
126  if (at(i)) delete at(i);
127  }
129  _refresh_ID();
130 }
131 
132 //*******************************************************************************************
133 
134 void BasisSet::append(const BasisSet &RHS,
135  std::function<Function *(Function *)> const &transform) {
136  // Before appending functions, copy over DoF IDs and subbasis info
137  if (dof_IDs().size()) {
138  for (Index i = 0; i < RHS.m_dof_IDs.size(); i++) {
139  Index ID_ind = find_index(m_dof_IDs, RHS.m_dof_IDs[i]);
140  if (ID_ind == m_dof_IDs.size()) {
141  throw std::runtime_error(
142  "In BasisSet::append(), it is unsafe to append a BasisSet whose "
143  "dependencies differ from (this).");
144  // m_dof_IDs.push_back(RHS.m_dof_IDs[i]);
145  // m_dof_subbases.push_back(SubBasis());
146  }
147 
148  for (Index j = 0; j < RHS.m_dof_subbases[i].size(); j++) {
149  // std::cout << "*** Push back " << j << " to subbases " << ID_ind << "
150  // value " << RHS.m_dof_subbases[i][j] + size() << "\n\n";
151  m_dof_subbases[ID_ind].push_back(RHS.m_dof_subbases[i][j] + size());
152  }
153  }
154  } else
155  set_dof_IDs(RHS.dof_IDs());
156 
157  // Convert polynomial constraints
159  RHS.min_poly_constraints().size());
160  for (Index i = 0; i < RHS.min_poly_constraints().size(); i++) {
162  for (Index j = 0; j < min_poly_constraints().back().first.size(); j++)
163  _min_poly_constraints().back().first[j] += size();
164  }
165 
167  RHS.max_poly_constraints().size());
168  for (Index i = 0; i < RHS.max_poly_constraints().size(); i++) {
170  for (Index j = 0; j < max_poly_constraints().back().first.size(); j++)
171  _max_poly_constraints().back().first[j] += size();
172  }
173 
174  // Convert RHS.min_poly_order() and RHS.max_poly_order into polynomial
175  // constraints
176  if (RHS.min_poly_order() > 0) {
179  RHS.min_poly_order()));
180  }
181  if (valid_index(RHS.max_poly_order())) {
184  RHS.max_poly_order()));
185  }
186  for (Index i = 0; i < RHS.size(); i++) {
187  if (!RHS[i])
188  push_back(nullptr);
189  else {
190  push_back(transform(RHS[i]->copy()));
191  }
192  }
193  _refresh_ID();
194 }
195 
196 //*******************************************************************************************
197 
199  BasisSet new_set;
200  for (Index i = 0; i < size(); i++) {
201  if (!at(i))
202  new_set.push_back(nullptr);
203  else
204  new_set.push_back(at(i)->poly_quotient(divisor));
205  }
206  return new_set;
207 }
208 
209 //*******************************************************************************************
210 
212  const Array<Index> &expons) const {
213  Index exp_sum;
214  for (Index i = 0; i < max_poly_constraints().size(); i++) {
215  exp_sum = 0;
216  for (Index j = 0; j < max_poly_constraints()[i].first.size(); j++) {
217  exp_sum += expons[max_poly_constraints()[i].first[j]];
218  }
219  if (max_poly_constraints()[i].second < exp_sum) return false;
220  }
221  for (Index i = 0; i < min_poly_constraints().size(); i++) {
222  exp_sum = 0;
223  for (Index j = 0; j < min_poly_constraints()[i].first.size(); j++) {
224  exp_sum += expons[min_poly_constraints()[i].first[j]];
225  }
226  if (exp_sum < min_poly_constraints()[i].second) return false;
227  }
228  exp_sum = expons.sum();
229  if (valid_index(min_poly_order()) && exp_sum < min_poly_order()) return false;
230  if (valid_index(max_poly_order()) && max_poly_order() < exp_sum) return false;
231  return true;
232 }
233 //*******************************************************************************************
234 bool BasisSet::accept(const FunctionVisitor &visitor) {
235  bool is_changed(false), tchanged;
236  // std::cout << "Accepting visitor of type " << visitor.type_name() << "...
237  // \n";
238  for (Index i = 0; i < m_argument.size(); i++) {
239  tchanged = m_argument[i]->accept(visitor);
240  is_changed = tchanged || is_changed;
241  }
242  // std::cout << "BasisSet " << name() << " (" << this << ") and is_changed is
243  // " << is_changed << "\n";
244 
245  for (Index i = 0; i < size(); i++) {
246  if (at(i)) {
247  if (is_changed) at(i)->clear_formula();
248  tchanged = at(i)->accept(visitor, this);
249  is_changed = tchanged || is_changed;
250  }
251  }
252  // if(is_changed)_refresh_ID(); //should we do this here?
253  return is_changed;
254 }
255 
256 //*******************************************************************************************
260  assert(size() == cumulant.size());
261  for (Index i = 0; i < m_argument.size(); i++) m_argument[i]->_eval_to_cache();
262 
263  for (Index i = 0; i < size(); i++) {
264  if (at(i)) cumulant[i] += at(i)->cache_eval();
265  }
266 }
267 
268 //*******************************************************************************************
272  const DoF::RemoteHandle &dvar) const {
273  assert(size() == cumulant.size());
274  for (Index i = 0; i < m_argument.size(); i++)
275  m_argument[i]->_deval_to_cache(dvar);
276 
277  for (Index i = 0; i < size(); i++) {
278  if (at(i)) cumulant[i] += at(i)->cache_deval(dvar);
279  }
280 }
281 
282 //*******************************************************************************************
283 
284 bool BasisSet::compare(const BasisSet &RHS) const {
285  if (m_argument.size() != RHS.m_argument.size() || size() != RHS.size())
286  return false;
287 
288  for (Index i = 0; i < m_argument.size(); i++) {
289  if (!m_argument[i]->compare(*(RHS.m_argument[i]))) return false;
290  }
291 
292  for (Index i = 0; i < size(); i++) {
293  if (!(at(i)->shallow_compare(RHS[i]))) return false;
294  }
295  return true;
296 }
297 
298 //*******************************************************************************************
299 
301  int result = -1;
302  for (Index i = 0; i < m_argument.size(); i++)
303  result = CASM::max(result, m_argument[i]->dependency_layer());
304  return ++result;
305 }
306 //*******************************************************************************************
310  remote_eval_to(m_eval_cache.begin(), m_eval_cache.end());
311 }
312 //*******************************************************************************************
316  remote_eval_to(m_eval_cache.begin(), m_eval_cache.end());
317  remote_deval_to(m_deval_cache.begin(), m_deval_cache.end(), dvar);
318  // std::cout << "eval_cache: " << m_eval_cache << "\ndeval_cache: " <<
319  // m_deval_cache << "\n\n";
320 }
321 //*******************************************************************************************
322 void BasisSet::set_variable_basis(DoFSet const &_dof_set) {
323  set_name(_dof_set.type_name());
324  m_argument.clear();
325  m_basis_symrep_ID = _dof_set.symrep_ID();
326  // std::vector<Index> tdof_IDs;
327  for (Index i = 0; i < _dof_set.size(); i++) {
328  if (!_dof_set[i].is_locked()) {
329  Index t_ind = CASM::find_index(m_dof_IDs, _dof_set[i].ID());
330  if (t_ind == m_dof_IDs.size()) {
331  m_dof_IDs.push_back(_dof_set[i].ID());
332  m_dof_subbases.push_back(Array<Index>());
333  }
334  m_dof_subbases[t_ind].push_back(i);
335  }
336  }
337  // set_dof_IDs(tdof_IDs);
338  for (Index i = 0; i < _dof_set.size(); i++) {
339  push_back(new Variable(_dof_set, i));
340  }
341  _refresh_ID();
342 }
343 //*******************************************************************************************
344 void BasisSet::set_dof_IDs(const std::vector<Index> &new_IDs) {
345  _update_dof_IDs(m_dof_IDs, new_IDs);
346 }
347 //*******************************************************************************************
348 // Pass before_IDs by value to avoid aliasing issues when called from
349 // BasisSet::set_dof_IDs()
350 bool BasisSet::_update_dof_IDs(std::vector<Index> before_IDs,
351  const std::vector<Index> &after_IDs) {
352  bool changed = false;
353 
354  // std::cout << "BasisSet " << this << " --> m_dof_IDs is " << m_dof_IDs << ";
355  // before_IDs: " << before_IDs << "; after_ID: " << after_IDs << "\n" <<
356  // std::endl;
357  if (before_IDs.size() != after_IDs.size() && size() > 0) {
358  throw std::runtime_error(
359  "In BasisSet::update_dof_IDs(), new IDs are incompatible with current "
360  "IDs.");
361  }
362  // std::cout << "***_update_dof_IDs on basisset: " << name()<< "\n";
363  // update m_dof_IDs after the other stuff, for easier debugging
364  if (m_dof_IDs.size() == 0) {
365  m_dof_IDs = after_IDs;
366  m_dof_subbases.resize(after_IDs.size());
367  changed = true;
368  } else {
369  Index m;
370  for (Index i = 0; i < m_dof_IDs.size(); i++) {
371  m = find_index(before_IDs, m_dof_IDs[i]);
372  if (m == before_IDs.size())
373  throw std::runtime_error(
374  "In BasisSet::update_dof_IDs(), new IDs are incompatible with "
375  "current IDs.");
376 
377  m_dof_IDs[i] = after_IDs[m];
378  }
379  }
380 
381  for (Index i = 0; i < m_argument.size(); i++)
382  changed = m_argument[i]->_update_dof_IDs(before_IDs, after_IDs) || changed;
383 
384  // std::cout << "Working on basisset: " << name() << " changed is " << changed
385  // << "\n"
386  // << "before_IDs: " << before_IDs << "; after_IDs: " << after_IDs <<
387  // "\n";
388 
389  for (Index i = 0; i < size(); i++) {
390  // std::cout << "Looping " << i+1 << " of " << size() << "; value: " <<
391  // at(i) << "; changed: " << changed <<"\n";
392  if (changed && at(i)) {
393  // std::cout << "clearing formula " << i << "\n";
394  at(i)->clear_formula();
395  }
396  }
397 
398  for (Index i = 0; i < size(); i++) {
399  if (at(i))
400  changed = at(i)->update_dof_IDs(before_IDs, after_IDs) || changed;
401  }
402 
403  return changed;
404 }
405 
406 //*******************************************************************************************
407 
408 std::vector<std::set<Index> > BasisSet::independent_sub_bases() const {
409  std::vector<std::set<Index> > result;
410  std::vector<bool> unclaimed(size(), true);
411  for (Index i = 0; i < dof_sub_bases().size(); i++) {
412  if (dof_sub_basis(i).size() == 0) continue;
413  Index j = 0;
414  for (j = 0; j < result.size(); j++) {
415  if (result[j].find(dof_sub_basis(i)[0]) != result[j].end()) {
416  break;
417  }
418  }
419  if (j == result.size()) result.emplace_back();
420  for (Index k = 0; k < dof_sub_basis(i).size(); k++) {
421  unclaimed[dof_sub_basis(i)[k]] = false;
422  result[j].insert(dof_sub_basis(i)[k]);
423  }
424  }
425  bool has_unclaimed = false;
426  for (Index i = 0; i < unclaimed.size(); i++) {
427  if (unclaimed[i]) {
428  if (!has_unclaimed) {
429  result.emplace_back();
430  has_unclaimed = true;
431  }
432  result.back().insert(i);
433  }
434  }
435  return result;
436 }
437 
438 //*******************************************************************************************
439 
441  const std::vector<DoF::RemoteHandle> &remote_handles) {
442  int sum(0);
443  for (Index i = 0; i < size(); i++)
444  sum += at(i)->register_remotes(remote_handles);
445 
446  for (Index i = 0; i < m_argument.size(); i++)
447  sum += m_argument[i]->register_remotes(remote_handles);
448 
449  return sum;
450 }
451 
452 //*******************************************************************************************
453 
455  Index order) {
456  _set_arguments(tsubs);
457  // Array<BasisSet const *> abset(m_argument);
459  Array<Index> curr_expon(tpoly.poly_coeffs().depth(), 0);
460 
461  IsoCounter<Array<Index> > exp_count(Array<Index>(tsubs[0]->size(), 0),
462  Array<Index>(tsubs[0]->size(), order), 1,
463  order);
464 
465  do {
466  for (int i = 0; i < exp_count.size(); i++) {
467  // std::cout << exp_count()[i] << " ";
468  curr_expon[i] = exp_count()[i];
469  }
470  PolyTrie<double> new_trie(curr_expon.size());
471  new_trie(curr_expon) = 1.0;
472  push_back(tpoly.copy(new_trie));
473  // std::cout << std::endl;
474  } while (++exp_count);
475 }
476 
477 //*******************************************************************************************
478 
480  const SymGroup &head_group,
481  Index order,
482  Index min_dof_order) {
483  // std::cout << "Constructing invariant polynomials of order " << order <<
484  // "\n";
485  _set_arguments(tsubs);
486  // std::cout << "Constructing invariant polynomials from DoFs:\n";
487  for (Index i = 0; i < tsubs.size(); ++i) {
488  if ((tsubs[i]->basis_symrep_ID()).empty()) {
489  tsubs[i]->get_symmetry_representation(head_group);
490  }
491  // std::cout << i+1 << ") " << tsubs[i]->name() << " : ";
492  // for(Index j=0; j<tsubs[i]->size(); ++j){
493  // std::cout << (*tsubs[i])[j]->tex_formula() << "\n";
494  //}
495 
496  // std::cout << "DoF_IDs: " << tsubs[i]->dof_IDs() << "\n";
497  // std::cout << "subbases: ";
498  // for(auto const &subbasis : tsubs[i]->dof_sub_bases())
499  // std::cout << " + " << subbasis << "\n";
500  }
501 
502  // Base case, zero-order function with no arguments -> unit function
503  if (tsubs.size() == 0 && order == 0 && min_poly_order() < 1) {
504  PolyTrie<double> ttrie(0);
505  ttrie(Array<Index>(0)) = 1.0;
507  return;
508  }
509  // std::cout << "\n\n";
510  // std::cout << "dof_IDs are: " << dof_IDs() << "\n\n";
511  // std::cout << "dof_sub_bases are: " << dof_sub_bases() << "\n\n";
512  PolynomialFunction *tpoly;
513  Array<Index> curr_exp;
515  typedef IsoCounter<Array<Index> > OrderCount;
516  typedef MultiCounter<OrderCount> ExpCount;
517 
518  // Add constraints that set a minimum combined order for each local dofset
519  for (Index i = 0; i < dof_IDs().size() && min_dof_order >= 0; i++) {
520  SubBasis big_sub_basis;
521  Index offset = 0;
522  for (Index j = 0; j < tsubs.size(); offset += tsubs[j]->size(), j++) {
523  // std::cout << "tsub " << j << ": size " << tsubs[j]->size() << ",
524  // dof_IDs " << tsubs[j]->dof_IDs() << "\n";
525  Index ID_ind = find_index(tsubs[j]->dof_IDs(), dof_IDs()[i]);
526  if (ID_ind == (tsubs[j]->dof_IDs()).size()) continue;
527  const SubBasis &sub_basis(tsubs[j]->dof_sub_basis(ID_ind));
528  // std::cout << "sub_basis[" << j << "](" << ID_ind << "): " << sub_basis
529  // << "\n";
530  for (Index b = 0; b < sub_basis.size(); b++) {
531  big_sub_basis.push_back(sub_basis[b] + offset);
532  }
533  }
534  // std::cout << "Adding min constraint: " << big_sub_basis << ": " <<
535  // min_dof_order << "\n";
536  add_min_poly_constraint(big_sub_basis, min_dof_order);
537  }
538  //\ end DoFset constraints
539 
540  OrderCount::Container initial_order(tsubs.size(), 0),
541  final_order(tsubs.size(), order);
542  for (Index i = 0; i < tsubs.size(); i++) {
543  if (valid_index(tsubs[i]->min_poly_order()))
544  initial_order[i] = tsubs[i]->min_poly_order();
545  if (valid_index(tsubs[i]->max_poly_order()))
546  final_order[i] = tsubs[i]->max_poly_order();
547  }
548  // std::cout << "order_count from: " << initial_order << " to final order: "
549  // << final_order << "\n\n";
550  OrderCount order_count(initial_order, final_order, 1, order);
551 
552  ExpCount exp_count;
553  for (Index i = 0; i < tsubs.size(); i++) {
554  exp_count.push_back(OrderCount(Array<Index>(tsubs[i]->size(), 0),
555  Array<Index>(tsubs[i]->size(), order), 1,
556  order_count[i]));
557  curr_exp.append(exp_count.back());
558  }
559 
560  for (; order_count.valid(); ++order_count) {
561  // std::cout << "New order_count: ";
562  for (Index i = 0; i < exp_count.size(); i++) {
563  exp_count[i].set_sum_constraint(order_count[i]);
564  // std::cout << order_count[i] << " ";
565  }
566  exp_count.reset();
567  // std::cout << "\n";
568 
569  for (; exp_count.valid(); ++exp_count) {
570  bool valid_expon = true;
571  for (Index i = 0; i < exp_count.size() && valid_expon; i++) {
572  // std::cout << "exp_count[" << i << "] : " << exp_count[i]() << " --
573  // valid? ";
574  valid_expon = tsubs[i]->satisfies_exponent_constraints(exp_count[i]());
575  // std::cout << valid_expon << "\n";
576  }
577 
578  if (!valid_expon) continue;
579 
580  Index ne = 0;
581  ExpCount::const_value_iterator it(exp_count.value_begin()),
582  it_end(exp_count.value_end());
583  // std::cout << "exp: ";
584  for (; it != it_end; ++it) {
585  curr_exp[ne++] = *it;
586  // std::cout << *it << " ";
587  }
588  if (!satisfies_exponent_constraints(curr_exp)) {
589  // std::cout << "failure to add " << curr_exp << "\n\n";
590  continue;
591  }
592  // else:
593  // std::cout << "Adding exponent " << curr_exp << "\n\n";
594 
595  tpoly = new PolynomialFunction(m_argument);
596  for (Index i = 0; i < head_group.size(); i++) {
597  tpoly->transform_monomial_and_add(1, curr_exp, head_group[i]);
598  }
599  push_back(tpoly);
600  }
601  }
602  Gram_Schmidt();
603 
604  // std::cout << "Result, with " << size() << " invariant functions:\n";
605  // for(Index i = 0; i < size(); i++)
606  // std::cout << "F_" << i << " = " << at(i)->tex_formula() << "\n\n";
607  return;
608 }
609 
610 //*******************************************************************************************
611 // Construct generalized occupant functions that is orthonormal with respect to
612 // the inner product defined by gram_mat.
613 // - gram_mat should look like a covariance matrix of random vectors, of
614 // dimension
615 // allowed_occs.size(). This means that gram_mat should be
616 // positive-definite and symmetric. Additionally, the variance of
617 // the vector of all ones (i.e., v={1,1,...,1}) should have
618 // variance = 1, which indicates the constraint of conservation of
619 // number of occupants.
620 //
621 // The goal is to find a matrix 'B' of column vectors such that
622 // B.transpose()*gram_mat*B = Identity
623 // and such that the first column of 'B' is the vector of all ones (i.e.,
624 // v={1,1,...,1}) The first property is general solved by the matrix
625 // B = gram_mat^(-1/2)*W
626 // where gram_mat^(-1/2) is the inverse matrix square root of gram_mat, and 'W'
627 // is an arbitrary orthogonal matrix The second property places constraints on
628 // 'W'. We attempt to find a 'B' that is similar to the Chebychev basis in
629 // certain limiting cases.
630 
632  const DiscreteDoF &allowed_occs, const Eigen::MatrixXd &gram_mat,
633  Index basis_ind, const SymGroup &symgroup) {
634  m_argument.clear();
635  set_name(allowed_occs.type_name());
636  m_max_poly_order = 1;
637  Index N = allowed_occs.size();
638  if (N <= 1) {
640  return;
641  }
642 
643  if (!allowed_occs.is_locked()) {
644  set_dof_IDs(std::vector<Index>(1, allowed_occs.ID()));
646  }
647 
648  // std::cout << "INSIDE construct_orthonormal_discrete_functions and gram_mat
649  // is \n"; std::cout << gram_mat << "\n\n";
650  if (!almost_zero(Eigen::MatrixXd(gram_mat - gram_mat.transpose()))) {
651  // we could 'fix' gram_mat, but that could cause mysterious behavior - leave
652  // it to the user
653  throw std::runtime_error(
654  "Passed a Gram Matrix to "
655  "BasisSet::construct_orthonormal_discrete_functions that is not "
656  "symmetric.");
657  }
658 
659  Eigen::VectorXd conc_vec(gram_mat * Eigen::MatrixXd::Ones(N, 1));
660 
661  if (!almost_equal(1.0, conc_vec.sum())) {
662  std::stringstream ss;
663  // we could 'fix' gram_mat, but that could cause mysterious behavior - leave
664  // it to the user
665  ss << "Passed ill-conditioned Gram Matrix to "
666  "BasisSet::construct_orthonormal_discrete_functions."
667  << "The sum of the elements of the Gram matrix must be equal to 1."
668  << "Gram Matrix is:\n"
669  << gram_mat;
670  throw std::runtime_error(ss.str());
671  }
672 
673  // ** step 1: find a generic 'B' matrix
674 
675  // Use SVD instead of eigendecomposition so that 'U' and 'V' matrices are
676  // orthogonal
677  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> teig(
678  gram_mat, Eigen::ComputeEigenvectors);
679  if (teig.eigenvalues().minCoeff() < TOL) {
680  std::stringstream ss;
681  // we could 'fix' gram_mat, but that could cause mysterious behavior - leave
682  // it to the user
683  ss << "Gram Matrix in BasisSet::construct_orthonormal_discrete_functions "
684  "is not positive-definite."
685  << "Gram Matrix is:\n"
686  << gram_mat
687  << "\nSmallest Eigenvalue = " << teig.eigenvalues().minCoeff();
688  throw std::runtime_error(ss.str());
689  }
690 
691  // B matrix is matrix square root of gram_mat.inverse(). Its columns form an
692  // orthonormal basis wrt gram_mat In other words,
693  // B = V*(1.0/sqrt(D))*V.inverse()
694  // where V is matrix of eigenvectors (as columns) of gram_mat and D is
695  // diagonal matrix of eigenvalues of gram_mat B is not ideally oriented for
696  // our purposes, so the rest of the algorithm will be focused on fixing the
697  // orientation
698  Eigen::MatrixXd B = teig.eigenvectors() *
699  (teig.eigenvalues()
700  .array()
701  .cwiseInverse()
702  .cwiseSqrt()
703  .matrix()
704  .asDiagonal()) *
705  teig.eigenvectors().inverse();
706 
707  // ** step 2: Make seed basis. This will be used to seed optimized orientation
708  // of 'B'
709  Eigen::MatrixXd tseed(Eigen::MatrixXd::Zero(N, N));
710  Eigen::MatrixXd::Index max_ind(0);
711  if (conc_vec.maxCoeff(&max_ind) < 0.75) {
712  // no "outlier" probabilities-> Use Chebychev polynomials as seed
713  // Fill cosine table -- columns contain powers of x from 0 to N-1
714  Eigen::MatrixXd tcos_table(N, N);
715  for (Index i = 0; i < N; i++) {
716  tcos_table(i, 0) = 1.0;
717  double x = -cos(M_PI * (i + 0.5) / double(N));
718  for (Index j = 1; j < N; j++) {
719  tcos_table(i, j) = x * tcos_table(i, j - 1);
720  }
721  }
722 
723  // QR decomposition of tcos_table yields Q matrix that holds chebychev basis
724  tseed = tcos_table.householderQr().householderQ();
725  } else {
726  // there is an outlier probability --> set seed matrix to occupation basis,
727  // with specis 'i==max_ind' as solvent
728  Eigen::MatrixXd::Index curr_i(0);
729  for (Eigen::MatrixXd::Index i = 0; i < B.rows(); i++) {
730  tseed(i, 0) = 1;
731  if (i == max_ind) continue;
732  for (Eigen::MatrixXd::Index j = 1; j < B.cols(); j++) {
733  if (curr_i + 1 == j) tseed(i, j) = 1;
734  }
735  curr_i++;
736  }
737  }
738 
739  // ** step 3: use seed matric to find a unitary matrix that rotates 'B' a more
740  // optimal form Assume: tseed = B * Q, with unitary Q approximate Q by finding
741  // QR decomposition of (B.inverse() * tseed)
742  Eigen::MatrixXd Q = (B.inverse() * tseed).householderQr().householderQ();
743 
744  // Rotate 'B' by multiplication with 'W'
745  // eigen matrix multiplication doesn't alias
746 
747  B = B * Q;
748 
749  // Columns of B are our basis functions, orthonormal wrt gram_mat
750  for (Index i = 1; i < N; i++) {
751  int sign_change = 1;
752  double max_abs(0.0);
753  // The sign of each OccupantFunction is ambiguous, so we use a convention
754  // Force sign convention
755  // max(function(occupation))=max(abs(function(occupation))) If multiple
756  // occupations evaluate to the same abs(phi) and it is the maximal abs(phi),
757  // then use convention that the last occurence is positive
758  // It IS confusing, but here's a simple example:
759  //
760  // phi(occ) = {-1, 0, 1} is always preferred over phi_alt(occ) = {1, 0,
761  // -1}
762  //
763  // even though they are otherwise both equally valid basis functions
764  for (Index j = 0; j < B.rows(); j++) {
765  if (std::abs(B(j, i)) > (max_abs - TOL)) {
766  max_abs = std::abs(B(j, i));
767  sign_change = float_sgn(B(j, i));
768  }
769  }
770 
771  B.col(i) *= double(sign_change);
772  OccupantFunction tOF(allowed_occs, B.col(i), size(), basis_ind,
773  allowed_occs.symrep_ID());
774 
775  push_back(tOF.copy());
776  }
777 
778  // ** step 4: Calculate BasisSet symmetry representation, based on
779  // allowed_occs.symrep_ID() && B matrix Q*B.T=B.T*S, where we know S (how to
780  // transform a column vector), and we want Q (how to transform row vector) so
781  // Q=B.T*S*inv(B.T)
782  if (allowed_occs.symrep_ID().is_identity())
784  else
786  allowed_occs.symrep_ID(),
787  Eigen::MatrixXd(B.rightCols(N - 1).transpose()));
788 }
789 
790 //*******************************************************************************************
791 // Construct orthonormal basis set of OccupantFunctions for degree of freedom
792 // 'allowed_occs'
793 // - 'occ_probs' is an array of probabilities; occ_prob[i] is the probability
794 // of allowed_occs
795 // taking on value allowed_occs[i].
796 // These are equivalent ot composition, but we make a
797 // distinction to avoid confusion with global "parameterized
798 // compositions"
799 //
800 // This method finds a Gram matrix that is consistent with the probabilities and
801 // then passes it on to the overloaded version that takes a Gram matrix. The
802 // convention we use for the gram matrix is kind of arbitrary. Its limiting
803 // cases should yield orthonormality of the Chebychev polynomials when the
804 // probabilities are equal, and orthonormality of the occupation basis when only
805 // one probability is non-zero.
806 
808  const DiscreteDoF &allowed_occs, const std::vector<double> &occ_probs,
809  Index basis_ind, const SymGroup &symgroup) {
810  Index N = allowed_occs.size();
811  if (allowed_occs.size() != occ_probs.size()) {
812  std::stringstream ss;
813  ss << "Error in BasiSet::construct_orthonormal_discrete_functions(): "
814  "occ_probs ([";
815  for (Index i = 0; i < occ_probs.size(); ++i) {
816  if (i != 0) ss << ",";
817  ss << occ_probs[i];
818  }
819  ss << "]) and allowed_occs (size=" + std::to_string(allowed_occs.size()) +
820  ") are incompatible"
821  " on sublattice " +
822  std::to_string(basis_ind) + ".";
823  throw std::runtime_error(ss.str());
824  }
825 
826  double prob_sum(0.);
827  for (double dub : occ_probs) prob_sum += dub;
828 
829  if (!almost_equal(1.0, prob_sum)) {
830  throw std::runtime_error(
831  "In BasiSet::construct_orthonormal_discrete_functions(), occ_probs "
832  "must sum to 1 (they specify a probability distributation).\n");
833  //<< " occ_probs is: " << occ_probs << "\nExiting...\n";
834  }
835 
836  // Build a matrix with N-1 non-zero eigenvalues equal to 1/N.
837  // Remaining eigenvalue is zero, and corresponds to vector of all ones
838  Eigen::MatrixXd gram_mat(Eigen::MatrixXd::Zero(N, N));
839 
840  for (Index i = 0; i < N; i++) {
841  gram_mat(i, i) += occ_probs[i] - occ_probs[i] * occ_probs[i];
842  for (Index j = 0; j < N; j++) {
843  if (i == j) continue;
844 
845  gram_mat(i, i) +=
846  (occ_probs[i] - occ_probs[j]) * (occ_probs[i] - occ_probs[j]);
847 
848  gram_mat(i, j) -=
849  occ_probs[i] * occ_probs[j] +
850  (occ_probs[i] - occ_probs[j]) * (occ_probs[i] - occ_probs[j]);
851  }
852  }
853 
854  // Add in the component corresponding to vector of all ones
855  // this is the uncorrelated part of the covariance
856  for (Index i = 0; i < N; i++) {
857  for (Index j = 0; j < N; j++) {
858  gram_mat(i, j) += occ_probs[i] * occ_probs[j];
859  }
860  }
861  construct_orthonormal_discrete_functions(allowed_occs, gram_mat, basis_ind,
862  symgroup);
863 }
864 
865 //*******************************************************************************************
866 
868  Index order, Index min_order,
869  bool even_only) {
870  _set_arguments(tsubs);
871  // std::cout <<"IN HARMONIC POLYNOMIALS:\n";
872 
873  // std::cout << "dof_IDs(): " << dof_IDs() << "\n";
874  // std::cout << "tsubs->dof_IDs(): " << tsubs[0]->dof_IDs() << "\n";
875  // std::cout << "subbases:\n";
876  // for(auto const& sub : m_dof_subbases){
877  // std::cout << " + "<< sub << "\n";
878  //}
880  Array<Index> expon(tpoly.num_args(), 0);
881  BasisSet even_basis, odd_basis;
882  PolyTrie<double> rtrie(expon.size());
883  for (Index i = 0; i < expon.size(); i++) {
884  expon[i] = 2;
885  rtrie(expon) = 1;
886  expon[i] = 0;
887  }
888  PolynomialFunction Rsqrd(m_argument, rtrie);
889 
890  // PolynomialFunction Rsqrd(/*initialize to R^2 = (a^2+b^2+c^2+...) */)
891  // initialize even basis with s-orbital
892  even_basis.construct_polynomials_by_order(tsubs, 0);
893 
894  // initialize odd basis with p-orbitals
895  odd_basis.construct_polynomials_by_order(tsubs, 1);
896  // std::cout << " construct_polys by order went OK" << std::endl;
897 
898  // add even_basis to (*this)
899  if (min_order <= 0) append(even_basis);
900 
901  // add odd_basis to (*this)
902  if (!even_only && min_order <= 1) append(odd_basis);
903 
904  for (Index i = 2; i <= order; i++) {
905  // Get a reference to either the odd or even basis
906  if (even_only && i % 2 != 0) continue;
907  // if(!even && i%2==0) continue;
908 
909  BasisSet &orthog_basis(i % 2 == 0 ? even_basis : odd_basis);
910 
911  // Multiply orthog_basis by R^2
912  for (Index j = 0; j < orthog_basis.size(); j++) {
913  // In lieu of PolynomialFunction::multiply_by() :
914  Function *tfunc = orthog_basis._at(j);
915  // Function const *temp;
916  // temp = tfunc->multiply(&Rsqrd);
917  orthog_basis._at(j) = (tfunc->multiply(&Rsqrd));
918  //*(orthog_basis._at(j)) = *temp;
919  orthog_basis._at(j)->set_arguments(orthog_basis.m_argument);
920  // Function const *tfunc=orthog_basis[j];
921  // orthog_basis._at(j)= tfunc->multiply(&Rsqrd);
922  // deleting tfunc doesn't work. is this a problem?
923  delete tfunc;
924  }
925  BasisSet curr_order_basis;
926  curr_order_basis.construct_polynomials_by_order(tsubs, i);
927  curr_order_basis.make_orthogonal_to(orthog_basis);
928  curr_order_basis.Gram_Schmidt();
929 
930  orthog_basis.append(curr_order_basis);
931  // add basis for current order to (*this)
932  if (min_order <= i) append(curr_order_basis);
933  }
934  // and fill the sub_bases
935  for (Index i = 0; i < m_dof_subbases.size(); ++i) {
936  m_dof_subbases[i] = SubBasis::sequence(0, size() - 1);
937  }
938 
939  // add polynomial constraint to prevent multiplication of harmonic functions
940  // with each other.
941  if (size() > 0)
944 }
945 
946 //*******************************************************************************************
948  Function *tfunc, *trans_func;
949  for (Index nf = 0; nf < size(); nf++) {
950  // std::cout << "trying function " << nf << " of " << size() << ": ";
951  // at(nf)->print(std::cout);
952  // std::cout << "\n";
953  tfunc = at(nf)->copy();
954  at(nf)->scale(0.0);
955  for (Index ng = 0; ng < head_group.size(); ng++) {
956  trans_func = tfunc->sym_copy_coeffs(head_group[ng]);
957  at(nf)->plus_in_place(trans_func);
958  delete trans_func;
959  }
960  // std::cout << "Result of Reynold's operator is ";
961  // at(nf)->print(std::cout);
962  // std::cout << "\n";
963  delete tfunc;
964  }
965  Gram_Schmidt();
966  return;
967 }
968 
969 //*******************************************************************************************
970 
972  Eigen::MatrixXd &trans_mat) const {
973  if (m_basis_symrep_ID.empty()) {
974  get_symmetry_representation(head_group);
975  }
976  if (m_basis_symrep_ID.empty()) {
977  std::cerr
978  << "CRITICAL ERROR: Inside BasisSet::calc_normal_basis() and cannot "
979  "calculate a valid SymGroup representation. Exiting...\n";
980  exit(1);
981  }
982  if (!head_group.size()) {
983  std::cerr << "CRITICAL ERROR: Inside BasisSet::calc_normal_basis() and and "
984  "passed empty SymGroup. Exiting...\n";
985  }
986 
987  SymGroupRep const &t_rep(
988  head_group[0].master_group().representation(m_basis_symrep_ID));
989  trans_mat = irrep_trans_mat(t_rep, head_group);
990  BasisSet normal_basis(transform_copy(trans_mat));
991  normal_basis.m_basis_symrep_ID = t_rep.master_group().add_representation(
992  coord_transformed_copy(t_rep, trans_mat));
993  return normal_basis;
994 }
995 
996 //*******************************************************************************************
997 
998 void BasisSet::push_back(Function *new_func) {
1000  m_eval_cache.push_back(0.0);
1001  m_deval_cache.push_back(0.0);
1002 
1003  // Nothing having to do with DoF_IDs should be touched here.
1004  // That should happen before pushing back onto the BasisSet
1005  if (new_func != nullptr) {
1006  if (m_argument.size() == 0) _set_arguments(new_func->argument_bases());
1007 
1009  }
1010 }
1011 
1012 //*******************************************************************************************
1013 
1015  if (!size()) return nullptr;
1016  if (size() != coeffs.size()) {
1017  std::cerr
1018  << "FATAL ERROR: In BasisSet::_linear_combination, the number of basis "
1019  "functions \n"
1020  << "does not match the size of the coefficient vector. Exiting...\n";
1021  exit(1);
1022  }
1023 
1024  Function *combfunc(nullptr), *tfunc(nullptr);
1025 
1026  for (EigenIndex i = 0; i < coeffs.size(); i++) {
1027  if (almost_zero(coeffs[i])) continue;
1028  if (!combfunc) {
1029  combfunc = at(i)->copy();
1030  combfunc->scale(coeffs[i]);
1031  continue;
1032  }
1033  tfunc = at(i)->copy();
1034  tfunc->scale(coeffs[i]);
1035  combfunc->plus_in_place(tfunc);
1036  delete tfunc;
1037  }
1038  if (!combfunc) {
1039  combfunc = at(0)->copy();
1040  combfunc->scale(0.0);
1041  }
1042  return combfunc;
1043 }
1044 
1045 //*******************************************************************************************
1046 
1050 
1052  BasisSet copy_basis;
1053  if (trans_mat.cols() != size()) {
1054  std::cerr << "In BasisSet::transform_copy(), attempting to transform basis "
1055  "with a transformation\n"
1056  << "matrix that has incompatible number of columns (has "
1057  << trans_mat.cols() << " and needs " << size() << ").\n"
1058  << "Exiting...\n";
1059  exit(1);
1060  }
1061  for (EigenIndex nc = 0; nc < trans_mat.rows(); nc++) {
1062  copy_basis.push_back(_linear_combination(trans_mat.row(nc)));
1063  }
1064 
1065  // We should also transform the SymGroupRep, but we only have the ID, not the
1066  // MasterSymGroup
1067 
1068  return copy_basis;
1069 }
1070 
1071 //*******************************************************************************************
1072 // TODO: Transform functions with mixed dependency layers
1074  int _dependency_layer /* = 1 */) {
1075  int this_dep_layer = dependency_layer();
1076  if (this_dep_layer == _dependency_layer) {
1077  if (op.has_valid_master() && !m_basis_symrep_ID.empty() &&
1079  auto ptr = op.get_matrix_rep(m_basis_symrep_ID);
1080  if (ptr == nullptr)
1082  else if (!ptr->isIdentity()) {
1085  }
1086  }
1087  for (Index i = 0; i < size(); i++) at(i)->apply_sym_coeffs(op);
1088  } else {
1089  for (Index i = 0; i < m_argument.size(); i++) {
1090  m_argument[i]->apply_sym(op, _dependency_layer);
1091  }
1092  }
1093  return *this;
1094 }
1095 
1096 //*******************************************************************************************
1097 
1099  if (m_argument.size()) {
1100  throw std::runtime_error(
1101  "In BasisSet::_set_arguments(), cannot reset arguments of "
1102  "already-initialized BasisSet.");
1103  }
1104  m_argument.reserve(new_args.size());
1105  Index t_ind;
1106  for (Index i = 0; i < new_args.size(); i++) {
1107  for (Index ID : new_args[i]->dof_IDs()) {
1108  t_ind = CASM::find_index(dof_IDs(), ID);
1109  if (t_ind == dof_IDs().size()) {
1110  m_dof_IDs.push_back(ID);
1111  m_dof_subbases.push_back(Array<Index>());
1112  }
1113  }
1114  m_argument.push_back(new_args[i]->shared_copy());
1115  }
1116 }
1117 
1118 //*******************************************************************************************
1119 // Does modified Gram-Schmidt procedure, using tolerance checking for increased
1120 // speed and stability In worst case, this requires N*(N-1)/2 binary operations
1121 
1122 // In future, may wish to use alternative approach: 1) find Gram matrix G, where
1123 // G(i,j)=at(i)->dot(at(j));
1124 // 2) find orthonormal
1125 // eigenvectors of G, forming
1126 // columns of the matrix V 3)
1127 // take the linear combination
1128 // V.transpose()*(*this)
1129 // This alternate approach may be more numerically stable, but probably results
1130 // in less sparse representations, unless there is a way to compute an optimally
1131 // sparse V matrix
1132 
1134  bool is_unchanged(true);
1135  Index i, j;
1136  double tcoeff;
1137  Function *tfunc(nullptr);
1138 
1139  // loop over functions
1140  for (i = 0; i < size(); i++) {
1141  at(i)->small_to_zero(2 * TOL);
1142 
1143  tcoeff = sqrt(at(i)->dot(at(i)));
1144 
1145  if (tcoeff < TOL) {
1146  is_unchanged = false;
1147  delete at(i);
1148  remove(i);
1149  i--;
1150  continue;
1151  } else if (!almost_zero(tcoeff - 1.0)) {
1152  is_unchanged = false;
1153  at(i)->scale(1.0 / tcoeff);
1154  }
1155 
1156  // loop from i+1 to end and subtract projection of Function i onto Function
1157  // j
1158  for (j = i + 1; j < size(); j++) {
1159  tcoeff = (at(i)->dot(at(j)));
1160  if (almost_zero(tcoeff)) {
1161  continue;
1162  }
1163 
1164  is_unchanged = false;
1165 
1166  tfunc = at(i)->copy();
1167 
1168  if (!almost_zero(tcoeff - 1)) {
1169  tfunc->scale(tcoeff);
1170  }
1171 
1172  at(j)->minus_in_place(tfunc);
1173 
1174  delete tfunc;
1175  }
1176  }
1177  if (!is_unchanged) m_basis_symrep_ID = SymGroupRepID();
1178  return is_unchanged;
1179 }
1180 
1181 //*******************************************************************************************
1182 
1184  bool is_unchanged(true);
1185  Index i(0), j(0);
1186  Index j_min(0), i_min(0), i_temp;
1187  double tcoeff, min_coeff(1e10);
1188  Function *tfunc;
1189  while (i < size()) {
1190  j_min = Index(-1);
1191  for (i_temp = i; i_temp < size(); i_temp++) {
1192  tcoeff = at(i_temp)->leading_coefficient(j);
1193  if (almost_zero(tcoeff)) {
1194  delete at(i_temp);
1195  remove(i_temp);
1196  i_temp--;
1197  is_unchanged = false;
1198  continue;
1199  }
1200  if (!valid_index(j_min) || j < j_min ||
1201  (j == j_min && std::abs(tcoeff) > std::abs(min_coeff))) {
1202  j_min = j;
1203  i_min = i_temp;
1204  min_coeff = tcoeff;
1205  }
1206  }
1207  if (i >= size()) break;
1208 
1209  j = j_min;
1210  if (i != i_min) {
1211  is_unchanged = false;
1212  swap_elem(i, i_min);
1213  }
1214  if (!almost_zero(min_coeff - 1)) {
1215  at(i)->scale(1.0 / min_coeff);
1216  }
1217 
1218  for (i_temp = 0; i_temp < size(); i_temp++) {
1219  if (i_temp == i) continue;
1220  tcoeff = at(i_temp)->get_coefficient(j);
1221  if (almost_zero(tcoeff)) continue;
1222  is_unchanged = false;
1223  if (almost_zero(tcoeff - 1)) {
1224  at(i_temp)->minus_in_place(at(i));
1225  continue;
1226  }
1227  tfunc = at(i)->copy();
1228  tfunc->scale(tcoeff);
1229  at(i_temp)->minus_in_place(tfunc);
1230  delete tfunc;
1231  }
1232  i++;
1233  }
1234  for (i = 0; i < size(); i++) {
1235  at(i)->small_to_zero(2 * TOL);
1236  }
1237 
1238  if (!is_unchanged) m_basis_symrep_ID = SymGroupRepID();
1239 
1240  return is_unchanged;
1241 }
1242 //*******************************************************************************************
1243 void BasisSet::get_symmetry_representation(const SymGroup &head_group) const {
1244  if (!head_group.size() || !head_group[0].has_valid_master()) return;
1245 
1247  Function *tfunct(nullptr);
1248  Eigen::MatrixXd tRep(size(), size());
1249 
1250  for (Index ng = 0; ng < head_group.size(); ng++) {
1251  // Get representation for operation head_group[ng]
1252  // store it in matrix tRep
1253  for (Index nb1 = 0; nb1 < size(); nb1++) {
1254  tfunct = at(nb1)->sym_copy_coeffs(head_group[ng]);
1255  for (Index nb2 = 0; nb2 < size(); nb2++) {
1256  tRep(nb2, nb1) = tfunct->dot(at(nb2));
1257  }
1258  delete tfunct;
1259  }
1260 
1261  // We have the representation of operation head_group[ng]
1262  // Make a new SymOpRepresentation out of it and push it back
1263  head_group[ng].set_rep(m_basis_symrep_ID, SymMatrixXd(tRep));
1264  }
1265 }
1266 
1267 //*******************************************************************************************
1268 
1269 bool BasisSet::make_orthogonal_to(const BasisSet &ortho_basis) {
1270  bool ortho_flag(true);
1271  for (Index i = 0; i < ortho_basis.size(); i++) {
1272  ortho_flag = make_orthogonal_to(ortho_basis[i]) && ortho_flag;
1273  }
1274  return ortho_flag;
1275 }
1276 
1277 //*******************************************************************************************
1278 
1279 bool BasisSet::make_orthogonal_to(Function const *ortho_func) {
1280  bool ortho_flag(true);
1281  if (!size()) {
1282  return ortho_flag;
1283  }
1284 
1285  double tcoeff;
1286  Function *tfunc(ortho_func->copy());
1287 
1288  // std::cout << "Ortho_func: " << tfunc->tex_formula() << "\n\n";
1289  tcoeff = tfunc->dot(tfunc);
1290  // std::cout << "Squared magnitude is " << tcoeff << "\n";
1291 
1292  if (almost_zero(tcoeff)) {
1293  delete tfunc;
1294  return ortho_flag;
1295  }
1296 
1297  if (!almost_zero(tcoeff - 1.0)) {
1298  tfunc->scale(1.0 / sqrt(tcoeff));
1299  }
1300  for (Index i = 0; i < size(); i++) {
1301  // std::cout << "Func " << i << ": " << at(i)->tex_formula() << "\n\n";
1302  tcoeff = (at(i)->dot(tfunc));
1303  // std::cout << "Projection: " << tcoeff << "\n\n";
1304  if (almost_zero(tcoeff)) {
1305  continue;
1306  }
1307  ortho_flag = false;
1308  // You're changing the BasisSet, so the representation is no longer useable!
1310 
1311  tfunc->scale(tcoeff);
1312  at(i)->minus_in_place(tfunc);
1313  tfunc->scale(1.0 / tcoeff);
1314  at(i)->normalize();
1315 
1316  // std::cout << "Result is " << at(i)->tex_formula() << "\n\n";
1317  }
1318 
1319  delete tfunc;
1320 
1321  return ortho_flag;
1322 }
1323 
1325  if (tsubs.empty()) return BasisSet();
1326  multivector<Index>::X<2> compat_maps;
1327  compat_maps.reserve(tsubs.size());
1328  BasisSet::ArgList tot_args;
1329  std::set<Index> dof_ids;
1330  std::string new_name = tsubs.size() ? tsubs[0]->name() : "";
1331 
1332  for (auto const &sub : tsubs) {
1333  if ((sub->name()).find(new_name) == std::string::npos)
1334  new_name += ("+" + sub->name());
1335  dof_ids.insert(sub->dof_IDs().begin(), sub->dof_IDs().end());
1336  compat_maps.push_back({});
1337  for (auto const &arg : sub->arguments()) {
1338  Index i = 0;
1339  for (; i < tot_args.size(); ++i) {
1340  if (tot_args[i]->compare(*arg)) break;
1341  }
1342  if (i == tot_args.size()) tot_args.add(*arg);
1343  compat_maps.back().push_back(i);
1344  }
1345  }
1346 
1347  std::vector<Index> dof_ids_vec(dof_ids.begin(), dof_ids.end());
1348  BasisSet result(new_name, tot_args);
1349  result.set_dof_IDs(dof_ids_vec);
1350  for (Index i = 0; i < tsubs.size(); ++i) {
1351  Index l = 0;
1352  result.append(*(tsubs[i]), [&](Function *f) -> Function * {
1353  f->set_arguments(result.arguments(), compat_maps[i]);
1354  if (f->identifier('l') == "?")
1355  f->set_identifier('l', std::to_string(l++));
1356  return f;
1357  });
1358  }
1359  return result;
1360 }
1361 //*******************************************************************************************
1362 //** jsonParser stuff - BasisSet
1363 //*******************************************************************************************
1364 
1366  json.put_obj();
1367 
1368  // class BasisSet: public Array<Function *>
1369  json["basis_functions"].put_array();
1370  for (Index i = 0; i < size(); i++) {
1371  json["basis_functions"].push_back(at(i));
1372  }
1373 
1374  // mutable int m_basis_symrep_ID;
1375  json["m_basis_symrep_ID"] = m_basis_symrep_ID;
1376 
1377  // Array<BasisSet> subspaces;
1378  // json["subspaces"] = m_subspaces;
1379 
1380  return json;
1381 }
1382 
1383 //*******************************************************************************************
1384 
1385 /*
1386 void BasisSet::from_json(const jsonParser &json) {
1387 
1388  // no reading BasisSet for now
1389 
1390 }
1391 */
1392 
1393 //*******************************************************************************************
1394 
1396  return bset.to_json(json);
1397 }
1398 
1399 //*******************************************************************************************
1400 /*
1401 // No reading functions for now
1402 void from_json(BasisSet &bset, const jsonParser &json) {
1403  return bset.from_json(json);
1404 }
1405 */
1406 
1407 //*******************************************************************************************
1408 BasisSet operator*(const SymOp &LHS, const BasisSet &RHS) {
1409  return BasisSet(RHS).apply_sym(LHS);
1410 }
1411 
1412 } // namespace CASM
Basic std::vector like container (deprecated)
Definition: Array.hh:45
T & back()
Definition: Array.hh:160
Function * & at(Index ind)
Definition: Array.hh:140
Index size() const
Definition: Array.hh:131
void swap_elem(Index i, Index j)
Definition: Array.hh:197
void clear()
Definition: Array.hh:182
size_type size() const
Definition: BaseCounter.hh:178
void add(BasisSet const &B)
Definition: BasisSet.hh:34
void clear()
Definition: BasisSet.cc:124
void construct_invariant_polynomials(ArgList const &tsubs, const SymGroup &head_sym_group, Index order, Index min_dof_order=1)
Definition: BasisSet.cc:479
void _deval_to_cache(const DoF::RemoteHandle &_dvar) const
Definition: BasisSet.cc:315
void construct_polynomials_by_order(ArgList const &tsubs, Index order)
Definition: BasisSet.cc:454
std::vector< std::shared_ptr< BasisSet > > m_argument
Definition: BasisSet.hh:252
void remote_eval_and_add_to(Array< double > &cumulant) const
Definition: BasisSet.cc:259
Index m_max_poly_order
Definition: BasisSet.hh:262
SymGroupRepID basis_symrep_ID() const
Definition: BasisSet.hh:80
std::vector< Index > m_dof_IDs
Definition: BasisSet.hh:264
const Array< SubBasis > & dof_sub_bases() const
Definition: BasisSet.hh:148
Array< Index > SubBasis
Definition: BasisSet.hh:51
bool compare(const BasisSet &RHS) const
Definition: BasisSet.cc:284
bool _update_dof_IDs(const std::vector< Index > before_IDs, const std::vector< Index > &after_IDs)
Definition: BasisSet.cc:350
BasisSet calc_normal_basis(const SymGroup &head_sym_group, Eigen::MatrixXd &trans_mat) const
Definition: BasisSet.cc:971
void append(const BasisSet &RHS, std::function< Function *(Function *)> const &transform=CASM_TMP::UnaryIdentity< Function * >())
Append contents of.
Definition: BasisSet.cc:134
bool satisfies_exponent_constraints(const Array< Index > &expons) const
Definition: BasisSet.cc:211
void remote_eval_to(IteratorType result_begin, IteratorType result_end) const
Definition: BasisSet.hh:323
void construct_harmonic_polynomials(const ArgList &tsubs, Index order, Index min_order, bool even_only)
Definition: BasisSet.cc:867
void set_variable_basis(const DoFSet &_dof_set)
Define the basis set to contain only variables (e.g., x,y,z)
Definition: BasisSet.cc:322
void set_dof_IDs(const std::vector< Index > &new_IDs)
Definition: BasisSet.cc:344
std::vector< std::set< Index > > independent_sub_bases() const
Definition: BasisSet.cc:408
void calc_invariant_functions(const SymGroup &head_sym_group)
Definition: BasisSet.cc:947
void _eval_to_cache() const
Definition: BasisSet.cc:309
const Array< PolyConstraint > & max_poly_constraints() const
Definition: BasisSet.hh:88
std::string m_name
Definition: BasisSet.hh:249
void remote_deval_and_add_to(Array< double > &cumulant, const DoF::RemoteHandle &dvar) const
Definition: BasisSet.cc:271
BasisSet(const std::string &name="", ArgList const &_args=ArgList())
Definition: BasisSet.hh:58
void _refresh_ID()
Definition: BasisSet.hh:288
std::pair< SubBasis, Index > PolyConstraint
Definition: BasisSet.hh:52
Array< PolyConstraint > & _min_poly_constraints()
Definition: BasisSet.hh:304
const std::vector< Index > & dof_IDs() const
Definition: BasisSet.hh:144
std::vector< double > m_deval_cache
Definition: BasisSet.hh:269
int dependency_layer() const
Definition: BasisSet.cc:300
jsonParser & to_json(jsonParser &json) const
Definition: BasisSet.cc:1365
void set_name(const std::string &new_name)
Definition: BasisSet.hh:123
Function * _linear_combination(const Eigen::VectorXd &coeffs) const
Definition: BasisSet.cc:1014
bool accept(const FunctionVisitor &visitor)
Definition: BasisSet.cc:234
void push_back(Function *new_func)
Definition: BasisSet.cc:998
void add_min_poly_constraint(const Array< Index > &expons, Index expon_sum)
Definition: BasisSet.hh:127
void construct_orthonormal_discrete_functions(const DiscreteDoF &allowed_occs, const Eigen::MatrixXd &gram_mat, Index basis_ind, const SymGroup &symgroup)
Definition: BasisSet.cc:631
const SubBasis & dof_sub_basis(Index i) const
Definition: BasisSet.hh:146
Index min_poly_order() const
Definition: BasisSet.hh:83
const std::string & name() const
Definition: BasisSet.hh:74
BasisSet poly_quotient_set(const Function *divisor) const
Definition: BasisSet.cc:198
Array< SubBasis > m_dof_subbases
Definition: BasisSet.hh:265
Function * _back()
Definition: BasisSet.hh:278
void get_symmetry_representation(const SymGroup &head_sym_group) const
Definition: BasisSet.cc:1243
Index m_min_poly_order
Definition: BasisSet.hh:262
Function *& _at(Index i)
Definition: BasisSet.hh:281
BasisSet transform_copy(const Eigen::MatrixXd &trans_mat) const
Definition: BasisSet.cc:1051
Index max_poly_order() const
Definition: BasisSet.hh:82
int register_remotes(const std::vector< DoF::RemoteHandle > &remote_handles)
Definition: BasisSet.cc:440
bool make_orthogonal_to(Function const *ortho_func)
Definition: BasisSet.cc:1279
bool Gaussian_Elim()
Definition: BasisSet.cc:1183
Array< PolyConstraint > & _max_poly_constraints()
Definition: BasisSet.hh:307
SymGroupRepID m_basis_symrep_ID
Definition: BasisSet.hh:247
std::vector< std::shared_ptr< BasisSet > > const & arguments() const
Definition: BasisSet.hh:76
std::shared_ptr< BasisSet > shared_copy() const
Definition: BasisSet.hh:93
void _set_arguments(const ArgList &new_args)
Definition: BasisSet.cc:1098
BasisSet & apply_sym(const SymOp &op, int dependency_layer=1)
Definition: BasisSet.cc:1073
const BasisSet & operator=(const BasisSet &RHS)
Definition: BasisSet.cc:79
std::vector< double > m_eval_cache
Definition: BasisSet.hh:268
const Array< PolyConstraint > & min_poly_constraints() const
Definition: BasisSet.hh:85
bool Gram_Schmidt()
Definition: BasisSet.cc:1133
void remote_deval_to(IteratorType result_begin, IteratorType result_end, const DoF::RemoteHandle &dvar) const
Definition: BasisSet.hh:339
virtual Index size() const =0
SymGroupRepID symrep_ID() const
Definition: DoF.hh:200
Index ID() const
Const access of integer ID.
Definition: DoF.hh:140
bool is_locked() const
true if ID is locked
Definition: DoF.hh:148
std::string type_name() const
Const access of DoF type name.
Definition: DoF.hh:134
SymGroupRepID const & symrep_ID() const
Definition: DoFSet.hh:276
Index size() const
Returns the number of components in this DoFSet.
Definition: DoFSet.hh:191
std::string const & type_name() const
Definition: DoFSet.hh:197
Function * plus_in_place(Function const *RHS)
virtual double leading_coefficient() const =0
std::string identifier(char _key) const
virtual double cache_eval() const =0
virtual void scale(double scale_factor)=0
Function * minus_in_place(Function const *RHS)
virtual Function * copy() const =0
virtual int register_remotes(const std::vector< DoF::RemoteHandle > &remote_handles)
void clear_formula()
bool accept(const FunctionVisitor &visitor, BasisSet const *home_basis_ptr=nullptr)
virtual Function * apply_sym_coeffs(const SymOp &op, int dependency_layer=1)
virtual double get_coefficient(Index i) const =0
const ArgumentContainer & argument_bases() const
void set_identifier(char _key, std::string const &_value)
bool update_dof_IDs(const std::vector< Index > &before_IDs, const std::vector< Index > &after_IDs)
Index num_args() const
Function * poly_quotient(Function const *RHS) const
double dot(Function const *RHS) const
Function * sym_copy_coeffs(const SymOp &op, int dependency_layer=1) const
virtual void small_to_zero(double tol=TOL)=0
void set_arguments(const ArgumentContainer &new_arg)
change arguments of this function
virtual double cache_deval(const DoF::RemoteHandle &dvar) const =0
Function * multiply(Function const *RHS) const
A IsoCounter allows looping over many incrementing variables in one loop.
Definition: IsoCounter.hh:96
SymGroupRepID add_representation(const SymGroupRep &new_rep) const
Definition: SymGroup.cc:702
SymGroupRepID add_transformed_rep(SymGroupRepID orig_ID, const Eigen::MatrixXd &trans_mat) const
Definition: SymGroup.cc:685
Function * copy() const override
Index depth() const
Definition: PolyTrie.hh:213
Function * copy() const override
const PolyTrie< double > & poly_coeffs() const
Function * transform_monomial_and_add(double prefactor, const Array< Index > &ind, const SymOp &op)
SymGroup is a collection of symmetry operations that satisfy the group property The symmetry operatio...
Definition: SymGroup.hh:42
const MasterSymGroup & master_group() const
Definition: SymGroup.hh:73
SymGroupRepID allocate_representation() const
Add a new empty representation.
Definition: SymGroup.cc:1601
SymGroupRep is an alternative representation of a SymGroup for something other than real space....
Definition: SymGroupRep.hh:31
const MasterSymGroup & master_group() const
Reference to MasterSymGroup for which this SymGroupRep is a group representation If a MasterSymGroup ...
Definition: SymGroupRep.hh:76
Type-safe ID object for communicating and accessing Symmetry representation info.
static SymGroupRepID identity(Index dim)
Static function to construct an ID for identity representations.
bool empty() const
Returns true if SymGroupRepID has not been initialized with valid group_index or rep_index.
bool is_identity() const
Returns true if SymGroupRepID corresponds to an Identity representation.
Generalized symmetry matrix representation for arbitrary dimension Can be used to describe applicatio...
Definition: SymMatrixXd.hh:26
SymOp is the Coordinate representation of a symmetry operation it keeps fraction (FRAC) and Cartesian...
Definition: SymOp.hh:28
const MasterSymGroup & master_group() const
const access of head group
Eigen::MatrixXd const * get_matrix_rep(SymGroupRepID _rep_ID) const
get pointer to matrix representation corresponding to rep_ID
bool has_valid_master() const
check if this representation is registered with a MasterSymGroup
jsonParser & put_obj()
Puts new empty JSON object.
Definition: jsonParser.hh:354
jsonParser & put_array()
Puts new empty JSON array.
Definition: jsonParser.hh:362
T sum() const
Definition: Array.hh:552
void reserve(Index new_max)
Definition: Array.hh:411
static ReturnArray< T > sequence(const T &initial, const T &final)
Definition: Array.hh:275
void push_back(const T &toPush)
Definition: Array.hh:431
Array & append(const Array &new_tail)
Definition: Array.hh:808
Index find(const Function * &test_elem) const
Definition: Array.hh:618
void remove(Index ind)
Definition: Array.hh:462
std::string to_string(ENUM val)
Return string representation of enum class.
Definition: io_traits.hh:172
T dot(const Tensor< T > &LHS, const Tensor< T > &RHS)
Definition: Tensor.hh:925
jsonParser & push_back(const T &value, Args &&... args)
Definition: jsonParser.hh:684
Main CASM namespace.
Definition: APICommand.hh:8
bool almost_equal(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Check if ClusterInvariants are equal.
Eigen::MatrixXd MatrixXd
jsonParser & to_json(const ClexDescription &desc, jsonParser &json)
bool compare(ClusterInvariants const &A, ClusterInvariants const &B, double tol)
Compare ClusterInvariants.
Index find_index(Iterator begin, Iterator end, const T &value)
Equivalent to std::distance(begin, std::find(begin, end, value))
Definition: algorithm.hh:24
SymGroupRep coord_transformed_copy(SymGroupRep const &_rep, const Eigen::MatrixXd &trans_mat)
Make a copy of representation on vector space 'V' that is transformed into a representation on vector...
Definition: SymGroupRep.cc:175
BasisSet direct_sum(BasisSet::ArgList const &_subs)
Definition: BasisSet.cc:1324
BasisSet operator*(const SymOp &LHS, const BasisSet &RHS)
Definition: BasisSet.cc:1408
const double TOL
Definition: definitions.hh:30
GenericDatumFormatter< std::string, DataObject > name()
bool almost_zero(const T &val, double tol=TOL)
If T is not integral, use std::abs(val) < tol;.
Definition: CASM_math.hh:104
int float_sgn(T val, double compare_tol=TOL)
Definition: CASM_math.hh:188
Eigen::MatrixXd irrep_trans_mat(SymGroupRep const &_rep, const SymGroup &head_group)
Finds the transformation matrix that block-diagonalizes this representation of head_group into irrep ...
Eigen::MatrixXd::Index EigenIndex
Definition: eigen.hh:7
bool valid_index(Index i)
Definition: definitions.cc:5
INDEX_TYPE Index
For long integer indexing:
Definition: definitions.hh:39
Eigen::VectorXd VectorXd
T max(const T &A, const T &B)
Definition: CASM_math.hh:95
pair_type bset
Definition: settings.cc:145
Shortcut for multidimensional vector (std::vector< std::vector< ...)
Definition: multivector.hh:26