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