123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- #! /bin/perl
- #
- # From: Reuben Settergren <rjs@jhu.edu>
- #
- # I wrote a perl script to randomly generate matrices with smallish integers,
- # and test for a suitable determinant (typically +/-1), and output the matrix
- # and its inverse.
- #
- # There are switches to make it -s[ymmetric] and/or -d[iagonally strong],
- # which are necessary and good properties for covariance matrices. You may
- # need to grab Math::MatrixReal and Math::Factor::XS from cpan.
- use Math::MatrixReal;
- use Math::Factor::XS qw(prime_factors);
- use Getopt::Std;
- $opt{n} = 1;
- getopts('hsdn:', \%opt);
- $usage .= "easyinverse.pl [-hsd] [-n N]\n";
- $usage .= " -h this message\n";
- $usage .= " -s symmetric\n";
- $usage .= " -d diagonally dominant\n";
- $usage .= " -n N number to generate (DEF 1)\n";
- if ($opt{h}) {
- print $usage;
- exit;
- }
- sub randint{ # but not 0
- my $min = shift or -5;
- my $max = shift or +5;
- my $ansa = 0;
- while ($ansa == 0) {
- my $r = rand;
- $r *= ($max - $min + 1);
- my $i = int($r);
- $ansa = $min + $i;
- }
- return $ansa;
- }
- $size = 4;
- sub printmateq {
- my $lbl = shift;
- my $mat = shift;
- my $round = shift;
- if ($round) {
- for my $r (1..$size) {
- for my $c (1..$size) {
- my $elt = $mat->element($r,$c);
- my $pelt = abs($elt);
- my $prnd = int($pelt * $round + 0.5) / $round;
- my $rnd = $prnd * ($elt < 0 ? -1 : 1);
- $mat->assign($r, $c, $rnd);
- }}
- }
- my @rows;
- for my $r (1..$size) {
- my @row = map {$mat->element($r, $_)} (1..$size);
- my $rowstr = '{' . (join ',', @row) . '}';
- push @rows, $rowstr;
- }
- my $matstr = '{' . (join ',', @rows) . '}';
- $matstr =~ s!\.?000+\d+!!g;
- print "$lbl = $matstr;\n";
- }
- for (1..$opt{n}) { # requested number of matrices
- $M = new Math::MatrixReal($size,$size);
- while (1) {
- for $r (1..$size) {
- if ($opt{d}) { $M->assign($r,$r, randint(5,10)) }
- else { $M->assign($r,$r, randint(-3, 3)) }
- for $c ($r+1..$size) {
- $i = randint(-5,5);
- $M->assign($r, $c, $i);
- $M->assign($c, $r, ($opt{s} ? $i : randint(-5,5)));
- }
- }
- $det = int($M->det + 0.5);
- next if $det == 0;
- @factors = prime_factors(abs($det));
- $all_good_factors = 1; # cross your fingers!
- for $f (@factors) {
- if ($f != 2 || $f != 5) {
- $all_good_factors = 0;
- last;
- }
- }
- next unless $all_good_factors;
- # test for positive semidefinite ~ all positive eigenvalues
- $evals = $M->sym_eigenvalues;
- $all_positive_evals = 1; # keep hope alive!
- for $r (1..$size) {
- if ($evals->element($r,1) <= 0) {
- $all_positive_evals = 0;
- last;
- }
- }
- last if $all_positive_evals;
- }
- printmateq(".mat", $M);
- printmateq(".inv", $M->inverse, 1000);
- }
|