Last active
July 1, 2017 11:19
-
-
Save hejmsdz/274da4aebdd3711a2d91c172e59a077e to your computer and use it in GitHub Desktop.
Matrix inversion helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "matrix" | |
require "readline" | |
class Rational | |
# convert to integer if is actually an integer | |
def try_to_i | |
to_i == self ? to_i : self | |
end | |
end | |
class Integer | |
# for consistency with Rational#try_to_i; does nothing | |
def try_to_i | |
self | |
end | |
end | |
class Matrix | |
# output the matrix in a user-readable way; return self for chaining | |
def output(line=-1) | |
row_vectors.each do |row| | |
row.to_a.each_with_index do |x, i| | |
print " | " if line == i | |
printf("%5s", x) | |
end | |
puts | |
end | |
self | |
end | |
# create an augmented matrix ie. append another matrix (identity by default) on the right | |
def aug(other=nil) | |
if other.nil? | |
other = Matrix.unit(row_count) | |
end | |
Matrix.build(row_count, column_count+other.column_count) do |i, j| # row, column | |
if j < column_count then element(i, j) | |
else other[i, j-column_count] | |
end | |
end | |
end | |
# swap rows of a matrix | |
def swap_rows(a, b) | |
row_a = row(a) | |
row_b = row(b) | |
Matrix.build(row_count, column_count) do |i, j| # row, column | |
if i == a then row_b[j] | |
elsif i == b then row_a[j] | |
else element(i, j) | |
end | |
end | |
end | |
# multiply a row by a constant | |
def multiply_row(a, times) | |
Matrix.build(row_count, column_count) do |i, j| # row, column | |
(element(i, j) * (i == a ? times : 1)).try_to_i | |
end | |
end | |
# add a row to another row, optionally multiplied by a constant | |
def add_row(row1, row2, times=1) | |
Matrix.build(row_count, column_count) do |i, j| # row, column | |
x = element(i, j) | |
if i == row1 then (x + element(row2, j) * times).try_to_i | |
else x | |
end | |
end | |
end | |
end | |
class MatrixInverter | |
def initialize(matrix) | |
raise "Matrix is not square" unless matrix.square? | |
raise "Matrix is singular" if matrix.singular? | |
@matrix = matrix | |
@size = matrix.row_count | |
@aug = matrix.aug | |
@inv = matrix.inv | |
@stack = [] | |
end | |
# check if the inversion process has been completed | |
def inverted? | |
@aug.minor(0...@size, @size...2*@size) == @inv | |
end | |
# return result of a transformation, according to the command | |
def exec_command(command, *args) | |
case command | |
when "undo" | |
if @stack.length >= 1 | |
puts "Undoing last operation" | |
@stack.pop | |
else | |
puts "Error: Nothing to undo!" | |
end | |
when "swap" | |
@aug.swap_rows(args[0]-1, args[1]-1) | |
when "mult" | |
@aug.multiply_row(args[0]-1, args[1]) | |
when "addr" | |
@aug.add_row(args[0]-1, args[1]-1, args[2] || 1) | |
end | |
end | |
def print_help | |
puts "Perform elementary operations on the augmented matrix" | |
puts "to transform the left side to an identity matrix." | |
puts "The right side will then become the inverse of the one you entered." | |
puts | |
puts "Commands:" | |
puts "swap a b swap a-th and b-th row" | |
puts "mult a x multiply the a-th row by x" | |
puts "addr a b [x] add the b-th row [multiplied by x] to the a-th row" | |
puts "undo revert the last operation" | |
puts "exit quit the program" | |
end | |
def main_loop | |
while !inverted? | |
@aug.output(@size) | |
buf = Readline.readline("> ", true) | |
break unless buf | |
words = buf.split | |
command = words.shift.downcase | |
args = words.map(&:to_r).map(&:try_to_i) | |
aug_prime = exec_command(command, *args) | |
unless aug_prime.nil? | |
@stack << @aug unless command == "undo" | |
@aug = aug_prime | |
end | |
end | |
puts "Done." | |
@aug.output(@size) | |
end | |
end | |
puts "Enter size of the matrix:" | |
n = gets.to_i | |
puts "Input the matrix:" | |
matrix = Matrix[*Array.new(n) { gets.split.map(&:to_i) }] | |
mi = MatrixInverter.new(matrix) | |
mi.print_help | |
mi.main_loop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment