GiNaC 1.8.10
matrix.cpp
Go to the documentation of this file.
1
5/*
6 * GiNaC Copyright (C) 1999-2026 Johannes Gutenberg University Mainz, Germany
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "matrix.h"
23#include "numeric.h"
24#include "lst.h"
25#include "idx.h"
26#include "indexed.h"
27#include "add.h"
28#include "power.h"
29#include "symbol.h"
30#include "operators.h"
31#include "normal.h"
32#include "archive.h"
33#include "utils.h"
34
35#include <map>
36#include <sstream>
37#include <stdexcept>
38#include <string>
39
40namespace GiNaC {
41
44 print_func<print_latex>(&matrix::do_print_latex).
45 print_func<print_tree>(&matrix::do_print_tree).
46 print_func<print_python_repr>(&matrix::do_print_python_repr))
47
48
49// default constructor
51
53matrix::matrix() : row(1), col(1), m(1, _ex0)
54{
56}
57
59// other constructors
61
62// public
63
68matrix::matrix(unsigned r, unsigned c) : row(r), col(c), m(r*c, _ex0)
69{
71}
72
77matrix::matrix(unsigned r, unsigned c, const lst & l)
78 : row(r), col(c), m(r*c, _ex0)
79{
81
82 size_t i = 0;
83 for (auto & it : l) {
84 size_t x = i % c;
85 size_t y = i / c;
86 if (y >= r)
87 break; // matrix smaller than list: throw away excessive elements
88 m[y*c+x] = it;
89 ++i;
90 }
91}
92
96matrix::matrix(std::initializer_list<std::initializer_list<ex>> l)
97 : row(l.size()), col(l.begin()->size())
98{
100
101 m.reserve(row*col);
102 for (const auto & r : l) {
103 unsigned c = 0;
104 for (const auto & e : r) {
105 m.push_back(e);
106 ++c;
107 }
108 if (c != col)
109 throw std::invalid_argument("matrix::matrix{{}}: wrong dimension");
110 }
111}
112
113// protected
114
116matrix::matrix(unsigned r, unsigned c, const exvector & m2)
117 : row(r), col(c), m(m2)
118{
120}
121matrix::matrix(unsigned r, unsigned c, exvector && m2)
122 : row(r), col(c), m(std::move(m2))
123{
125}
126
128// archiving
130
132{
133 inherited::read_archive(n, sym_lst);
134
135 if (!(n.find_unsigned("row", row)) || !(n.find_unsigned("col", col)))
136 throw (std::runtime_error("unknown matrix dimensions in archive"));
137 m.reserve(row * col);
138 // XXX: default ctor inserts a zero element, we need to erase it here.
139 m.pop_back();
140 auto range = n.find_property_range("m", "m");
141 for (auto i=range.begin; i != range.end; ++i) {
142 ex e;
143 n.find_ex_by_loc(i, e, sym_lst);
144 m.emplace_back(e);
145 }
146}
148
150{
151 inherited::archive(n);
152 n.add_unsigned("row", row);
153 n.add_unsigned("col", col);
154 for (auto & i : m) {
155 n.add_ex("m", i);
156 }
157}
158
160// functions overriding virtual functions from base classes
162
163// public
164
165void matrix::print_elements(const print_context & c, const char *row_start, const char *row_end, const char *row_sep, const char *col_sep) const
166{
167 for (unsigned ro=0; ro<row; ++ro) {
168 c.s << row_start;
169 for (unsigned co=0; co<col; ++co) {
170 m[ro*col+co].print(c);
171 if (co < col-1)
172 c.s << col_sep;
173 else
174 c.s << row_end;
175 }
176 if (ro < row-1)
177 c.s << row_sep;
178 }
179}
180
181void matrix::do_print(const print_context & c, unsigned level) const
182{
183 c.s << "[";
184 print_elements(c, "[", "]", ",", ",");
185 c.s << "]";
186}
187
188void matrix::do_print_latex(const print_latex & c, unsigned level) const
189{
190 c.s << "\\left(\\begin{array}{" << std::string(col,'c') << "}";
191 print_elements(c, "", "", "\\\\", "&");
192 c.s << "\\end{array}\\right)";
193}
194
195void matrix::do_print_python_repr(const print_python_repr & c, unsigned level) const
196{
197 c.s << class_name() << '(';
198 print_elements(c, "[", "]", ",", ",");
199 c.s << ')';
200}
201
203size_t matrix::nops() const
204{
205 return static_cast<size_t>(row) * static_cast<size_t>(col);
206}
207
209ex matrix::op(size_t i) const
210{
211 GINAC_ASSERT(i<nops());
212
213 return m[i];
214}
215
218{
219 GINAC_ASSERT(i<nops());
220
222 return m[i];
223}
224
225ex matrix::subs(const exmap & mp, unsigned options) const
226{
227 exvector m2(row * col);
228 for (unsigned r=0; r<row; ++r)
229 for (unsigned c=0; c<col; ++c)
230 m2[r*col+c] = m[r*col+c].subs(mp, options);
231
232 return matrix(row, col, std::move(m2)).subs_one_level(mp, options);
233}
234
237{
238 std::unique_ptr<exvector> ev(nullptr);
239 for (auto i=m.begin(); i!=m.end(); ++i) {
240 ex x = i->conjugate();
241 if (ev) {
242 ev->push_back(x);
243 continue;
244 }
245 if (are_ex_trivially_equal(x, *i)) {
246 continue;
247 }
248 ev.reset(new exvector);
249 ev->reserve(m.size());
250 for (auto j=m.begin(); j!=i; ++j) {
251 ev->push_back(*j);
252 }
253 ev->push_back(x);
254 }
255 if (ev) {
256 return matrix(row, col, std::move(*ev));
257 }
258 return *this;
259}
260
262{
263 exvector v;
264 v.reserve(m.size());
265 for (auto & i : m)
266 v.push_back(i.real_part());
267 return matrix(row, col, std::move(v));
268}
269
271{
272 exvector v;
273 v.reserve(m.size());
274 for (auto & i : m)
275 v.push_back(i.imag_part());
276 return matrix(row, col, std::move(v));
277}
278
279// protected
280
281int matrix::compare_same_type(const basic & other) const
282{
283 GINAC_ASSERT(is_exactly_a<matrix>(other));
284 const matrix &o = static_cast<const matrix &>(other);
285
286 // compare number of rows
287 if (row != o.rows())
288 return row < o.rows() ? -1 : 1;
289
290 // compare number of columns
291 if (col != o.cols())
292 return col < o.cols() ? -1 : 1;
293
294 // equal number of rows and columns, compare individual elements
295 int cmpval;
296 for (unsigned r=0; r<row; ++r) {
297 for (unsigned c=0; c<col; ++c) {
298 cmpval = ((*this)(r,c)).compare(o(r,c));
299 if (cmpval!=0) return cmpval;
300 }
301 }
302 // all elements are equal => matrices are equal;
303 return 0;
304}
305
306bool matrix::match_same_type(const basic & other) const
307{
308 GINAC_ASSERT(is_exactly_a<matrix>(other));
309 const matrix & o = static_cast<const matrix &>(other);
310
311 // The number of rows and columns must be the same. This is necessary to
312 // prevent a 2x3 matrix from matching a 3x2 one.
313 return row == o.rows() && col == o.cols();
314}
315
318{
319 GINAC_ASSERT(is_a<indexed>(i));
320 GINAC_ASSERT(is_a<matrix>(i.op(0)));
321
322 bool all_indices_unsigned = static_cast<const indexed &>(i).all_index_values_are(info_flags::nonnegint);
323
324 // Check indices
325 if (i.nops() == 2) {
326
327 // One index, must be one-dimensional vector
328 if (row != 1 && col != 1)
329 throw (std::runtime_error("matrix::eval_indexed(): vector must have exactly 1 index"));
330
331 const idx & i1 = ex_to<idx>(i.op(1));
332
333 if (col == 1) {
334
335 // Column vector
336 if (!i1.get_dim().is_equal(row))
337 throw (std::runtime_error("matrix::eval_indexed(): dimension of index must match number of vector elements"));
338
339 // Index numeric -> return vector element
340 if (all_indices_unsigned) {
341 unsigned n1 = ex_to<numeric>(i1.get_value()).to_int();
342 if (n1 >= row)
343 throw (std::runtime_error("matrix::eval_indexed(): value of index exceeds number of vector elements"));
344 return (*this)(n1, 0);
345 }
346
347 } else {
348
349 // Row vector
350 if (!i1.get_dim().is_equal(col))
351 throw (std::runtime_error("matrix::eval_indexed(): dimension of index must match number of vector elements"));
352
353 // Index numeric -> return vector element
354 if (all_indices_unsigned) {
355 unsigned n1 = ex_to<numeric>(i1.get_value()).to_int();
356 if (n1 >= col)
357 throw (std::runtime_error("matrix::eval_indexed(): value of index exceeds number of vector elements"));
358 return (*this)(0, n1);
359 }
360 }
361
362 } else if (i.nops() == 3) {
363
364 // Two indices
365 const idx & i1 = ex_to<idx>(i.op(1));
366 const idx & i2 = ex_to<idx>(i.op(2));
367
368 if (!i1.get_dim().is_equal(row))
369 throw (std::runtime_error("matrix::eval_indexed(): dimension of first index must match number of rows"));
370 if (!i2.get_dim().is_equal(col))
371 throw (std::runtime_error("matrix::eval_indexed(): dimension of second index must match number of columns"));
372
373 // Pair of dummy indices -> compute trace
374 if (is_dummy_pair(i1, i2))
375 return trace();
376
377 // Both indices numeric -> return matrix element
378 if (all_indices_unsigned) {
379 unsigned n1 = ex_to<numeric>(i1.get_value()).to_int(), n2 = ex_to<numeric>(i2.get_value()).to_int();
380 if (n1 >= row)
381 throw (std::runtime_error("matrix::eval_indexed(): value of first index exceeds number of rows"));
382 if (n2 >= col)
383 throw (std::runtime_error("matrix::eval_indexed(): value of second index exceeds number of columns"));
384 return (*this)(n1, n2);
385 }
386
387 } else
388 throw (std::runtime_error("matrix::eval_indexed(): matrix must have exactly 2 indices"));
389
390 return i.hold();
391}
392
394ex matrix::add_indexed(const ex & self, const ex & other) const
395{
396 GINAC_ASSERT(is_a<indexed>(self));
397 GINAC_ASSERT(is_a<matrix>(self.op(0)));
398 GINAC_ASSERT(is_a<indexed>(other));
399 GINAC_ASSERT(self.nops() == 2 || self.nops() == 3);
400
401 // Only add two matrices
402 if (is_a<matrix>(other.op(0))) {
403 GINAC_ASSERT(other.nops() == 2 || other.nops() == 3);
404
405 const matrix &self_matrix = ex_to<matrix>(self.op(0));
406 const matrix &other_matrix = ex_to<matrix>(other.op(0));
407
408 if (self.nops() == 2 && other.nops() == 2) { // vector + vector
409
410 if (self_matrix.row == other_matrix.row)
411 return indexed(self_matrix.add(other_matrix), self.op(1));
412 else if (self_matrix.row == other_matrix.col)
413 return indexed(self_matrix.add(other_matrix.transpose()), self.op(1));
414
415 } else if (self.nops() == 3 && other.nops() == 3) { // matrix + matrix
416
417 if (self.op(1).is_equal(other.op(1)) && self.op(2).is_equal(other.op(2)))
418 return indexed(self_matrix.add(other_matrix), self.op(1), self.op(2));
419 else if (self.op(1).is_equal(other.op(2)) && self.op(2).is_equal(other.op(1)))
420 return indexed(self_matrix.add(other_matrix.transpose()), self.op(1), self.op(2));
421
422 }
423 }
424
425 // Don't know what to do, return unevaluated sum
426 return self + other;
427}
428
430ex matrix::scalar_mul_indexed(const ex & self, const numeric & other) const
431{
432 GINAC_ASSERT(is_a<indexed>(self));
433 GINAC_ASSERT(is_a<matrix>(self.op(0)));
434 GINAC_ASSERT(self.nops() == 2 || self.nops() == 3);
435
436 const matrix &self_matrix = ex_to<matrix>(self.op(0));
437
438 if (self.nops() == 2)
439 return indexed(self_matrix.mul(other), self.op(1));
440 else // self.nops() == 3
441 return indexed(self_matrix.mul(other), self.op(1), self.op(2));
442}
443
445bool matrix::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const
446{
447 GINAC_ASSERT(is_a<indexed>(*self));
448 GINAC_ASSERT(is_a<indexed>(*other));
449 GINAC_ASSERT(self->nops() == 2 || self->nops() == 3);
450 GINAC_ASSERT(is_a<matrix>(self->op(0)));
451
452 // Only contract with other matrices
453 if (!is_a<matrix>(other->op(0)))
454 return false;
455
456 GINAC_ASSERT(other->nops() == 2 || other->nops() == 3);
457
458 const matrix &self_matrix = ex_to<matrix>(self->op(0));
459 const matrix &other_matrix = ex_to<matrix>(other->op(0));
460
461 if (self->nops() == 2) {
462
463 if (other->nops() == 2) { // vector * vector (scalar product)
464
465 if (self_matrix.col == 1) {
466 if (other_matrix.col == 1) {
467 // Column vector * column vector, transpose first vector
468 *self = self_matrix.transpose().mul(other_matrix)(0, 0);
469 } else {
470 // Column vector * row vector, swap factors
471 *self = other_matrix.mul(self_matrix)(0, 0);
472 }
473 } else {
474 if (other_matrix.col == 1) {
475 // Row vector * column vector, perfect
476 *self = self_matrix.mul(other_matrix)(0, 0);
477 } else {
478 // Row vector * row vector, transpose second vector
479 *self = self_matrix.mul(other_matrix.transpose())(0, 0);
480 }
481 }
482 *other = _ex1;
483 return true;
484
485 } else { // vector * matrix
486
487 // B_i * A_ij = (B*A)_j (B is row vector)
488 if (is_dummy_pair(self->op(1), other->op(1))) {
489 if (self_matrix.row == 1)
490 *self = indexed(self_matrix.mul(other_matrix), other->op(2));
491 else
492 *self = indexed(self_matrix.transpose().mul(other_matrix), other->op(2));
493 *other = _ex1;
494 return true;
495 }
496
497 // B_j * A_ij = (A*B)_i (B is column vector)
498 if (is_dummy_pair(self->op(1), other->op(2))) {
499 if (self_matrix.col == 1)
500 *self = indexed(other_matrix.mul(self_matrix), other->op(1));
501 else
502 *self = indexed(other_matrix.mul(self_matrix.transpose()), other->op(1));
503 *other = _ex1;
504 return true;
505 }
506 }
507
508 } else if (other->nops() == 3) { // matrix * matrix
509
510 // A_ij * B_jk = (A*B)_ik
511 if (is_dummy_pair(self->op(2), other->op(1))) {
512 *self = indexed(self_matrix.mul(other_matrix), self->op(1), other->op(2));
513 *other = _ex1;
514 return true;
515 }
516
517 // A_ij * B_kj = (A*Btrans)_ik
518 if (is_dummy_pair(self->op(2), other->op(2))) {
519 *self = indexed(self_matrix.mul(other_matrix.transpose()), self->op(1), other->op(1));
520 *other = _ex1;
521 return true;
522 }
523
524 // A_ji * B_jk = (Atrans*B)_ik
525 if (is_dummy_pair(self->op(1), other->op(1))) {
526 *self = indexed(self_matrix.transpose().mul(other_matrix), self->op(2), other->op(2));
527 *other = _ex1;
528 return true;
529 }
530
531 // A_ji * B_kj = (B*A)_ki
532 if (is_dummy_pair(self->op(1), other->op(2))) {
533 *self = indexed(other_matrix.mul(self_matrix), other->op(1), self->op(2));
534 *other = _ex1;
535 return true;
536 }
537 }
538
539 return false;
540}
541
542
544// non-virtual functions in this class
546
547// public
548
552matrix matrix::add(const matrix & other) const
553{
554 if (col != other.col || row != other.row)
555 throw std::logic_error("matrix::add(): incompatible matrices");
556
557 exvector sum(this->m);
558 auto ci = other.m.begin();
559 for (auto & i : sum)
560 i += *ci++;
561
562 return matrix(row, col, std::move(sum));
563}
564
565
569matrix matrix::sub(const matrix & other) const
570{
571 if (col != other.col || row != other.row)
572 throw std::logic_error("matrix::sub(): incompatible matrices");
573
574 exvector dif(this->m);
575 auto ci = other.m.begin();
576 for (auto & i : dif)
577 i -= *ci++;
578
579 return matrix(row, col, std::move(dif));
580}
581
582
586matrix matrix::mul(const matrix & other) const
587{
588 if (this->cols() != other.rows())
589 throw std::logic_error("matrix::mul(): incompatible matrices");
590
591 exvector prod(this->rows()*other.cols());
592
593 for (unsigned r1=0; r1<this->rows(); ++r1) {
594 for (unsigned c=0; c<this->cols(); ++c) {
595 // Quick test: can we shortcut?
596 if (m[r1*col+c].is_zero())
597 continue;
598 for (unsigned r2=0; r2<other.cols(); ++r2)
599 prod[r1*other.col+r2] += (m[r1*col+c] * other.m[c*other.col+r2]);
600 }
601 }
602 return matrix(row, other.col, std::move(prod));
603}
604
605
607matrix matrix::mul(const numeric & other) const
608{
609 exvector prod(row * col);
610
611 for (unsigned r=0; r<row; ++r)
612 for (unsigned c=0; c<col; ++c)
613 prod[r*col+c] = m[r*col+c] * other;
614
615 return matrix(row, col, std::move(prod));
616}
617
618
620matrix matrix::mul_scalar(const ex & other) const
621{
623 throw std::runtime_error("matrix::mul_scalar(): non-commutative scalar");
624
625 exvector prod(row * col);
626
627 for (unsigned r=0; r<row; ++r)
628 for (unsigned c=0; c<col; ++c)
629 prod[r*col+c] = m[r*col+c] * other;
630
631 return matrix(row, col, std::move(prod));
632}
633
634
636matrix matrix::pow(const ex & expn) const
637{
638 if (col!=row)
639 throw (std::logic_error("matrix::pow(): matrix not square"));
640
641 if (is_exactly_a<numeric>(expn)) {
642 // Integer cases are computed by successive multiplication, using the
643 // obvious shortcut of storing temporaries, like A^4 == (A*A)*(A*A).
644 if (expn.info(info_flags::integer)) {
645 numeric b = ex_to<numeric>(expn);
646 matrix A(row,col);
647 if (expn.info(info_flags::negative)) {
648 b *= -1;
649 A = this->inverse();
650 } else {
651 A = *this;
652 }
653 matrix C(row,col);
654 for (unsigned r=0; r<row; ++r)
655 C(r,r) = _ex1;
656 if (b.is_zero())
657 return C;
658 // This loop computes the representation of b in base 2 from right
659 // to left and multiplies the factors whenever needed. Note
660 // that this is not entirely optimal but close to optimal and
661 // "better" algorithms are much harder to implement. (See Knuth,
662 // TAoCP2, section "Evaluation of Powers" for a good discussion.)
663 while (b!=*_num1_p) {
664 if (b.is_odd()) {
665 C = C.mul(A);
666 --b;
667 }
668 b /= *_num2_p; // still integer.
669 A = A.mul(A);
670 }
671 return A.mul(C);
672 }
673 }
674 throw (std::runtime_error("matrix::pow(): don't know how to handle exponent"));
675}
676
677
683const ex & matrix::operator() (unsigned ro, unsigned co) const
684{
685 if (ro>=row || co>=col)
686 throw (std::range_error("matrix::operator(): index out of range"));
687
688 return m[ro*col+co];
689}
690
691
697ex & matrix::operator() (unsigned ro, unsigned co)
698{
699 if (ro>=row || co>=col)
700 throw (std::range_error("matrix::operator(): index out of range"));
701
703 return m[ro*col+co];
704}
705
706
710{
711 exvector trans(this->cols()*this->rows());
712
713 for (unsigned r=0; r<this->cols(); ++r)
714 for (unsigned c=0; c<this->rows(); ++c)
715 trans[r*this->rows()+c] = m[c*this->cols()+r];
716
717 return matrix(this->cols(), this->rows(), std::move(trans));
718}
719
734ex matrix::determinant(unsigned algo) const
735{
736 if (row!=col)
737 throw (std::logic_error("matrix::determinant(): matrix not square"));
738 GINAC_ASSERT(row*col==m.capacity());
739
740 // Gather some statistical information about this matrix:
741 bool numeric_flag = true;
742 bool normal_flag = false;
743 unsigned sparse_count = 0; // counts non-zero elements
744 for (auto r : m) {
745 if (!r.info(info_flags::numeric))
746 numeric_flag = false;
747 exmap srl; // symbol replacement list
748 ex rtest = r.to_rational(srl);
749 if (!rtest.is_zero())
750 ++sparse_count;
753 normal_flag = true;
754 }
755
756 // Here is the heuristics in case this routine has to decide:
757 if (algo == determinant_algo::automatic) {
758 // Minor expansion is generally a good guess:
760 // Does anybody know when a matrix is really sparse?
761 // Maybe <~row/2.236 nonzero elements average in a row?
762 if (row>3 && 5*sparse_count<=row*col)
764 // Purely numeric matrix can be handled by Gauss elimination.
765 // This overrides any prior decisions.
766 if (numeric_flag)
768 }
769
770 // Trap the trivial case here, since some algorithms don't like it
771 if (this->row==1) {
772 // for consistency with non-trivial determinants...
773 if (normal_flag)
774 return m[0].normal();
775 else
776 return m[0].expand();
777 }
778
779 // Compute the determinant
780 switch(algo) {
782 ex det = 1;
783 matrix tmp(*this);
784 int sign = tmp.gauss_elimination(true);
785 for (unsigned d=0; d<row; ++d)
786 det *= tmp.m[d*col+d];
787 if (normal_flag)
788 return (sign*det).normal();
789 else
790 return (sign*det).normal().expand();
791 }
793 matrix tmp(*this);
794 int sign;
795 sign = tmp.fraction_free_elimination(true);
796 if (normal_flag)
797 return (sign*tmp.m[row*col-1]).normal();
798 else
799 return (sign*tmp.m[row*col-1]).expand();
800 }
802 matrix tmp(*this);
803 int sign;
804 sign = tmp.division_free_elimination(true);
805 if (sign==0)
806 return _ex0;
807 ex det = tmp.m[row*col-1];
808 // factor out accumulated bogus slag
809 for (unsigned d=0; d<row-2; ++d)
810 for (unsigned j=0; j<row-d-2; ++j)
811 det = (det/tmp.m[d*col+d]).normal();
812 return (sign*det);
813 }
815 default: {
816 // This is the minor expansion scheme. We always develop such
817 // that the smallest minors (i.e, the trivial 1x1 ones) are on the
818 // rightmost column. For this to be efficient, empirical tests
819 // have shown that the emptiest columns (i.e. the ones with most
820 // zeros) should be the ones on the right hand side -- although
821 // this might seem counter-intuitive (and in contradiction to some
822 // literature like the FORM manual). Please go ahead and test it
823 // if you don't believe me! Therefore we presort the columns of
824 // the matrix:
825 typedef std::pair<unsigned,unsigned> uintpair;
826 std::vector<uintpair> c_zeros; // number of zeros in column
827 for (unsigned c=0; c<col; ++c) {
828 unsigned acc = 0;
829 for (unsigned r=0; r<row; ++r)
830 if (m[r*col+c].is_zero())
831 ++acc;
832 c_zeros.push_back(uintpair(acc,c));
833 }
834 std::sort(c_zeros.begin(),c_zeros.end());
835 std::vector<unsigned> pre_sort;
836 for (auto & i : c_zeros)
837 pre_sort.push_back(i.second);
838 std::vector<unsigned> pre_sort_test(pre_sort); // permutation_sign() modifies the vector so we make a copy here
839 int sign = permutation_sign(pre_sort_test.begin(), pre_sort_test.end());
840 exvector result(row*col); // represents sorted matrix
841 unsigned c = 0;
842 for (auto & it : pre_sort) {
843 for (unsigned r=0; r<row; ++r)
844 result[r*col+c] = m[r*col+it];
845 ++c;
846 }
847
848 if (normal_flag)
849 return (sign*matrix(row, col, std::move(result)).determinant_minor()).normal();
850 else
851 return sign*matrix(row, col, std::move(result)).determinant_minor();
852 }
853 }
854}
855
856
864{
865 if (row != col)
866 throw (std::logic_error("matrix::trace(): matrix not square"));
867
868 ex tr;
869 for (unsigned r=0; r<col; ++r)
870 tr += m[r*col+r];
871
874 return tr.normal();
875 else
876 return tr.expand();
877}
878
879
891ex matrix::charpoly(const ex & lambda) const
892{
893 if (row != col)
894 throw (std::logic_error("matrix::charpoly(): matrix not square"));
895
896 bool numeric_flag = true;
897 for (auto & r : m) {
898 if (!r.info(info_flags::numeric)) {
899 numeric_flag = false;
900 break;
901 }
902 }
903
904 // The pure numeric case is traditionally rather common. Hence, it is
905 // trapped and we use Leverrier's algorithm which goes as row^3 for
906 // every coefficient. The expensive part is the matrix multiplication.
907 if (numeric_flag) {
908
909 matrix B(*this);
910 ex c = B.trace();
911 ex poly = power(lambda, row) - c*power(lambda, row-1);
912 for (unsigned i=1; i<row; ++i) {
913 for (unsigned j=0; j<row; ++j)
914 B.m[j*col+j] -= c;
915 B = this->mul(B);
916 c = B.trace() / ex(i+1);
917 poly -= c*power(lambda, row-i-1);
918 }
919 if (row%2)
920 return -poly;
921 else
922 return poly;
923
924 } else {
925
926 matrix M(*this);
927 for (unsigned r=0; r<col; ++r)
928 M.m[r*col+r] -= lambda;
929
930 return M.determinant().collect(lambda);
931 }
932}
933
934
940
947matrix matrix::inverse(unsigned algo) const
948{
949 if (row != col)
950 throw (std::logic_error("matrix::inverse(): matrix not square"));
951
952 // This routine actually doesn't do anything fancy at all. We compute the
953 // inverse of the matrix A by solving the system A * A^{-1} == Id.
954
955 // First populate the identity matrix supposed to become the right hand side.
956 matrix identity(row,col);
957 for (unsigned i=0; i<row; ++i)
958 identity(i,i) = _ex1;
959
960 // Populate a dummy matrix of variables, just because of compatibility with
961 // matrix::solve() which wants this (for compatibility with under-determined
962 // systems of equations).
963 matrix vars(row,col);
964 for (unsigned r=0; r<row; ++r)
965 for (unsigned c=0; c<col; ++c)
966 vars(r,c) = symbol();
967
968 matrix sol(row,col);
969 try {
970 sol = this->solve(vars, identity, algo);
971 } catch (const std::runtime_error & e) {
972 if (e.what()==std::string("matrix::solve(): inconsistent linear system"))
973 throw (std::runtime_error("matrix::inverse(): singular matrix"));
974 else
975 throw;
976 }
977 return sol;
978}
979
980
993 const matrix & rhs,
994 unsigned algo) const
995{
996 const unsigned m = this->rows();
997 const unsigned n = this->cols();
998 const unsigned p = rhs.cols();
999
1000 // syntax checks
1001 if ((rhs.rows() != m) || (vars.rows() != n) || (vars.cols() != p))
1002 throw (std::logic_error("matrix::solve(): incompatible matrices"));
1003 for (unsigned ro=0; ro<n; ++ro)
1004 for (unsigned co=0; co<p; ++co)
1005 if (!vars(ro,co).info(info_flags::symbol))
1006 throw (std::invalid_argument("matrix::solve(): 1st argument must be matrix of symbols"));
1007
1008 // build the augmented matrix of *this with rhs attached to the right
1009 matrix aug(m,n+p);
1010 for (unsigned r=0; r<m; ++r) {
1011 for (unsigned c=0; c<n; ++c)
1012 aug.m[r*(n+p)+c] = this->m[r*n+c];
1013 for (unsigned c=0; c<p; ++c)
1014 aug.m[r*(n+p)+c+n] = rhs.m[r*p+c];
1015 }
1016
1017 // Eliminate the augmented matrix:
1018 auto colid = aug.echelon_form(algo, n);
1019
1020 // assemble the solution matrix:
1021 matrix sol(n,p);
1022 for (unsigned co=0; co<p; ++co) {
1023 unsigned last_assigned_sol = n+1;
1024 for (int r=m-1; r>=0; --r) {
1025 unsigned fnz = 1; // first non-zero in row
1026 while ((fnz<=n) && (aug.m[r*(n+p)+(fnz-1)].normal().is_zero()))
1027 ++fnz;
1028 if (fnz>n) {
1029 // row consists only of zeros, corresponding rhs must be 0, too
1030 if (!aug.m[r*(n+p)+n+co].normal().is_zero()) {
1031 throw (std::runtime_error("matrix::solve(): inconsistent linear system"));
1032 }
1033 } else {
1034 // assign solutions for vars between fnz+1 and
1035 // last_assigned_sol-1: free parameters
1036 for (unsigned c=fnz; c<last_assigned_sol-1; ++c)
1037 sol(colid[c],co) = vars.m[colid[c]*p+co];
1038 ex e = aug.m[r*(n+p)+n+co];
1039 for (unsigned c=fnz; c<n; ++c)
1040 e -= aug.m[r*(n+p)+c]*sol.m[colid[c]*p+co];
1041 sol(colid[fnz-1],co) = (e/(aug.m[r*(n+p)+fnz-1])).normal();
1042 last_assigned_sol = fnz;
1043 }
1044 }
1045 // assign solutions for vars between 1 and
1046 // last_assigned_sol-1: free parameters
1047 for (unsigned ro=0; ro<last_assigned_sol-1; ++ro)
1048 sol(colid[ro],co) = vars(colid[ro],co);
1049 }
1050
1051 return sol;
1052}
1053
1055unsigned matrix::rank() const
1056{
1058}
1059
1062unsigned matrix::rank(unsigned solve_algo) const
1063{
1064 // Method:
1065 // Transform this matrix into upper echelon form and then count the
1066 // number of non-zero rows.
1067 GINAC_ASSERT(row*col==m.capacity());
1068
1069 matrix to_eliminate = *this;
1070 to_eliminate.echelon_form(solve_algo, col);
1071
1072 unsigned r = row*col; // index of last non-zero element
1073 while (r--) {
1074 if (!to_eliminate.m[r].is_zero())
1075 return 1+r/col;
1076 }
1077 return 0;
1078}
1079
1080
1081// protected
1082
1094{
1095 const unsigned n = this->cols();
1096
1097 // This algorithm can best be understood by looking at a naive
1098 // implementation of Laplace-expansion, like this one:
1099 // ex det;
1100 // matrix minorM(this->rows()-1,this->cols()-1);
1101 // for (unsigned r1=0; r1<this->rows(); ++r1) {
1102 // // shortcut if element(r1,0) vanishes
1103 // if (m[r1*col].is_zero())
1104 // continue;
1105 // // assemble the minor matrix
1106 // for (unsigned r=0; r<minorM.rows(); ++r) {
1107 // for (unsigned c=0; c<minorM.cols(); ++c) {
1108 // if (r<r1)
1109 // minorM(r,c) = m[r*col+c+1];
1110 // else
1111 // minorM(r,c) = m[(r+1)*col+c+1];
1112 // }
1113 // }
1114 // // recurse down and care for sign:
1115 // if (r1%2)
1116 // det -= m[r1*col] * minorM.determinant_minor();
1117 // else
1118 // det += m[r1*col] * minorM.determinant_minor();
1119 // }
1120 // return det.expand();
1121 // What happens is that while proceeding down many of the minors are
1122 // computed more than once. In particular, there are binomial(n,k)
1123 // kxk minors and each one is computed factorial(n-k) times. Therefore
1124 // it is reasonable to store the results of the minors. We proceed from
1125 // right to left. At each column c we only need to retrieve the minors
1126 // calculated in step c-1. We therefore only have to store at most
1127 // 2*binomial(n,n/2) minors.
1128
1129 // we store the minors in maps, keyed by the rows they arise from
1130 typedef std::vector<unsigned> keyseq;
1131 typedef std::map<keyseq, ex> Rmap;
1132
1133 Rmap M, N; // minors used in current and next column, respectively
1134 // populate M with dummy unit, to be used as factor in rightmost column
1135 M[keyseq{}] = _ex1;
1136
1137 // keys to identify minor of M and N (Mkey is a subsequence of Nkey)
1138 keyseq Mkey, Nkey;
1139 Mkey.reserve(n-1);
1140 Nkey.reserve(n);
1141
1142 ex det;
1143 // proceed from right to left through matrix
1144 for (int c=n-1; c>=0; --c) {
1145 Nkey.clear();
1146 Mkey.clear();
1147 for (unsigned i=0; i<n-c; ++i)
1148 Nkey.push_back(i);
1149 unsigned fc = 0; // controls logic for minor key generator
1150 do {
1151 det = _ex0;
1152 for (unsigned r=0; r<n-c; ++r) {
1153 // maybe there is nothing to do?
1154 if (m[Nkey[r]*n+c].is_zero())
1155 continue;
1156 // Mkey is same as Nkey, but with element r removed
1157 Mkey.clear();
1158 Mkey.insert(Mkey.begin(), Nkey.begin(), Nkey.begin() + r);
1159 Mkey.insert(Mkey.end(), Nkey.begin() + r + 1, Nkey.end());
1160 // add product of matrix element and minor M to determinant
1161 if (r%2)
1162 det -= m[Nkey[r]*n+c]*M[Mkey];
1163 else
1164 det += m[Nkey[r]*n+c]*M[Mkey];
1165 }
1166 // prevent nested expressions to save time
1167 det = det.expand();
1168 // if the next computed minor is zero, don't store it in N:
1169 // (if key is not found, operator[] will just return a zero ex)
1170 if (!det.is_zero())
1171 N[Nkey] = det;
1172 // compute next minor key
1173 for (fc=n-c; fc>0; --fc) {
1174 ++Nkey[fc-1];
1175 if (Nkey[fc-1]<fc+c)
1176 break;
1177 }
1178 if (fc<n-c && fc>0)
1179 for (unsigned j=fc; j<n-c; ++j)
1180 Nkey[j] = Nkey[j-1]+1;
1181 } while(fc);
1182 // if N contains no minors, then they all vanished
1183 if (N.empty())
1184 return _ex0;
1185
1186 // proceed to next column: switch roles of M and N, clear N
1187 M = std::move(N);
1188 }
1189
1190 return det;
1191}
1192
1193std::vector<unsigned>
1194matrix::echelon_form(unsigned algo, int n)
1195{
1196 // Here is the heuristics in case this routine has to decide:
1197 if (algo == solve_algo::automatic) {
1198 // Gather some statistical information about the augmented matrix:
1199 bool numeric_flag = true;
1200 for (const auto & r : m) {
1201 if (!r.info(info_flags::numeric)) {
1202 numeric_flag = false;
1203 break;
1204 }
1205 }
1206 unsigned density = 0;
1207 for (const auto & r : m) {
1208 density += !r.is_zero();
1209 }
1210 unsigned ncells = col*row;
1211 if (numeric_flag) {
1212 // For numerical matrices Gauss is good, but Markowitz becomes
1213 // better for large sparse matrices.
1214 if ((ncells > 200) && (density < ncells/2)) {
1215 algo = solve_algo::markowitz;
1216 } else {
1217 algo = solve_algo::gauss;
1218 }
1219 } else {
1220 // For symbolic matrices Markowitz is good, but Bareiss/Divfree
1221 // is better for small and dense matrices.
1222 if ((ncells < 120) && (density*5 > ncells*3)) {
1223 if (ncells <= 12) {
1224 algo = solve_algo::divfree;
1225 } else {
1226 algo = solve_algo::bareiss;
1227 }
1228 } else {
1229 algo = solve_algo::markowitz;
1230 }
1231 }
1232 }
1233 // Eliminate the augmented matrix:
1234 std::vector<unsigned> colid(col);
1235 for (unsigned c = 0; c < col; c++) {
1236 colid[c] = c;
1237 }
1238 switch(algo) {
1239 case solve_algo::gauss:
1241 break;
1244 break;
1247 break;
1249 colid = markowitz_elimination(n);
1250 break;
1251 default:
1252 throw std::invalid_argument("matrix::echelon_form(): 'algo' is not one of the solve_algo enum");
1253 }
1254 return colid;
1255}
1256
1266int matrix::gauss_elimination(const bool det)
1267{
1269 const unsigned m = this->rows();
1270 const unsigned n = this->cols();
1271 GINAC_ASSERT(!det || n==m);
1272 int sign = 1;
1273
1274 unsigned r0 = 0;
1275 for (unsigned c0=0; c0<n && r0<m-1; ++c0) {
1276 int indx = pivot(r0, c0, true);
1277 if (indx == -1) {
1278 sign = 0;
1279 if (det)
1280 return 0; // leaves *this in a messy state
1281 }
1282 if (indx>=0) {
1283 if (indx > 0)
1284 sign = -sign;
1285 for (unsigned r2=r0+1; r2<m; ++r2) {
1286 if (!this->m[r2*n+c0].is_zero()) {
1287 // yes, there is something to do in this row
1288 ex piv = this->m[r2*n+c0] / this->m[r0*n+c0];
1289 for (unsigned c=c0+1; c<n; ++c) {
1290 this->m[r2*n+c] = (this->m[r2*n+c] - piv * this->m[r0*n+c]).normal();
1291 }
1292 }
1293 // fill up left hand side with zeros
1294 for (unsigned c=r0; c<=c0; ++c)
1295 this->m[r2*n+c] = _ex0;
1296 }
1297 if (det) {
1298 // save space by deleting no longer needed elements
1299 for (unsigned c=r0+1; c<n; ++c)
1300 this->m[r0*n+c] = _ex0;
1301 }
1302 ++r0;
1303 }
1304 }
1305 // clear remaining rows
1306 for (unsigned r=r0+1; r<m; ++r) {
1307 for (unsigned c=0; c<n; ++c)
1308 this->m[r*n+c] = _ex0;
1309 }
1310
1311 return sign;
1312}
1313
1314/* Perform Markowitz-ordered Gaussian elimination (with full
1315 * pivoting) on a matrix, constraining the choice of pivots to
1316 * the first n columns (this simplifies handling of augmented
1317 * matrices). Return the column id vector v, such that v[column]
1318 * is the original number of the column before shuffling (v[i]==i
1319 * for i >= n). */
1320std::vector<unsigned>
1322{
1323 GINAC_ASSERT(n <= col);
1324 std::vector<int> rowcnt(row, 0);
1325 std::vector<int> colcnt(col, 0);
1326 // Normalize everything before start. We'll keep all the
1327 // cells normalized throughout the algorithm to properly
1328 // handle unnormal zeros.
1329 for (unsigned r = 0; r < row; r++) {
1330 for (unsigned c = 0; c < col; c++) {
1331 if (!m[r*col + c].is_zero()) {
1332 m[r*col + c] = m[r*col + c].normal();
1333 rowcnt[r]++;
1334 colcnt[c]++;
1335 }
1336 }
1337 }
1338 std::vector<unsigned> colid(col);
1339 for (unsigned c = 0; c < col; c++) {
1340 colid[c] = c;
1341 }
1342 exvector ab(row);
1343 for (unsigned k = 0; (k < col) && (k < row - 1); k++) {
1344 // Find the pivot that minimizes (rowcnt[r]-1)*(colcnt[c]-1).
1345 unsigned pivot_r = row + 1;
1346 unsigned pivot_c = col + 1;
1347 int pivot_m = row*col;
1348 for (unsigned r = k; r < row; r++) {
1349 for (unsigned c = k; c < n; c++) {
1350 const ex &mrc = m[r*col + c];
1351 if (mrc.is_zero())
1352 continue;
1353 GINAC_ASSERT(rowcnt[r] > 0);
1354 GINAC_ASSERT(colcnt[c] > 0);
1355 int measure = (rowcnt[r] - 1)*(colcnt[c] - 1);
1356 if (measure < pivot_m) {
1357 pivot_m = measure;
1358 pivot_r = r;
1359 pivot_c = c;
1360 }
1361 }
1362 }
1363 if (pivot_m == row*col) {
1364 // The rest of the matrix is zero.
1365 break;
1366 }
1367 GINAC_ASSERT(k <= pivot_r && pivot_r < row);
1368 GINAC_ASSERT(k <= pivot_c && pivot_c < col);
1369 // Swap the pivot into (k, k).
1370 if (pivot_c != k) {
1371 for (unsigned r = 0; r < row; r++) {
1372 m[r*col + pivot_c].swap(m[r*col + k]);
1373 }
1374 std::swap(colid[pivot_c], colid[k]);
1375 std::swap(colcnt[pivot_c], colcnt[k]);
1376 }
1377 if (pivot_r != k) {
1378 for (unsigned c = k; c < col; c++) {
1379 m[pivot_r*col + c].swap(m[k*col + c]);
1380 }
1381 std::swap(rowcnt[pivot_r], rowcnt[k]);
1382 }
1383 // No normalization before is_zero() here, because
1384 // we maintain the matrix normalized throughout the
1385 // algorithm.
1386 ex a = m[k*col + k];
1387 GINAC_ASSERT(!a.is_zero());
1388 // Subtract the pivot row KJI-style (so: loop by pivot, then
1389 // column, then row) to maximally exploit pivot row zeros (at
1390 // the expense of the pivot column zeros). The speedup compared
1391 // to the usual KIJ order is not really significant though...
1392 for (unsigned r = k + 1; r < row; r++) {
1393 const ex &b = m[r*col + k];
1394 if (!b.is_zero()) {
1395 ab[r] = b/a;
1396 rowcnt[r]--;
1397 }
1398 }
1399 colcnt[k] = rowcnt[k] = 0;
1400 for (unsigned c = k + 1; c < col; c++) {
1401 const ex &mr0c = m[k*col + c];
1402 if (mr0c.is_zero())
1403 continue;
1404 colcnt[c]--;
1405 for (unsigned r = k + 1; r < row; r++) {
1406 if (ab[r].is_zero())
1407 continue;
1408 bool waszero = m[r*col + c].is_zero();
1409 m[r*col + c] = (m[r*col + c] - ab[r]*mr0c).normal();
1410 bool iszero = m[r*col + c].is_zero();
1411 if (waszero && !iszero) {
1412 rowcnt[r]++;
1413 colcnt[c]++;
1414 }
1415 if (!waszero && iszero) {
1416 rowcnt[r]--;
1417 colcnt[c]--;
1418 }
1419 }
1420 }
1421 for (unsigned r = k + 1; r < row; r++) {
1422 ab[r] = m[r*col + k] = _ex0;
1423 }
1424 }
1425 return colid;
1426}
1427
1437{
1439 const unsigned m = this->rows();
1440 const unsigned n = this->cols();
1441 GINAC_ASSERT(!det || n==m);
1442 int sign = 1;
1443
1444 unsigned r0 = 0;
1445 for (unsigned c0=0; c0<n && r0<m-1; ++c0) {
1446 int indx = pivot(r0, c0, true);
1447 if (indx==-1) {
1448 sign = 0;
1449 if (det)
1450 return 0; // leaves *this in a messy state
1451 }
1452 if (indx>=0) {
1453 if (indx>0)
1454 sign = -sign;
1455 for (unsigned r2=r0+1; r2<m; ++r2) {
1456 for (unsigned c=c0+1; c<n; ++c)
1457 this->m[r2*n+c] = (this->m[r0*n+c0]*this->m[r2*n+c] - this->m[r2*n+c0]*this->m[r0*n+c]).normal();
1458 // fill up left hand side with zeros
1459 for (unsigned c=r0; c<=c0; ++c)
1460 this->m[r2*n+c] = _ex0;
1461 }
1462 if (det) {
1463 // save space by deleting no longer needed elements
1464 for (unsigned c=r0+1; c<n; ++c)
1465 this->m[r0*n+c] = _ex0;
1466 }
1467 ++r0;
1468 }
1469 }
1470 // clear remaining rows
1471 for (unsigned r=r0+1; r<m; ++r) {
1472 for (unsigned c=0; c<n; ++c)
1473 this->m[r*n+c] = _ex0;
1474 }
1475
1476 return sign;
1477}
1478
1479
1491{
1492 // Method:
1493 // (single-step fraction free elimination scheme, already known to Jordan)
1494 //
1495 // Usual division-free elimination sets m[0](r,c) = m(r,c) and then sets
1496 // m[k+1](r,c) = m[k](k,k) * m[k](r,c) - m[k](r,k) * m[k](k,c).
1497 //
1498 // Bareiss (fraction-free) elimination in addition divides that element
1499 // by m[k-1](k-1,k-1) for k>1, where it can be shown by means of the
1500 // Sylvester identity that this really divides m[k+1](r,c).
1501 //
1502 // We also allow rational functions where the original prove still holds.
1503 // However, we must care for numerator and denominator separately and
1504 // "manually" work in the integral domains because of subtle cancellations
1505 // (see below). This blows up the bookkeeping a bit and the formula has
1506 // to be modified to expand like this (N{x} stands for numerator of x,
1507 // D{x} for denominator of x):
1508 // N{m[k+1](r,c)} = N{m[k](k,k)}*N{m[k](r,c)}*D{m[k](r,k)}*D{m[k](k,c)}
1509 // -N{m[k](r,k)}*N{m[k](k,c)}*D{m[k](k,k)}*D{m[k](r,c)}
1510 // D{m[k+1](r,c)} = D{m[k](k,k)}*D{m[k](r,c)}*D{m[k](r,k)}*D{m[k](k,c)}
1511 // where for k>1 we now divide N{m[k+1](r,c)} by
1512 // N{m[k-1](k-1,k-1)}
1513 // and D{m[k+1](r,c)} by
1514 // D{m[k-1](k-1,k-1)}.
1515
1517 const unsigned m = this->rows();
1518 const unsigned n = this->cols();
1519 GINAC_ASSERT(!det || n==m);
1520 int sign = 1;
1521 if (m==1)
1522 return 1;
1523 ex divisor_n = 1;
1524 ex divisor_d = 1;
1525 ex dividend_n;
1526 ex dividend_d;
1527
1528 // We populate temporary matrices to subsequently operate on. There is
1529 // one holding numerators and another holding denominators of entries.
1530 // This is a must since the evaluator (or even earlier mul's constructor)
1531 // might cancel some trivial element which causes divide() to fail. The
1532 // elements are normalized first (yes, even though this algorithm doesn't
1533 // need GCDs) since the elements of *this might be unnormalized, which
1534 // makes things more complicated than they need to be.
1535 matrix tmp_n(*this);
1536 matrix tmp_d(m,n); // for denominators, if needed
1537 exmap srl; // symbol replacement list
1538 auto tmp_n_it = tmp_n.m.begin(), tmp_d_it = tmp_d.m.begin();
1539 for (auto & it : this->m) {
1540 ex nd = it.normal().to_rational(srl).numer_denom();
1541 *tmp_n_it++ = nd.op(0);
1542 *tmp_d_it++ = nd.op(1);
1543 }
1544
1545 unsigned r0 = 0;
1546 for (unsigned c0=0; c0<n && r0<m-1; ++c0) {
1547 // When trying to find a pivot, we should try a bit harder than expand().
1548 // Searching the first non-zero element in-place here instead of calling
1549 // pivot() allows us to do no more substitutions and back-substitutions
1550 // than are actually necessary.
1551 unsigned indx = r0;
1552 while ((indx<m) &&
1553 (tmp_n[indx*n+c0].subs(srl, subs_options::no_pattern).expand().is_zero()))
1554 ++indx;
1555 if (indx==m) {
1556 // all elements in column c0 below row r0 vanish
1557 sign = 0;
1558 if (det)
1559 return 0;
1560 } else {
1561 if (indx>r0) {
1562 // Matrix needs pivoting, swap rows r0 and indx of tmp_n and tmp_d.
1563 sign = -sign;
1564 for (unsigned c=c0; c<n; ++c) {
1565 tmp_n.m[n*indx+c].swap(tmp_n.m[n*r0+c]);
1566 tmp_d.m[n*indx+c].swap(tmp_d.m[n*r0+c]);
1567 }
1568 }
1569 for (unsigned r2=r0+1; r2<m; ++r2) {
1570 for (unsigned c=c0+1; c<n; ++c) {
1571 dividend_n = (tmp_n.m[r0*n+c0]*tmp_n.m[r2*n+c]*
1572 tmp_d.m[r2*n+c0]*tmp_d.m[r0*n+c]
1573 -tmp_n.m[r2*n+c0]*tmp_n.m[r0*n+c]*
1574 tmp_d.m[r0*n+c0]*tmp_d.m[r2*n+c]).expand();
1575 dividend_d = (tmp_d.m[r2*n+c0]*tmp_d.m[r0*n+c]*
1576 tmp_d.m[r0*n+c0]*tmp_d.m[r2*n+c]).expand();
1577 bool check = divide(dividend_n, divisor_n,
1578 tmp_n.m[r2*n+c], true);
1579 check &= divide(dividend_d, divisor_d,
1580 tmp_d.m[r2*n+c], true);
1581 GINAC_ASSERT(check);
1582 }
1583 // fill up left hand side with zeros
1584 for (unsigned c=r0; c<=c0; ++c)
1585 tmp_n.m[r2*n+c] = _ex0;
1586 }
1587 if (c0<n && r0<m-1) {
1588 // compute next iteration's divisor
1589 divisor_n = tmp_n.m[r0*n+c0].expand();
1590 divisor_d = tmp_d.m[r0*n+c0].expand();
1591 if (det) {
1592 // save space by deleting no longer needed elements
1593 for (unsigned c=0; c<n; ++c) {
1594 tmp_n.m[r0*n+c] = _ex0;
1595 tmp_d.m[r0*n+c] = _ex1;
1596 }
1597 }
1598 }
1599 ++r0;
1600 }
1601 }
1602 // clear remaining rows
1603 for (unsigned r=r0+1; r<m; ++r) {
1604 for (unsigned c=0; c<n; ++c)
1605 tmp_n.m[r*n+c] = _ex0;
1606 }
1607
1608 // repopulate *this matrix:
1609 tmp_n_it = tmp_n.m.begin();
1610 tmp_d_it = tmp_d.m.begin();
1611 for (auto & it : this->m)
1612 it = ((*tmp_n_it++)/(*tmp_d_it++)).subs(srl, subs_options::no_pattern);
1613
1614 return sign;
1615}
1616
1617
1631int matrix::pivot(unsigned ro, unsigned co, bool symbolic)
1632{
1633 unsigned k = ro;
1634 if (symbolic) {
1635 // search first non-zero element in column co beginning at row ro
1636 while ((k<row) && (this->m[k*col+co].expand().is_zero()))
1637 ++k;
1638 } else {
1639 // search largest element in column co beginning at row ro
1640 GINAC_ASSERT(is_exactly_a<numeric>(this->m[k*col+co]));
1641 unsigned kmax = k+1;
1642 numeric mmax = abs(ex_to<numeric>(m[kmax*col+co]));
1643 while (kmax<row) {
1644 GINAC_ASSERT(is_exactly_a<numeric>(this->m[kmax*col+co]));
1645 numeric tmp = ex_to<numeric>(this->m[kmax*col+co]);
1646 if (abs(tmp) > mmax) {
1647 mmax = tmp;
1648 k = kmax;
1649 }
1650 ++kmax;
1651 }
1652 if (!mmax.is_zero())
1653 k = kmax;
1654 }
1655 if (k==row)
1656 // all elements in column co below row ro vanish
1657 return -1;
1658 if (k==ro)
1659 // matrix needs no pivoting
1660 return 0;
1661 // matrix needs pivoting, so swap rows k and ro
1663 for (unsigned c=0; c<col; ++c)
1664 this->m[k*col+c].swap(this->m[ro*col+c]);
1665
1666 return k;
1667}
1668
1672{
1673 for (auto & i : m)
1674 if (!i.is_zero())
1675 return false;
1676 return true;
1677}
1678
1680{
1681 // Find number of rows and columns
1682 size_t rows = l.nops(), cols = 0;
1683 for (auto & itr : l) {
1684 if (!is_a<lst>(itr))
1685 throw (std::invalid_argument("lst_to_matrix: argument must be a list of lists"));
1686 if (itr.nops() > cols)
1687 cols = itr.nops();
1688 }
1689
1690 // Allocate and fill matrix
1691 matrix & M = dynallocate<matrix>(rows, cols);
1692
1693 unsigned i = 0;
1694 for (auto & itr : l) {
1695 unsigned j = 0;
1696 for (auto & itc : ex_to<lst>(itr)) {
1697 M(i, j) = itc;
1698 ++j;
1699 }
1700 ++i;
1701 }
1702
1703 return M;
1704}
1705
1707{
1708 size_t dim = l.nops();
1709
1710 // Allocate and fill matrix
1711 matrix & M = dynallocate<matrix>(dim, dim);
1712
1713 unsigned i = 0;
1714 for (auto & it : l) {
1715 M(i, i) = it;
1716 ++i;
1717 }
1718
1719 return M;
1720}
1721
1722ex diag_matrix(std::initializer_list<ex> l)
1723{
1724 size_t dim = l.size();
1725
1726 // Allocate and fill matrix
1727 matrix & M = dynallocate<matrix>(dim, dim);
1728
1729 unsigned i = 0;
1730 for (auto & it : l) {
1731 M(i, i) = it;
1732 ++i;
1733 }
1734
1735 return M;
1736}
1737
1738ex unit_matrix(unsigned r, unsigned c)
1739{
1740 matrix & Id = dynallocate<matrix>(r, c);
1742 for (unsigned i=0; i<r && i<c; i++)
1743 Id(i,i) = _ex1;
1744
1745 return Id;
1746}
1747
1748ex symbolic_matrix(unsigned r, unsigned c, const std::string & base_name, const std::string & tex_base_name)
1749{
1750 matrix & M = dynallocate<matrix>(r, c);
1752
1753 bool long_format = (r > 10 || c > 10);
1754 bool single_row = (r == 1 || c == 1);
1755
1756 for (unsigned i=0; i<r; i++) {
1757 for (unsigned j=0; j<c; j++) {
1758 std::ostringstream s1, s2;
1759 s1 << base_name;
1760 s2 << tex_base_name << "_{";
1761 if (single_row) {
1762 if (c == 1) {
1763 s1 << i;
1764 s2 << i << '}';
1765 } else {
1766 s1 << j;
1767 s2 << j << '}';
1768 }
1769 } else {
1770 if (long_format) {
1771 s1 << '_' << i << '_' << j;
1772 s2 << i << ';' << j << "}";
1773 } else {
1774 s1 << i << j;
1775 s2 << i << j << '}';
1776 }
1777 }
1778 M(i, j) = symbol(s1.str(), s2.str());
1779 }
1780 }
1781
1782 return M;
1783}
1784
1785ex reduced_matrix(const matrix& m, unsigned r, unsigned c)
1786{
1787 if (r+1>m.rows() || c+1>m.cols() || m.cols()<2 || m.rows()<2)
1788 throw std::runtime_error("minor_matrix(): index out of bounds");
1789
1790 const unsigned rows = m.rows()-1;
1791 const unsigned cols = m.cols()-1;
1792 matrix & M = dynallocate<matrix>(rows, cols);
1794
1795 unsigned ro = 0;
1796 unsigned ro2 = 0;
1797 while (ro2<rows) {
1798 if (ro==r)
1799 ++ro;
1800 unsigned co = 0;
1801 unsigned co2 = 0;
1802 while (co2<cols) {
1803 if (co==c)
1804 ++co;
1805 M(ro2,co2) = m(ro, co);
1806 ++co;
1807 ++co2;
1808 }
1809 ++ro;
1810 ++ro2;
1811 }
1812
1813 return M;
1814}
1815
1816ex sub_matrix(const matrix&m, unsigned r, unsigned nr, unsigned c, unsigned nc)
1817{
1818 if (r+nr>m.rows() || c+nc>m.cols())
1819 throw std::runtime_error("sub_matrix(): index out of bounds");
1820
1821 matrix & M = dynallocate<matrix>(nr, nc);
1823
1824 for (unsigned ro=0; ro<nr; ++ro) {
1825 for (unsigned co=0; co<nc; ++co) {
1826 M(ro,co) = m(ro+r,co+c);
1827 }
1828 }
1829
1830 return M;
1831}
1832
1833} // namespace GiNaC
Interface to GiNaC's sums of expressions.
Archiving of GiNaC expressions.
#define GINAC_ASSERT(X)
Assertion macro for checking invariances.
Definition assertion.h:32
This class stores all properties needed to record/retrieve the state of one object of class basic (or...
Definition archive.h:48
This class is the ABC (abstract base class) of GiNaC's class hierarchy.
Definition basic.h:104
virtual size_t nops() const
Number of operands/members.
Definition basic.cpp:228
const basic & setflag(unsigned f) const
Set some status_flags.
Definition basic.h:287
virtual bool info(unsigned inf) const
Information about the object.
Definition basic.cpp:221
void ensure_if_modifiable() const
Ensure the object may be modified without hurting others, throws if this is not the case.
Definition basic.cpp:893
friend class ex
Definition basic.h:107
virtual ex op(size_t i) const
Return operand/member at position i.
Definition basic.cpp:237
ex subs_one_level(const exmap &m, unsigned options) const
Helper function for subs().
Definition basic.cpp:584
const basic & hold() const
Stop further evaluation.
Definition basic.cpp:886
virtual int compare_same_type(const basic &other) const
Returns order relation between two objects of same type.
Definition basic.cpp:718
void do_print_tree(const print_tree &c, unsigned level) const
Tree output to stream.
Definition basic.cpp:174
virtual ex expand(unsigned options=0) const
Expand expression, i.e.
Definition basic.cpp:795
virtual ex normal(exmap &repl, exmap &rev_lookup, lst &modifier) const
Default implementation of ex::normal().
Definition normal.cpp:2217
Wrapper template for making GiNaC classes out of STL containers.
Definition container.h:72
size_t nops() const override
Number of operands/members.
Definition container.h:117
@ automatic
Let the system choose.
Definition flags.h:92
@ divfree
Division-free elimination.
Definition flags.h:113
@ laplace
Laplace elimination.
Definition flags.h:119
@ gauss
Gauss elimination.
Definition flags.h:101
@ bareiss
Bareiss fraction-free elimination.
Definition flags.h:135
Lightweight wrapper for GiNaC's symbolic objects.
Definition ex.h:72
ex to_rational(exmap &repl) const
Rationalization of non-rational functions.
Definition normal.cpp:2626
ex expand(unsigned options=0) const
Expand an expression.
Definition ex.cpp:73
ex numer_denom() const
Get numerator and denominator of an expression.
Definition normal.cpp:2593
bool is_equal(const ex &other) const
Definition ex.h:345
ex normal() const
Normalization of rational functions.
Definition normal.cpp:2518
ex conjugate() const
Definition ex.h:146
unsigned return_type() const
Definition ex.h:230
size_t nops() const
Definition ex.h:135
bool info(unsigned inf) const
Definition ex.h:132
bool is_zero() const
Definition ex.h:213
ex collect(const ex &s, bool distributed=false) const
Definition ex.h:181
ex op(size_t i) const
Definition ex.h:136
This class holds one index of an indexed object.
Definition idx.h:35
ex get_dim() const
Get dimension of index space.
Definition idx.h:79
ex get_value() const
Get value of index.
Definition idx.h:70
This class holds an indexed expression.
Definition indexed.h:39
Symbolic matrices.
Definition matrix.h:37
matrix inverse() const
Inverse of this matrix, with automatic algorithm selection.
Definition matrix.cpp:936
int gauss_elimination(const bool det=false)
Perform the steps of an ordinary Gaussian elimination to bring the m x n matrix into an upper echelon...
Definition matrix.cpp:1266
ex scalar_mul_indexed(const ex &self, const numeric &other) const override
Product of an indexed matrix with a number.
Definition matrix.cpp:430
ex eval_indexed(const basic &i) const override
Automatic symbolic evaluation of an indexed matrix.
Definition matrix.cpp:317
unsigned cols() const
Get number of columns.
Definition matrix.h:76
ex charpoly(const ex &lambda) const
Characteristic Polynomial.
Definition matrix.cpp:891
void do_print_latex(const print_latex &c, unsigned level) const
Definition matrix.cpp:188
exvector m
representation (cols indexed first)
Definition matrix.h:116
ex determinant(unsigned algo=determinant_algo::automatic) const
Determinant of square matrix.
Definition matrix.cpp:734
const ex & operator()(unsigned ro, unsigned co) const
operator() to access elements for reading.
Definition matrix.cpp:683
void archive(archive_node &n) const override
Save (a.k.a.
Definition matrix.cpp:149
ex add_indexed(const ex &self, const ex &other) const override
Sum of two indexed matrices.
Definition matrix.cpp:394
bool is_zero_matrix() const
Function to check that all elements of the matrix are zero.
Definition matrix.cpp:1671
ex trace() const
Trace of a matrix.
Definition matrix.cpp:863
bool match_same_type(const basic &other) const override
Returns true if the attributes of two objects are similar enough for a match.
Definition matrix.cpp:306
matrix add(const matrix &other) const
Sum of matrices.
Definition matrix.cpp:552
matrix pow(const ex &expn) const
Power of a matrix.
Definition matrix.cpp:636
ex subs(const exmap &m, unsigned options=0) const override
Substitute a set of objects by arbitrary expressions.
Definition matrix.cpp:225
matrix(unsigned r, unsigned c)
Very common ctor.
Definition matrix.cpp:68
std::vector< unsigned > markowitz_elimination(unsigned n)
Definition matrix.cpp:1321
matrix solve(const matrix &vars, const matrix &rhs, unsigned algo=solve_algo::automatic) const
Solve a linear system consisting of a m x n matrix and a m x p right hand side by applying an elimina...
Definition matrix.cpp:992
void do_print_python_repr(const print_python_repr &c, unsigned level) const
Definition matrix.cpp:195
ex imag_part() const override
Definition matrix.cpp:270
void read_archive(const archive_node &n, lst &syms) override
Read (a.k.a.
Definition matrix.cpp:131
matrix mul_scalar(const ex &other) const
Product of matrix and scalar expression.
Definition matrix.cpp:620
void do_print(const print_context &c, unsigned level) const
Definition matrix.cpp:181
void print_elements(const print_context &c, const char *row_start, const char *row_end, const char *row_sep, const char *col_sep) const
Definition matrix.cpp:165
size_t nops() const override
nops is defined to be rows x columns.
Definition matrix.cpp:203
ex real_part() const override
Definition matrix.cpp:261
unsigned rank() const
Compute the rank of this matrix.
Definition matrix.cpp:1055
ex determinant_minor() const
Recursive determinant for small matrices having at least one symbolic entry.
Definition matrix.cpp:1093
matrix mul(const matrix &other) const
Product of matrices.
Definition matrix.cpp:586
bool contract_with(exvector::iterator self, exvector::iterator other, exvector &v) const override
Contraction of an indexed matrix with something else.
Definition matrix.cpp:445
unsigned rows() const
Get number of rows.
Definition matrix.h:74
ex op(size_t i) const override
returns matrix entry at position (i/col, icol).
Definition matrix.cpp:209
int division_free_elimination(const bool det=false)
Perform the steps of division free elimination to bring the m x n matrix into an upper echelon form.
Definition matrix.cpp:1436
unsigned col
number of columns
Definition matrix.h:115
ex & let_op(size_t i) override
returns writable matrix entry at position (i/col, icol).
Definition matrix.cpp:217
matrix transpose() const
Transposed of an m x n matrix, producing a new n x m matrix object that represents the transposed.
Definition matrix.cpp:709
matrix sub(const matrix &other) const
Difference of matrices.
Definition matrix.cpp:569
std::vector< unsigned > echelon_form(unsigned algo, int n)
Definition matrix.cpp:1194
ex conjugate() const override
Complex conjugate every matrix entry.
Definition matrix.cpp:236
int pivot(unsigned ro, unsigned co, bool symbolic=true)
Partial pivoting method for matrix elimination schemes.
Definition matrix.cpp:1631
int fraction_free_elimination(const bool det=false)
Perform the steps of Bareiss' one-step fraction free elimination to bring the matrix into an upper ec...
Definition matrix.cpp:1490
unsigned row
number of rows
Definition matrix.h:114
Product of expressions.
Definition mul.h:31
This class is a wrapper around CLN-numbers within the GiNaC class hierarchy.
Definition numeric.h:81
bool is_odd() const
True if object is an exact odd integer.
Definition numeric.cpp:1181
bool is_zero() const
True if object is zero.
Definition numeric.cpp:1128
This class holds a two-component object, a basis and and exponent representing exponentiation.
Definition power.h:38
Base class for print_contexts.
Definition print.h:101
Context for latex-parsable output.
Definition print.h:121
Context for python-parsable output.
Definition print.h:137
Switch to control algorithm for linear system solving.
Definition flags.h:140
@ bareiss
Bareiss fraction-free elimination.
Definition flags.h:184
@ markowitz
Markowitz-ordered Gaussian elimination.
Definition flags.h:192
@ automatic
Let the system choose.
Definition flags.h:145
@ divfree
Division-free elimination.
Definition flags.h:167
@ gauss
Gauss elimination.
Definition flags.h:155
@ evaluated
.eval() has already done its job
Definition flags.h:202
@ not_shareable
don't share instances of this object between different expressions unless explicitly asked to (used b...
Definition flags.h:205
@ no_pattern
disable pattern matching
Definition flags.h:50
Basic CAS symbol.
Definition symbol.h:38
unsigned options
Definition factor.cpp:2473
vector< int > k
Definition factor.cpp:1434
size_t n
Definition factor.cpp:1431
size_t c
Definition factor.cpp:756
ex x
Definition factor.cpp:1609
size_t r
Definition factor.cpp:756
mvec m
Definition factor.cpp:757
upoly poly
Definition factor.cpp:1442
Interface to GiNaC's indices.
Interface to GiNaC's indexed expressions.
Definition of GiNaC's lst.
Interface to symbolic matrices.
Definition add.cpp:35
bool is_zero(const ex &thisex)
Definition ex.h:835
ex symbolic_matrix(unsigned r, unsigned c, const std::string &base_name, const std::string &tex_base_name)
Create an r times c matrix of newly generated symbols consisting of the given base name plus the nume...
Definition matrix.cpp:1748
ex sub_matrix(const matrix &m, unsigned r, unsigned nr, unsigned c, unsigned nc)
Return the nr times nc submatrix starting at position r, c of matrix m.
Definition matrix.cpp:1816
std::map< ex, ex, ex_is_less > exmap
Definition basic.h:49
const numeric abs(const numeric &x)
Absolute value.
Definition numeric.cpp:2319
const ex _ex1
Definition utils.cpp:384
bool are_ex_trivially_equal(const ex &e1, const ex &e2)
Compare two objects of class quickly without doing a deep tree traversal.
Definition ex.h:699
ex rhs(const ex &thisex)
Definition ex.h:832
ex diag_matrix(const lst &l)
Convert list of diagonal elements to matrix.
Definition matrix.cpp:1706
const numeric * _num2_p
Definition utils.cpp:387
unsigned rows(const matrix &m)
Definition matrix.h:131
bool is_dummy_pair(const idx &i1, const idx &i2)
Check whether two indices form a dummy pair.
Definition idx.cpp:500
print_func< print_context >(&varidx::do_print). print_func< print_latex >(&varidx
Definition idx.cpp:43
unsigned cols(const matrix &m)
Definition matrix.h:134
const numeric * _num1_p
Definition utils.cpp:383
void swap(ex &e1, ex &e2)
Definition ex.h:838
int permutation_sign(It first, It last)
Definition utils.h:76
GINAC_IMPLEMENT_REGISTERED_CLASS_OPT_T(lst, basic, print_func< print_context >(&lst::do_print). print_func< print_tree >(&lst::do_print_tree)) template<> bool lst GINAC_BIND_UNARCHIVER(lst)
Specialization of container::info() for lst.
Definition lst.cpp:41
ex lst_to_matrix(const lst &l)
Convert list of lists to matrix.
Definition matrix.cpp:1679
bool divide(const ex &a, const ex &b, ex &q, bool check_args)
Exact polynomial division of a(X) by b(X) in Q[X].
Definition normal.cpp:594
const ex _ex0
Definition utils.cpp:368
std::vector< ex > exvector
Definition basic.h:47
ex unit_matrix(unsigned r, unsigned c)
Create an r times c unit matrix.
Definition matrix.cpp:1738
ex reduced_matrix(const matrix &m, unsigned r, unsigned c)
Return the reduced matrix that is formed by deleting the rth row and cth column of matrix m.
Definition matrix.cpp:1785
Definition ex.h:987
void swap(GiNaC::ex &a, GiNaC::ex &b)
Specialization of std::swap() for ex objects.
Definition ex.h:991
This file defines several functions that work on univariate and multivariate polynomials and rational...
Makes the interface to the underlying bignum package available.
Interface to GiNaC's overloaded operators.
Interface to GiNaC's symbolic exponentiation (basis^exponent).
#define GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(classname, supername, options)
Macro for inclusion in the implementation of each registered class.
Definition registrar.h:183
Interface to GiNaC's symbolic objects.
Interface to several small and furry utilities needed within GiNaC but not of any interest to the use...

This page is part of the GiNaC developer's reference. It was generated automatically by doxygen. For an introduction, see the tutorial.