3 // implementation of "binomial_t" starts here
5 clebsch::binomial_t binomial;
7 int clebsch::binomial_t::operator()(int n, int k) {
9 for (cache.resize((n + 1) * (n + 2) / 2); N <= n; ++N) {
10 cache[N * (N + 1) / 2] = cache[N * (N + 1) / 2 + N] = 1;
11 for (int k = 1; k < N; ++k) {
12 cache[N * (N + 1) / 2 + k] = cache[(N - 1) * N / 2 + k]
13 + cache[(N - 1) * N / 2 + k - 1];
18 return cache[n * (n + 1) / 2 + k];
21 // implementation of "weight" starts here
23 clebsch::weight::weight(int N) : elem(N), N(N) {}
25 clebsch::weight::weight(int N, int index) : elem(N, 0), N(N) {
26 for (int i = 0; index > 0 && i < N; ++i) {
27 for (int j = 1; binomial(N - i - 1 + j, N - i - 1) <= index; j <<= 1) {
31 for (int j = elem[i] >> 1; j > 0; j >>= 1) {
32 if (binomial(N - i - 1 + (elem[i] | j), N - i - 1) <= index) {
37 index -= binomial(N - i - 1 + elem[i]++, N - i - 1);
41 clebsch::weight &clebsch::weight::operator=(const clebsch::weight &w) {
42 int &n = const_cast<int &>(N);
48 int &clebsch::weight::operator()(int k) {
49 assert(1 <= k && k <= N);
53 const int &clebsch::weight::operator()(int k) const {
54 assert(1 <= k && k <= N);
58 bool clebsch::weight::operator<(const weight &w) const {
60 for (int i = 0; i < N; ++i) {
61 if (elem[i] - elem[N - 1] != w.elem[i] - w.elem[N - 1]) {
62 return elem[i] - elem[N - 1] < w.elem[i] - w.elem[N - 1];
68 bool clebsch::weight::operator==(const weight &w) const {
71 for (int i = 1; i < N; ++i) {
72 if (w.elem[i] - w.elem[i - 1] != elem[i] - elem[i - 1]) {
80 clebsch::weight clebsch::weight::operator+(const weight &w) const {
83 transform(elem.begin(), elem.end(), w.elem.begin(), result.elem.begin(), std::plus<int>());
88 int clebsch::weight::index() const {
91 for (int i = 0; elem[i] > elem[N - 1]; ++i) {
92 result += binomial(N - i - 1 + elem[i] - elem[N - 1] - 1, N - i - 1);
98 long long clebsch::weight::dimension() const {
99 long long numerator = 1, denominator = 1;
101 for (int i = 1; i < N; ++i) {
102 for (int j = 0; i + j < N; ++j) {
103 numerator *= elem[j] - elem[i + j] + i;
108 return numerator / denominator;
111 // implementation of "pattern" starts here
113 clebsch::pattern::pattern(const pattern &p) : elem(p.elem), N(p.N) {}
115 clebsch::pattern::pattern(const weight &irrep, int index) :
116 elem((irrep.N * (irrep.N + 1)) / 2), N(irrep.N) {
117 for (int i = 1; i <= N; ++i) {
118 (*this)(i, N) = irrep(i);
121 for (int l = N - 1; l >= 1; --l) {
122 for (int k = 1; k <= l; ++k) {
123 (*this)(k, l) = (*this)(k + 1, l + 1);
127 while (index-- > 0) {
134 int &clebsch::pattern::operator()(int k, int l) {
135 return elem[(N * (N + 1) - l * (l + 1)) / 2 + k - 1];
138 const int &clebsch::pattern::operator()(int k, int l) const {
139 return elem[(N * (N + 1) - l * (l + 1)) / 2 + k - 1];
142 bool clebsch::pattern::operator++() {
145 while (l < N && (*this)(k, l) == (*this)(k, l + 1)) {
157 while (k != 1 || l != 1) {
163 (*this)(k, l) = (*this)(k + 1, l + 1);
169 bool clebsch::pattern::operator--() {
172 while (l < N && (*this)(k, l) == (*this)(k + 1, l + 1)) {
184 while (k != 1 || l != 1) {
190 (*this)(k, l) = (*this)(k, l + 1);
196 int clebsch::pattern::index() const {
199 for (pattern p(*this); --p; ++result) {}
204 clebsch::weight clebsch::pattern::get_weight() const {
205 clebsch::weight result(N);
207 for (int prev = 0, l = 1; l <= N; ++l) {
210 for (int k = 1; k <= l; ++k) {
211 now += (*this)(k, l);
214 result(l) = now - prev;
221 double clebsch::pattern::lowering_coeff(int k, int l) const {
224 for (int i = 1; i <= l + 1; ++i) {
225 result *= (*this)(i, l + 1) - (*this)(k, l) + k - i + 1;
228 for (int i = 1; i <= l - 1; ++i) {
229 result *= (*this)(i, l - 1) - (*this)(k, l) + k - i;
232 for (int i = 1; i <= l; ++i) {
233 if (i == k) continue;
234 result /= (*this)(i, l) - (*this)(k, l) + k - i + 1;
235 result /= (*this)(i, l) - (*this)(k, l) + k - i;
238 return std::sqrt(-result);
241 double clebsch::pattern::raising_coeff(int k, int l) const {
244 for (int i = 1; i <= l + 1; ++i) {
245 result *= (*this)(i, l + 1) - (*this)(k, l) + k - i;
248 for (int i = 1; i <= l - 1; ++i) {
249 result *= (*this)(i, l - 1) - (*this)(k, l) + k - i - 1;
252 for (int i = 1; i <= l; ++i) {
253 if (i == k) continue;
254 result /= (*this)(i, l) - (*this)(k, l) + k - i;
255 result /= (*this)(i, l) - (*this)(k, l) + k - i - 1;
258 return std::sqrt(-result);
261 // implementation of "decomposition" starts here
263 clebsch::decomposition::decomposition(const weight &factor1, const weight &factor2) :
264 N(factor1.N), factor1(factor1), factor2(factor2) {
265 assert(factor1.N == factor2.N);
266 std::vector<clebsch::weight> result;
267 pattern low(factor1), high(factor1);
268 weight trial(factor2);
275 low(k, l) = std::max(high(k + N - l, N), high(k, l + 1) + trial(l + 1) - trial(l));
276 high(k, l) = high(k, l + 1);
277 if (k > 1 && high(k, l) > high(k - 1, l - 1)) {
278 high(k, l) = high(k - 1, l - 1);
280 if (l > 1 && k == l && high(k, l) > trial(l - 1) - trial(l)) {
281 high(k, l) = trial(l - 1) - trial(l);
283 if (low(k, l) > high(k, l)) {
286 trial(l + 1) += high(k, l + 1) - high(k, l);
288 trial(l + 1) += high(k, l + 1);
295 result.push_back(trial);
296 for (int i = 1; i <= N; ++i) {
297 result.back()(i) -= result.back()(N);
303 while (k != 1 || l != N) {
306 trial(l + 1) -= high(k, l + 1);
307 } else if (low(k, l) < high(k, l)) {
312 trial(l + 1) -= high(k, l + 1) - high(k, l);
316 } while (k != 1 || l != N);
318 sort(result.begin(), result.end());
319 for (std::vector<clebsch::weight>::iterator it = result.begin(); it != result.end(); ++it) {
320 if (it != result.begin() && *it == weights.back()) {
321 ++multiplicities.back();
323 weights.push_back(*it);
324 multiplicities.push_back(1);
329 int clebsch::decomposition::size() const {
330 return weights.size();
333 const clebsch::weight &clebsch::decomposition::operator()(int j) const {
337 int clebsch::decomposition::multiplicity(const weight &irrep) const {
338 assert(irrep.N == N);
339 std::vector<clebsch::weight>::const_iterator it
340 = std::lower_bound(weights.begin(), weights.end(), irrep);
342 return it != weights.end() && *it == irrep ? multiplicities[it - weights.begin()] : 0;
345 // implementation of "index_adapter" starts here
347 clebsch::index_adapter::index_adapter(const clebsch::decomposition &decomp) :
349 factor1(decomp.factor1.index()),
350 factor2(decomp.factor2.index()) {
351 for (int i = 0, s = decomp.size(); i < s; ++i) {
352 indices.push_back(decomp(i).index());
353 multiplicities.push_back(decomp.multiplicity(decomp(i)));
357 int clebsch::index_adapter::size() const {
358 return indices.size();
361 int clebsch::index_adapter::operator()(int j) const {
365 int clebsch::index_adapter::multiplicity(int irrep) const {
366 std::vector<int>::const_iterator it = std::lower_bound(indices.begin(), indices.end(), irrep);
368 return it != indices.end() && *it == irrep ? multiplicities[it - indices.begin()] : 0;
371 // implementation of "clebsch" starts here
373 void clebsch::coefficients::set(int factor1_state,
375 int multiplicity_index,
378 assert(0 <= factor1_state && factor1_state < factor1_dimension);
379 assert(0 <= factor2_state && factor2_state < factor2_dimension);
380 assert(0 <= multiplicity_index && multiplicity_index < multiplicity);
381 assert(0 <= irrep_state && irrep_state < irrep_dimension);
383 int coefficient_label[] = { factor1_state,
387 clzx[std::vector<int>(coefficient_label, coefficient_label
388 + sizeof coefficient_label / sizeof coefficient_label[0])] = value;
391 void clebsch::coefficients::highest_weight_normal_form() {
392 int hws = irrep_dimension - 1;
394 // bring CGCs into reduced row echelon form
395 for (int h = 0, i = 0; h < multiplicity - 1 && i < factor1_dimension; ++i) {
396 for (int j = 0; h < multiplicity - 1 && j < factor2_dimension; ++j) {
399 for (int k = h + 1; k < multiplicity; ++k) {
400 if (fabs((*this)(i, j, k, hws)) > fabs((*this)(i, j, k0, hws))) {
405 if ((*this)(i, j, k0, hws) < -EPS) {
406 for (int i2 = i; i2 < factor1_dimension; ++i2) {
407 for (int j2 = i2 == i ? j : 0; j2 < factor2_dimension; ++j2) {
408 set(i2, j2, k0, hws, -(*this)(i2, j2, k0, hws));
411 } else if ((*this)(i, j, k0, hws) < EPS) {
416 for (int i2 = i; i2 < factor1_dimension; ++i2) {
417 for (int j2 = i2 == i ? j : 0; j2 < factor2_dimension; ++j2) {
418 double x = (*this)(i2, j2, k0, hws);
419 set(i2, j2, k0, hws, (*this)(i2, j2, h, hws));
420 set(i2, j2, h, hws, x);
425 for (int k = h + 1; k < multiplicity; ++k) {
426 for (int i2 = i; i2 < factor1_dimension; ++i2) {
427 for (int j2 = i2 == i ? j : 0; j2 < factor2_dimension; ++j2) {
428 set(i2, j2, k, hws, (*this)(i2, j2, k, hws) - (*this)(i2, j2, h, hws)
429 * (*this)(i, j, k, hws) / (*this)(i, j, h, hws));
434 // next 3 lines not strictly necessary, might improve numerical stability
435 for (int k = h + 1; k < multiplicity; ++k) {
436 set(i, j, k, hws, 0.0);
443 // Gram-Schmidt orthonormalization
444 for (int h = 0; h < multiplicity; ++h) {
445 for (int k = 0; k < h; ++k) {
446 double overlap = 0.0;
447 for (int i = 0; i < factor1_dimension; ++i) {
448 for (int j = 0; j < factor2_dimension; ++j) {
449 overlap += (*this)(i, j, h, hws) * (*this)(i, j, k, hws);
453 for (int i = 0; i < factor1_dimension; ++i) {
454 for (int j = 0; j < factor2_dimension; ++j) {
455 set(i, j, h, hws, (*this)(i, j, h, hws) - overlap * (*this)(i, j, k, hws));
461 for (int i = 0; i < factor1_dimension; ++i) {
462 for (int j = 0; j < factor2_dimension; ++j) {
463 norm += (*this)(i, j, h, hws) * (*this)(i, j, h, hws);
466 norm = std::sqrt(norm);
468 for (int i = 0; i < factor1_dimension; ++i) {
469 for (int j = 0; j < factor2_dimension; ++j) {
470 set(i, j, h, hws, (*this)(i, j, h, hws) / norm);
476 void clebsch::coefficients::compute_highest_weight_coeffs() {
477 if (multiplicity == 0) {
481 std::vector<std::vector<int> > map_coeff(factor1_dimension,
482 std::vector<int>(factor2_dimension, -1));
483 std::vector<std::vector<int> > map_states(factor1_dimension,
484 std::vector<int>(factor2_dimension, -1));
485 int n_coeff = 0, n_states = 0;
486 pattern p(factor1, 0);
488 for (int i = 0; i < factor1_dimension; ++i, ++p) {
489 weight pw(p.get_weight());
490 pattern q(factor2, 0);
491 for (int j = 0; j < factor2_dimension; ++j, ++q) {
492 if (pw + q.get_weight() == irrep) {
493 map_coeff[i][j] = n_coeff++;
499 for (int i = 0; i < factor1_dimension; ++i) {
500 for (int j = 0; j < factor2_dimension; ++j) {
501 if (map_coeff[i][j] >= 0) {
502 set(i, j, 0, irrep_dimension - 1, 1.0);
509 double *hw_system = new double[n_coeff * (factor1_dimension * factor2_dimension)];
510 assert(hw_system != NULL);
511 memset(hw_system, 0, n_coeff * (factor1_dimension * factor2_dimension) * sizeof (double));
513 pattern r(factor1, 0);
514 for (int i = 0; i < factor1_dimension; ++i, ++r) {
515 pattern q(factor2, 0);
517 for (int j = 0; j < factor2_dimension; ++j, ++q) {
518 if (map_coeff[i][j] >= 0) {
519 for (int l = 1; l <= N - 1; ++l) {
520 for (int k = 1; k <= l; ++k) {
521 if ((k == 1 || r(k, l) + 1 <= r(k - 1, l - 1)) && r(k, l) + 1 <= r(k, l + 1)) {
526 if (map_states[h][j] < 0) {
527 map_states[h][j] = n_states++;
530 hw_system[n_coeff * map_states[h][j] + map_coeff[i][j]]
531 += r.raising_coeff(k, l);
534 if ((k == 1 || q(k, l) + 1 <= q(k - 1, l - 1)) && q(k, l) + 1 <= q(k, l + 1)) {
539 if (map_states[i][h] < 0) {
540 map_states[i][h] = n_states++;
544 hw_system[n_coeff * map_states[i][h] + map_coeff[i][j]]
545 += q.raising_coeff(k, l);
553 int lwork = -1, info;
556 double *singval = new double[std::min(n_coeff, n_states)];
557 assert(singval != NULL);
558 double *singvec = new double[n_coeff * n_coeff];
559 assert(singvec != NULL);
578 double *work = new double[lwork];
579 assert(work != NULL);
597 for (int i = 0; i < multiplicity; ++i) {
598 for (int j = 0; j < factor1_dimension; ++j) {
599 for (int k = 0; k < factor2_dimension; ++k) {
600 if (map_coeff[j][k] >= 0) {
601 double x = singvec[n_coeff * (n_coeff - 1 - i) + map_coeff[j][k]];
604 set(j, k, i, irrep_dimension - 1, x);
611 // uncomment next line to bring highest-weight coefficients into "normal form"
612 // highest_weight_normal_form();
620 void clebsch::coefficients::compute_lower_weight_coeffs(int multip_index,
622 std::vector<char> &done) {
623 weight statew(pattern(irrep, state).get_weight());
625 std::vector<int> map_parent(irrep_dimension, -1),
626 map_multi(irrep_dimension, -1),
627 which_l(irrep_dimension, -1);
628 int n_parent = 0, n_multi = 0;
630 for (int i = 0; i < irrep_dimension; ++i, ++p) {
631 weight v(p.get_weight());
634 map_multi[i] = n_multi++;
635 } else for (int l = 1; l < N; ++l) {
639 map_parent[i] = n_parent++;
642 compute_lower_weight_coeffs(multip_index, i, done);
651 double *irrep_coeffs = new double[n_parent * n_multi];
652 assert(irrep_coeffs != NULL);
653 memset(irrep_coeffs, 0, n_parent * n_multi * sizeof (double));
655 double *prod_coeffs = new double[n_parent * factor1_dimension * factor2_dimension];
656 assert(prod_coeffs != NULL);
657 memset(prod_coeffs, 0, n_parent * factor1_dimension * factor2_dimension * sizeof (double));
659 std::vector<std::vector<int> > map_prodstat(factor1_dimension,
660 std::vector<int>(factor2_dimension, -1));
664 for (int i = 0; i < irrep_dimension; ++i, ++r) {
665 if (map_parent[i] >= 0) {
666 for (int k = 1, l = which_l[i]; k <= l; ++k) {
667 if (r(k, l) > r(k + 1, l + 1) && (k == l || r(k, l) > r(k, l - 1))) {
672 irrep_coeffs[n_parent * map_multi[h] + map_parent[i]] += r.lowering_coeff(k, l);
676 pattern q1(factor1, 0);
677 for (int j1 = 0; j1 < factor1_dimension; ++j1, ++q1) {
678 pattern q2(factor2, 0);
680 for (int j2 = 0; j2 < factor2_dimension; ++j2, ++q2) {
681 if (std::fabs((*this)(j1, j2, multip_index, i)) > EPS) {
682 for (int k = 1, l = which_l[i]; k <= l; ++k) {
683 if (q1(k, l) > q1(k + 1, l + 1) && (k == l || q1(k, l) > q1(k, l - 1))) {
688 if (map_prodstat[h][j2] < 0) {
689 map_prodstat[h][j2] = n_prodstat++;
692 prod_coeffs[n_parent * map_prodstat[h][j2] + map_parent[i]] +=
693 (*this)(j1, j2, multip_index, i) * q1.lowering_coeff(k, l);
696 if (q2(k, l) > q2(k + 1, l + 1) && (k == l || q2(k, l) > q2(k, l - 1))) {
701 if (map_prodstat[j1][h] < 0) {
702 map_prodstat[j1][h] = n_prodstat++;
705 prod_coeffs[n_parent * map_prodstat[j1][h] + map_parent[i]] +=
706 (*this)(j1, j2, multip_index, i) * q2.lowering_coeff(k, l);
716 int lwork = -1, info;
732 double *work = new double[lwork];
733 assert(work != NULL);
748 for (int i = 0; i < irrep_dimension; ++i) {
749 if (map_multi[i] >= 0) {
750 for (int j = 0; j < factor1_dimension; ++j) {
751 for (int k = 0; k < factor2_dimension; ++k) {
752 if (map_prodstat[j][k] >= 0) {
753 double x = prod_coeffs[n_parent * map_prodstat[j][k] + map_multi[i]];
756 set(j, k, multip_index, i, x);
767 delete[] prod_coeffs;
768 delete[] irrep_coeffs;
771 clebsch::coefficients::coefficients(const weight &irrep, const weight &factor1, const weight &factor2) :
776 factor1_dimension(factor1.dimension()),
777 factor2_dimension(factor2.dimension()),
778 irrep_dimension(irrep.dimension()),
779 multiplicity(decomposition(factor1, factor2).multiplicity(irrep)) {
780 assert(factor1.N == irrep.N);
781 assert(factor2.N == irrep.N);
783 compute_highest_weight_coeffs();
785 for (int i = 0; i < multiplicity; ++i) {
786 std::vector<char> done(irrep_dimension, 0);
787 done[irrep_dimension - 1] = true;
788 for (int j = irrep_dimension - 1; j >= 0; --j) {
790 compute_lower_weight_coeffs(i, j, done);
796 double clebsch::coefficients::operator()(int factor1_state,
798 int multiplicity_index,
799 int irrep_state) const {
801 assert(0 <= factor1_state && factor1_state < factor1_dimension);
802 assert(0 <= factor2_state && factor2_state < factor2_dimension);
803 assert(0 <= multiplicity_index && multiplicity_index < multiplicity);
804 assert(0 <= irrep_state && irrep_state < irrep_dimension);
806 int coefficient_label[] = { factor1_state,
810 std::map<std::vector<int>, double>::const_iterator it(
811 clzx.find(std::vector<int>(coefficient_label, coefficient_label
812 + sizeof coefficient_label / sizeof coefficient_label[0])));
814 return it != clzx.end() ? it->second : 0.0;