View Javadoc

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 }