1 /*
2
3 dsh-matrix-io Matrix readers and writers.
4 Copyright (c) 2008-2013 held jointly by the individual authors.
5
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 3 of the License, or (at
9 your option) any later version.
10
11 This library is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with this library; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19
20 > http://www.fsf.org/licensing/licenses/lgpl.html
21 > http://www.opensource.org/licenses/lgpl-license.php
22
23 */
24 package org.dishevelled.matrix.io.impl;
25
26 import java.io.BufferedReader;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30
31 import java.util.HashMap;
32 import java.util.Map;
33
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 import org.dishevelled.matrix.Matrix2D;
38
39 /**
40 * Abstract Matrix Market format reader for matrices of doubles in two dimensions.
41 *
42 * @author Michael Heuer
43 * @version $Revision$ $Date$
44 */
45 public abstract class AbstractMatrixMarketReader
46 extends AbstractMatrix2DReader<Double>
47 {
48 /** Map of reader strategies keyed by name. */
49 private static final Map<String, ReaderStrategy> STRATEGIES;
50
51 /** Pattern to match Matrix Market header line. */
52 private static final Pattern HEADER = Pattern.compile("^%%MatrixMarket matrix\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s*$");
53
54 static
55 {
56 STRATEGIES = new HashMap<String, ReaderStrategy>();
57 STRATEGIES.put("general", new GeneralReaderStrategy());
58 STRATEGIES.put("symmetric", new SymmetricReaderStrategy());
59 STRATEGIES.put("skew-symmetric", new SkewSymmetricReaderStrategy());
60 // TODO: hermitian might only be valid for complex matrices
61 STRATEGIES.put("hermitian", new HermitianReaderStrategy());
62 }
63
64 /**
65 * {@inheritDoc}
66 *
67 * Not used in this implementation, parse functionality is provided by reader strategy.
68 */
69 protected Double parse(final String value)
70 {
71 return null;
72 }
73
74 /** {@inheritDoc} */
75 public final Matrix2D<Double> read(final InputStream inputStream) throws IOException
76 {
77 if (inputStream == null)
78 {
79 throw new IllegalArgumentException ("inputStream must not be null");
80 }
81 int lineNumber = 0;
82 BufferedReader reader = null;
83 ReaderStrategy readerStrategy = null;
84 Matrix2D<Double> matrix = null;
85 try
86 {
87 reader = new BufferedReader(new InputStreamReader(inputStream));
88 while (reader.ready())
89 {
90 String line = reader.readLine();
91 if (line.startsWith("%"))
92 {
93 Matcher m = HEADER.matcher(line);
94 if (m.matches())
95 {
96 String format = m.group(1); // coordinate, array
97 if (!("coordinate".equals(format)))
98 {
99 throw new IOException("header line format must be coordinate, was " + format);
100 }
101 String type = m.group(2); // real, complex, integer, pattern
102 if (!("real".equals(type) || "integer".equals(type)))
103 {
104 throw new IOException("header line type must be real or integer, was " + type);
105 }
106 String symmetryStructure = m.group(3); // general, symmetric, skew-symmetric, hermitian
107 readerStrategy = STRATEGIES.get(symmetryStructure);
108 if (readerStrategy == null)
109 {
110 throw new IOException("header line symmetry structure must be one of: general, symmetric,"
111 + "skew-symmetric, or hermitian; was " + symmetryStructure);
112 }
113 }
114 }
115 else
116 {
117 String[] tokens = line.split("\\s+");
118 if (matrix == null)
119 {
120 long rows = Long.parseLong(tokens[0]);
121 long columns = Long.parseLong(tokens[1]);
122 int entries = Integer.parseInt(tokens[2]);
123 if (readerStrategy == null)
124 {
125 throw new IOException("read matrix size definition at line number " + lineNumber
126 + " before reading header line");
127 }
128 int cardinality = readerStrategy.cardinality(entries);
129 matrix = createMatrix2D(rows, columns, cardinality);
130 }
131 else
132 {
133 long row = Long.parseLong(tokens[0]);
134 long column = Long.parseLong(tokens[1]);
135 double value = Double.parseDouble(tokens[2]);
136 if (readerStrategy == null)
137 {
138 throw new IOException("read values at line number " + lineNumber
139 + " before reading header line");
140 }
141 // note: indices in the file are 1-based
142 readerStrategy.read(matrix, row - 1, column - 1, value);
143 }
144 }
145 lineNumber++;
146 }
147 }
148 catch (NumberFormatException e)
149 {
150 throw new IOException("caught NumberFormatException at line number " + lineNumber
151 + "\n" + e.getMessage());
152 // jdk 1.6+
153 //throw new IOException("caught NumberFormatException at line number " + lineNumber, e);
154 }
155 catch (IndexOutOfBoundsException e)
156 {
157 throw new IOException("caught IndexOutOfBoundsException at line number " + lineNumber
158 + "\n" + e.getMessage());
159 // jdk 1.6+
160 //throw new IOException("caught IndexOutOfBoundsException at line number " + lineNumber, e);
161 }
162 finally
163 {
164 MatrixIOUtils.closeQuietly(reader);
165 }
166 if (matrix == null)
167 {
168 throw new IOException("could not create create matrix, check header and first non-comment line");
169 }
170 return matrix;
171 }
172
173 /**
174 * Strategy for handling symmetry structure.
175 */
176 interface ReaderStrategy
177 {
178
179 /**
180 * Return the approximate cardinality of the matrix to create for the specified number
181 * of entries in the file.
182 *
183 * @param entries number of entries in the file
184 * @return the approximate cardinality of the matrix to create for the specified number
185 * of entries in the file
186 */
187 int cardinality(int entries);
188
189 /**
190 * Notify this reader strategy that the specified value was read for the specified
191 * matrix at the specified coordinates.
192 *
193 * @param matrix matrix
194 * @param row row
195 * @param column column
196 * @param value value
197 */
198 void read(Matrix2D<Double> matrix, long row, long column, double value);
199 }
200
201 /**
202 * Reader strategy for <code>general</code> symmetry structure.
203 */
204 private static class GeneralReaderStrategy implements ReaderStrategy
205 {
206 /** {@inheritDoc} */
207 public int cardinality(final int entries)
208 {
209 return entries;
210 }
211
212 /** {@inheritDoc} */
213 public void read(final Matrix2D<Double> matrix, final long row, final long column, final double value)
214 {
215 matrix.set(row, column, value);
216 }
217 }
218
219 /**
220 * Reader strategy for <code>symmetric</code> symmetry structure.
221 */
222 private static class SymmetricReaderStrategy implements ReaderStrategy
223 {
224 /** {@inheritDoc} */
225 public int cardinality(final int entries)
226 {
227 return (2 * entries);
228 }
229
230 /** {@inheritDoc} */
231 public void read(final Matrix2D<Double> matrix, final long row, final long column, final double value)
232 {
233 // TODO: what if an upper-right value is specified?
234 matrix.set(row, column, value);
235 // only set values on the diagonal once
236 if (row != column)
237 {
238 matrix.set(column, row, value);
239 }
240 }
241 }
242
243 /**
244 * Reader strategy for <code>skew-symmetric</code> symmetry structure.
245 */
246 private static class SkewSymmetricReaderStrategy implements ReaderStrategy
247 {
248 /** {@inheritDoc} */
249 public int cardinality(final int entries)
250 {
251 return (2 * entries);
252 }
253
254 /** {@inheritDoc} */
255 public void read(final Matrix2D<Double> matrix, final long row, final long column, final double value)
256 {
257 // TODO: what if an upper-right value is specified?
258 // diagonal entries are always zero
259 if (row != column)
260 {
261 // lower-left values should be specified
262 matrix.set(row, column, value);
263 // upper-right values are inverse
264 matrix.set(column, row, -1.0d * value);
265 }
266 }
267 }
268
269 /**
270 * Reader strategy for <code>hermitian</code> symmetry structure.
271 */
272 private static class HermitianReaderStrategy implements ReaderStrategy
273 {
274 /** {@inheritDoc} */
275 public int cardinality(final int entries)
276 {
277 return entries;
278 }
279
280 /** {@inheritDoc} */
281 public void read(final Matrix2D<Double> matrix, final long row, final long column, final double value)
282 {
283 matrix.set(row, column, value);
284 matrix.set(column, row, value);
285 }
286 }
287 }