def conjugate_gradient_solve(sys_mat: Matrix,
                             sys_vec: Vector,
                             max_iter=100,
                             max_error=1e-8) -> Vector:
    """
    The conjugate gradient method is a iterative numeric method to
    solve systems of linear equations.

    The method starts with a vector full of zeroes as the first
    approximation to the solution and improves it in each
    iteration.
    In every iteration the error vector `error` is checked and only
    in the case where every value is less than the `max_error`, the
    solution is considered "good enough" and the solution vector
    returned.

    If after `max_iter` iterations a "good enough" solution hasn't
    been found, the function raises an `ArithmeticError`.

    :param sys_mat: system `Matrix`
    :param sys_vec: system `Vector`
    :param max_iter: `int` max number of iterations
    :param max_error: `float` max error accepted in the solution
    :return: solution `Vector`
    """
    validate_system(sys_mat, sys_vec)

    solution = Vector(sys_vec.length)
    error = sys_vec - sys_mat.times_vector(solution)
    p = error.copy()

    def solution_good_enough():
        for i in range(sys_vec.length):
            if math.fabs(error.value_at(i)) > max_error:
                return False

        return True

    for _ in range(max_iter):
        if solution_good_enough():
            return solution

        m_times_p = sys_mat.times_vector(p)
        alpha = (error * error).sum / (p * m_times_p).sum
        solution += p.scaled(alpha)
        old_error = error.copy()
        error -= m_times_p.scaled(alpha)
        beta = (error * error).sum / (old_error * old_error).sum
        p = error + p.scaled(beta)

    raise ArithmeticError(
        f'Reached max number of iterations ({max_iter}) without ' +
        'a good solution ')
Beispiel #2
0
    def __assemble_system_matrix(self, size: int):
        matrix = Matrix(size, size)

        for bar in self.__bars:
            bar_matrix = bar.global_stiffness_matrix()
            dofs = self.__bar_dofs(bar)

            for row, row_dof in enumerate(dofs):
                for col, col_dof in enumerate(dofs):
                    matrix.add_to_value(bar_matrix.value_at(row, col), row_dof,
                                        col_dof)

        self.__system_matrix = matrix
Beispiel #3
0
def doolitle_decomposition(matrix: Matrix) -> (Matrix, Matrix):
    """
    Decomposes the matrix `matrix` into the product of a lower
    triangular and an upper triangular matrices: [A] = [L][U].

    This function returns a tuple including the lower and upper
    triangular matrices, exactly in this order: ([L], [U]).

    :param matrix: `Matrix`
    :return: ([L], [U])
    """
    if not matrix.is_square:
        raise ValueError('Can\'t decompose a non-square matrix')

    size = matrix.rows_count
    (lower, upper) = (Matrix(size, size), Matrix(size, size))

    for i in range(size):
        for j in range(size):
            val = matrix.value_at(i, j)

            if i <= j:
                _sum = 0
                for k in range(i):
                    l_ik = lower.value_at(i, k)
                    u_kj = upper.value_at(k, j)
                    _sum += (l_ik * u_kj)

                upper.set_value(val - _sum, i, j)

            if j <= i:
                _sum = 0
                for k in range(j):
                    l_ik = lower.value_at(i, k)
                    u_kj = upper.value_at(k, j)
                    _sum += (l_ik * u_kj)

                u_jj = upper.value_at(j, j)
                lower.set_value((val - _sum) / u_jj, i, j)

    return lower, upper
Beispiel #4
0
class ConjugateGradientTest(unittest.TestCase):
    sys_matrix = Matrix(4, 4).set_data([
        4.0, -2.0, 4.0, 2.0,
        -2.0, 10.0, -2.0, -7.0,
        4.0, -2.0, 8.0, 4.0,
        2.0, -7.0, 4.0, 7.0
    ])
    sys_vec = Vector(4).set_data([20, -16, 40, 28])
    solution = Vector(4).set_data([1.0, 2.0, 3.0, 4.0])

    def test_solve(self):
        actual = conjugate_gradient_solve(
            self.sys_matrix,
            self.sys_vec
        )
        self.assertEqual(self.solution, actual)
Beispiel #5
0
    def test_assemble_system_matrix(self, cholesky_mock):
        eal3 = 0.1118033989
        c2_eal3 = .8 * eal3
        s2_eal3 = .2 * eal3
        cs_eal3 = .4 * eal3
        expected_mat = Matrix(6, 6).set_data([
            c2_eal3, cs_eal3, 0, 0, -c2_eal3, -cs_eal3, cs_eal3, .25 + s2_eal3,
            0, -.25, -cs_eal3, -s2_eal3, 0, 0, .125, 0, -.125, 0, 0, -.25, 0,
            .25, 0, 0, -c2_eal3, -cs_eal3, -.125, 0, .125 + c2_eal3, cs_eal3,
            -cs_eal3, -s2_eal3, 0, 0, cs_eal3, s2_eal3
        ])

        self.structure.solve_structure()
        [actual_mat, _] = cholesky_mock.call_args[0]

        cholesky_mock.assert_called_once()
        self.assertEqual(expected_mat, actual_mat)
Beispiel #6
0
    def test_system_matrix_constraints(self, cholesky_mock):
        self._set_external_constraints()

        eal3 = 0.1118033989
        c2_eal3 = .8 * eal3
        s2_eal3 = .2 * eal3
        cs_eal3 = .4 * eal3
        expected_mat = Matrix(6, 6).set_data([
            1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
            0, 0, 0, 0, 0, 0, .125 + c2_eal3, cs_eal3, 0, 0, 0, 0, cs_eal3,
            s2_eal3
        ])

        self.structure.solve_structure()
        [actual_mat, _] = cholesky_mock.call_args[0]

        cholesky_mock.assert_called_once()
        self.assertEqual(expected_mat, actual_mat)
Beispiel #7
0
    def global_stiffness_matrix(self) -> Matrix:
        """
        Computes the bar's stiffness matrix in global coordinates.

        :return: global stiffness `Matrix`
        """
        direction = self.geometry.direction_vector
        eal = self.young_mod * self.cross_section / self.length
        c = direction.cosine
        s = direction.sine

        c2_eal = (c**2) * eal
        s2_eal = (s**2) * eal
        sc_eal = (s * c) * eal

        return Matrix(4, 4).set_data([
            c2_eal, sc_eal, -c2_eal, -sc_eal, sc_eal, s2_eal, -sc_eal, -s2_eal,
            -c2_eal, -sc_eal, c2_eal, sc_eal, -sc_eal, -s2_eal, sc_eal, s2_eal
        ])
Beispiel #8
0
 def test_global_stiffness_matrix(self):
     expected = Matrix(4, 4).set_data(
         [4, 2, -4, -2, 2, 1, -2, -1, -4, -2, 4, 2, -2, -1, 2, 1])
     actual = self.bar.global_stiffness_matrix()
     self.assertEqual(expected, actual)