Graphviz 14.1.3~dev.20260207.0611
Loading...
Searching...
No Matches
lu.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * This code was (mostly) written by Ken Turkowski, who said:
13 *
14 * Oh, that. I wrote it in college the first time. It's open source - I think I
15 * posted it after seeing so many people solve equations by inverting matrices
16 * by computing minors naïvely.
17 * -Ken
18 *
19 * The views represented here are mine and are not necessarily shared by
20 * my employer.
21 Ken Turkowski turk@apple.com
22 Immersive Media Technologist http://www.worldserver.com/turk/
23 Apple Computer, Inc.
24 1 Infinite Loop, MS 302-3VR
25 Cupertino, CA 95014
26 */
27
28
29
30/* This module solves linear equations in several variables (Ax = b) using
31 * LU decomposition with partial pivoting and row equilibration. Although
32 * slightly more work than Gaussian elimination, it is faster for solving
33 * several equations using the same coefficient matrix. It is
34 * particularly useful for matrix inversion, by sequentially solving the
35 * equations with the columns of the unit matrix.
36 *
37 * lu_decompose() decomposes the coefficient matrix into the LU matrix,
38 * and lu_solve() solves the series of matrix equations using the
39 * previous LU decomposition.
40 *
41 * Ken Turkowski (apple!turk)
42 * written 3/2/79, revised and enhanced 8/9/83.
43 */
44
45#include "config.h"
46
47#include <assert.h>
48#include <math.h>
49#include <neatogen/neato.h>
50#include <stdlib.h>
51#include <util/alloc.h>
52#include <util/gv_math.h>
53
54/* lu_decompose() decomposes the coefficient matrix A into upper and lower
55 * triangular matrices, the composite being the LU matrix.
56 *
57 * The arguments are:
58 *
59 * lu - the state for the computation
60 * a - the (n x n) coefficient matrix
61 * n - the order of the matrix
62 *
63 * 1 is returned if the decomposition was successful,
64 * and 0 is returned if the coefficient matrix is singular.
65 */
66
67int lu_decompose(lu_t *lu, double **a, int n) {
68 assert(lu != NULL);
69
70 lu->lu = new_array(n, n, 0.0);
71 lu->ps = gv_calloc(n, sizeof(int));
72 double *const scales = gv_calloc(n, sizeof(double));
73
74 for (int i = 0; i < n; i++) { // for each row
75 /* Find the largest element in each row for row equilibration */
76 double biggest = 0;
77 for (int j = 0; j < n; j++)
78 biggest = fmax(biggest, fabs(lu->lu[i][j] = a[i][j]));
79 if (biggest > 0.0)
80 scales[i] = 1.0 / biggest;
81 else {
82 free(scales);
83 lu_free(lu);
84 return 0; /* Zero row: singular matrix */
85 }
86 lu->ps[i] = i; // initialize pivot sequence
87 }
88
89 for (int k = 0, pivotindex = 0; k < n - 1; k++) { // for each column
90 /* Find the largest element in each column to pivot around */
91 double biggest = 0;
92 for (int i = k; i < n; i++) {
93 const double tempf = fabs(lu->lu[lu->ps[i]][k]) * scales[lu->ps[i]];
94 if (biggest < tempf) {
95 biggest = tempf;
96 pivotindex = i;
97 }
98 }
99 if (biggest <= 0.0) {
100 free(scales);
101 lu_free(lu);
102 return 0; /* Zero column: singular matrix */
103 }
104 if (pivotindex != k) { /* Update pivot sequence */
105 SWAP(&lu->ps[k], &lu->ps[pivotindex]);
106 }
107
108 /* Pivot, eliminating an extra variable each time */
109 const double pivot = lu->lu[lu->ps[k]][k];
110 for (int i = k + 1; i < n; i++) {
111 const double mult = lu->lu[lu->ps[i]][k] = lu->lu[lu->ps[i]][k] / pivot;
112 for (int j = k + 1; j < n; j++)
113 lu->lu[lu->ps[i]][j] -= mult * lu->lu[lu->ps[k]][j];
114 }
115 }
116
117 free(scales);
118 if (lu->lu[lu->ps[n - 1]][n - 1] == 0.0) {
119 lu_free(lu);
120 return 0; /* Singular matrix */
121 }
122 return 1;
123}
124
125/* lu_solve() solves the linear equation (Ax = b) after the matrix A has
126 * been decomposed with lu_decompose() into the lower and upper triangular
127 * matrices L and U.
128 *
129 * The arguments are:
130 *
131 * lu - the state for the computation
132 * x - the solution vector
133 * bi - the only 1.0 element of b within an otherwise-0.0 vector
134 * n - the order of the equation
135*/
136
137void lu_solve(const lu_t *lu, double *x, int bi, int n) {
138 /* Vector reduction using U triangular matrix */
139 for (int i = 0; i < n; i++) {
140 double dot = 0;
141 for (int j = 0; j < i; j++)
142 dot += lu->lu[lu->ps[i]][j] * x[j];
143 x[i] = (lu->ps[i] == bi) - dot;
144 }
145
146 /* Back substitution, in L triangular matrix */
147 for (int i = n - 1; i >= 0; i--) {
148 double dot = 0;
149 for (int j = i + 1; j < n; j++)
150 dot += lu->lu[lu->ps[i]][j] * x[j];
151 x[i] = (x[i] - dot) / lu->lu[lu->ps[i]][i];
152 }
153}
154
155void lu_free(lu_t *lu) {
156 if (lu == NULL) {
157 return;
158 }
159 if (lu->lu != NULL) {
160 free_array(lu->lu);
161 }
162 free(lu->ps);
163}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define dot(v, w)
Definition geom.c:191
void free(void *)
node NULL
Definition grammar.y:181
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:134
void lu_solve(const lu_t *lu, double *x, int bi, int n)
Definition lu.c:137
void lu_free(lu_t *lu)
release resources relating to LU decomposition
Definition lu.c:155
int lu_decompose(lu_t *lu, double **a, int n)
Definition lu.c:67
NEATOPROCS_API void free_array(double **rv)
Definition stuff.c:54
NEATOPROCS_API double ** new_array(int i, int j, double val)
Definition stuff.c:39
state for working on LU decomposition
Definition neato.h:38
double ** lu
composite of upper and lower triangular matrices
Definition neato.h:39
int * ps
pivot sequence
Definition neato.h:40