-
Notifications
You must be signed in to change notification settings - Fork 0
/
MainActivity.java
281 lines (236 loc) · 10.7 KB
/
MainActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
package com.bob.largeqr;
/*
This app uses high precision arithmetic in software to implement a QR factorization of a complex matrix.
The high precision arithmetic software library is from:
http://www.apfloat.org/apfloat_java/
The computation of the Householder vector follows the definition given in:
http://arith.cs.ucla.edu/publications/House-Asil06.pdf
While this does give the correct answer, compared to a simple Java version, I don't consider it particularly
useful because of the time consumed in the software implementation of arithmetic operations. For example,
running on a 2019 variety mobile phone the execution times for a 192x120 matrix are:
extended precision: 60 seconds
Java double precision: 100 milliseconds
Arm64 assembly: 3.5 milliseconds
If the matrices were very large, where 64 bit floating point was inadequate, a parallel implementation
would be necessary.
These results are in contrast to a backsolve operation where complex matrices as small as 64x64 start to
benefit and are required at size 128x128.
The Complex class is from:
*
* WRITTEN BY: Dr Michael Thomas Flanagan
*
* DATE: February 2002
* UPDATED: 1 August 2006, 29 April 2007, 15,21,22 June 2007, 22 November 2007
* 20 May 2008, 26 August 2008, 9 November 2009, 6 june 2010
*
* DOCUMENTATION:
* See Michael T Flanagan's Java library on-line web pages:
* http://www.ee.ucl.ac.uk/~mflanaga/java/Complex.html
* http://www.ee.ucl.ac.uk/~mflanaga/java/
*
* Copyright (c) 2002 - 2009 Michael Thomas Flanagan
*
*/
import static org.apfloat.ApfloatMath.sqrt;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.apfloat.Apcomplex;
import org.apfloat.Apfloat;
import org.apfloat.ApfloatRuntimeException;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "bob";
private TextView tv;
private ProgressBar spinner;
public static final int APFLOAT_PRECISION = 100; // arbitrary, choose your own value
public static final int rows = 192;
public static final int cols = 120;
CreateComplexMatrix src; // simple A matrix as Complex[][]
private boolean ready = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onResume() {
super.onResume();
if (ready) {
initialize();
process();
} else {
Log.d(TAG, "sleeping");
}
}
private void initialize() {
tv = findViewById(R.id.tv);
tv.setText(getResources().getString(R.string.instructions));
tv.append("Matrix size is " + rows + "x" + cols + " complex elements.\n");
spinner = findViewById(R.id.spinner);
spinner.setVisibility(View.INVISIBLE);
}
private void process() {
// create a large complex matrix for testing
src = new CreateComplexMatrix(rows, cols);
doSomeTaskAsync(rows, cols);
}
Apcomplex[] householderVector(Apcomplex[] x) {
Apfloat one = new Apfloat(1.0d, APFLOAT_PRECISION);
Apfloat xnorm = norm(x);
Apfloat x0mag = sqrt(x[0].multiply(x[0].conj()).real()); // magnitude of first element of vector
Apcomplex[] w = new Apcomplex[x.length];
try {
x[0] = x[0].multiply(one.add(xnorm.divide(x0mag)));
} catch (ArithmeticException | ApfloatRuntimeException e) {
Log.d(TAG, "div exception " + x.length);
}
System.arraycopy(x, 0, w, 0, w.length);
Apfloat wnorm = one.divide(norm(w));
for (int i = 0; i < w.length; i++) { // w vector is normalized, this is the Householder vector
w[i] = w[i].multiply(wnorm);
}
return w;
}
Apfloat norm(Apcomplex[] x) {
Apfloat xnorm = new Apfloat(0.0d, APFLOAT_PRECISION);
for (int i=0; i<x.length; i++) {
xnorm = xnorm.add(x[i].multiply(x[i].conj()).real());
}
xnorm = sqrt(xnorm);
return xnorm;
}
private void postProcess(Apcomplex[][] QR) {
Log.d(TAG, "results of QR:");
int m = QR.length;
int n = QR[0].length; // this is m x n
long start, end;
Complex[][] z = src.getMatrix();
start = System.currentTimeMillis();
QRfactorizationComplex qr = new QRfactorizationComplex(z);
Complex[][] ans = qr.getR(); // this will be n x n
end = System.currentTimeMillis();
tv.append("Java time is " + (end-start) + " milliseconds.\n");
double norm = 0., ar, ai;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
ar = ans[i][j].getReal() - QR[i][j].real().doubleValue();
ai = ans[i][j].getImag() - QR[i][j].imag().doubleValue();
norm += ar*ar + ai*ai;
}
}
tv.append("norm of difference from Java version is " + norm + "\n");
}
private void doSomeTaskAsync(int m, int n) {
Activity currActivity = MainActivity.this;
spinner.setVisibility(View.VISIBLE);
new BackgroundTask(currActivity) {
String allDone;
final Complex[][] qr = src.getMatrix();
final Apcomplex[][] QR = new Apcomplex[m][n];
long executionTime;
@Override
public void doInBackground() {
long start, end;
// this converts the Complex[][] source matrix to extended precision form
for (int i=0; i<m; i++) {
for (int j=0; j<n; j++) {
QR[i][j] = new Apcomplex(new Apfloat(qr[i][j].getReal(), APFLOAT_PRECISION), new Apfloat(qr[i][j].getImag(), APFLOAT_PRECISION));
}
}
start = System.currentTimeMillis();
Log.d(TAG, "working on it");
int p = n;
if (n == m) p = n-1;
Apfloat beta = new Apfloat(2.0d, APFLOAT_PRECISION);
for (int k=0; k<p; k++) {
Apcomplex[] column = new Apcomplex[m-k];
for (int i=0; i<m-k; i++) {
column[i] = QR[i + k] [k];
}
Apcomplex[] w = householderVector(column);
for (int j = k; j < n; j++) {
Apcomplex s = new Apcomplex(new Apfloat(0.0d, APFLOAT_PRECISION));
for (int i = k; i < m; i++) {
s = s.add(QR[i][j].multiply(w[i - k].conj()));
}
Apcomplex alpha = s.multiply(beta);
for (int i = k; i < m; i++) {
QR[i][j] = QR[i][j].subtract(alpha.multiply(w[i - k]));
}
}
}
end = System.currentTimeMillis();
executionTime = end - start;
Log.d(TAG, "finished in " + executionTime + " milliseconds");
allDone = "Thank you for your attention.\n";
}
@Override
public void onPostExecute() {
spinner.setVisibility(View.INVISIBLE);
tv.append(executionTime + " milliseconds for " + m + "x" + n + " complex matrix using extended precision.\n");
tv.append(allDone);
postProcess(QR);
}
}.execute();
}
public void logPrint(Complex[][] m) {
logPrint(m, "");
}
public void logPrint(Complex[][] mat, String header) {
int m = mat.length;
int n = mat[0].length;
if (!header.equals("")) {
Log.d(TAG, header);
}
if (m == 1) {
Log.d(TAG, "row = 0" + " " + mat[0][0].getReal() + " +i*" + mat[0][0].getImag());
} else if (m == 2) {
for (int i=0; i<m; i++) {
switch (n) {
case 1:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag());
break;
case 2:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag());
break;
}
}
} else if (m == 3) {
for (int i=0; i<m; i++) {
switch (n) {
case 1:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag());
break;
case 2:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag());
break;
case 3:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag() + " " + mat[i][2].getReal() + " +i*" + mat[i][2].getImag());
break;
}
}
} else {
for (int i=0; i<4; i++) {
switch (n) {
case 1:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag());
break;
case 2:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag());
break;
case 3:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag() + " " + mat[i][2].getReal() + " +i*" + mat[i][2].getImag());
break;
default:
Log.d(TAG, "row = " + i + " " + mat[i][0].getReal() + " +i*" + mat[i][0].getImag() + " " + mat[i][1].getReal() + " +i*" + mat[i][1].getImag() + " " + mat[i][2].getReal() + " +i*" + mat[i][2].getImag() + " " + mat[i][3].getReal() + " +i*" + mat[i][3].getImag());
break;
}
}
}
}
}