001/*
002 * @author Francesco.Kriegel@gmx.de
003 */
004package conexp.fx.core.math;
005
006/*
007 * #%L
008 * Concept Explorer FX
009 * %%
010 * Copyright (C) 2010 - 2023 Francesco Kriegel
011 * %%
012 * This program is free software: you can redistribute it and/or modify
013 * it under the terms of the GNU General Public License as
014 * published by the Free Software Foundation, either version 3 of the
015 * License, or (at your option) any later version.
016 * 
017 * This program is distributed in the hope that it will be useful,
018 * but WITHOUT ANY WARRANTY; without even the implied warranty of
019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
020 * GNU General Public License for more details.
021 * 
022 * You should have received a copy of the GNU General Public
023 * License along with this program.  If not, see
024 * <http://www.gnu.org/licenses/gpl-3.0.html>.
025 * #L%
026 */
027
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Iterator;
031
032import org.ujmp.core.booleanmatrix.BooleanMatrix;
033import org.ujmp.core.booleanmatrix.BooleanMatrix2D;
034import org.ujmp.core.calculation.Calculation.Ret;
035
036public final class BooleanMatrices {
037
038  public static final BooleanMatrix clone(final BooleanMatrix m) {
039    final BooleanMatrix copy = BooleanMatrices.empty(m.getRowCount(), m.getColumnCount());
040    copy.or(Ret.ORIG, m);
041    return copy;
042//    The below return statement sometimes yields read-only matrices.
043//    return m.clone().toBooleanMatrix();
044  }
045
046  public static final BooleanMatrix empty(final long size) {
047    return empty(size, size);
048  }
049
050  public static final BooleanMatrix empty(final long rows, final long columns) {
051    return BooleanMatrix2D.Factory.zeros(rows, columns);
052  }
053
054  public static final BooleanMatrix full(final long size) {
055    return full(size, size);
056  }
057
058  public static final BooleanMatrix full(final long rows, final long columns) {
059    return complement(empty(rows, columns));
060  }
061
062  public static final BooleanMatrix identity(final long size) {
063    final BooleanMatrix m = empty(size);
064    for (int i = 0; i < size; i++)
065      m.setBoolean(true, i, i);
066    return m;
067  }
068
069  public static final BooleanMatrix negativeIdentity(final long size) {
070    return complement(identity(size));
071  }
072
073  public static final BooleanMatrix lowerDiagonal(final long size) {
074    return dual(upperDiagonal(size));
075  }
076
077  public static final BooleanMatrix upperDiagonal(final long size) {
078    final BooleanMatrix m = empty(size);
079    for (int i = 0; i < size; i++)
080      for (int j = i; j < size; j++)
081        m.setBoolean(true, i, j);
082    return m;
083  }
084
085  public static final BooleanMatrix strictLowerDiagonal(final long size) {
086    return complement(upperDiagonal(size));
087  }
088
089  public static final BooleanMatrix strictUpperDiagonal(final long size) {
090    return complement(lowerDiagonal(size));
091  }
092
093  public static final BooleanMatrix apposition(final BooleanMatrix... ms) {
094    if (ms.length == 0)
095      return null;
096    BooleanMatrix m = ms[0];
097    for (int i = 1; i < ms.length; i++)
098      m = apposition(m, ms[i]);
099    return m;
100  }
101
102  public static final BooleanMatrix apposition(final BooleanMatrix left, final BooleanMatrix right) {
103    if (left == null)
104      return (BooleanMatrix) right.clone();
105    if (right == null)
106      return (BooleanMatrix) left.clone();
107    return (BooleanMatrix) left.appendHorizontally(Ret.NEW, right);
108  }
109
110  public static final BooleanMatrix subposition(final BooleanMatrix... ms) {
111    if (ms.length == 0)
112      return null;
113    BooleanMatrix m = ms[0];
114    for (int i = 1; i < ms.length; i++)
115      m = subposition(m, ms[i]);
116    return m;
117  }
118
119  public static final BooleanMatrix subposition(final BooleanMatrix upper, final BooleanMatrix lower) {
120    if (upper == null)
121      return (BooleanMatrix) lower.clone();
122    if (lower == null)
123      return (BooleanMatrix) upper.clone();
124    return (BooleanMatrix) upper.appendVertically(Ret.NEW, lower);
125  }
126
127  public static final BooleanMatrix quadPosition(
128      final BooleanMatrix leftUpper,
129      final BooleanMatrix rightUpper,
130      final BooleanMatrix leftLower,
131      final BooleanMatrix rightLower) {
132    return subposition(apposition(leftUpper, rightUpper), apposition(leftLower, rightLower));
133  }
134
135  public static final BooleanMatrix complement(final BooleanMatrix m) {
136    return (BooleanMatrix) m.not(Ret.NEW);
137  }
138
139  public static final BooleanMatrix dual(final BooleanMatrix m) {
140    return (BooleanMatrix) m.transpose(Ret.NEW);
141  }
142
143  public static final BooleanMatrix booleann(final long size) {
144    if (size < 0)
145      return null;
146    else if (size == 0)
147      return identity(1);
148    final BooleanMatrix m = booleann(size - 1);
149    return subposition(apposition(m, m), apposition(empty((long) Math.pow(2, size - 1)), m));
150  }
151
152  public static final BooleanMatrix directSum(final BooleanMatrix leftUpper, final BooleanMatrix rightLower) {
153    return subposition(
154        apposition(leftUpper, full(leftUpper.getRowCount(), rightLower.getColumnCount())),
155        apposition(full(rightLower.getRowCount(), leftUpper.getColumnCount()), rightLower));
156  }
157
158  public static final BooleanMatrix horizontalSum(final BooleanMatrix leftUpper, final BooleanMatrix rightLower) {
159    return subposition(
160        apposition(leftUpper, empty(leftUpper.getRowCount(), rightLower.getColumnCount())),
161        apposition(empty(rightLower.getRowCount(), leftUpper.getColumnCount()), rightLower));
162  }
163
164  public static final BooleanMatrix verticalSum(final BooleanMatrix leftUpper, final BooleanMatrix rightLower) {
165    return subposition(
166        apposition(leftUpper, full(leftUpper.getRowCount(), rightLower.getColumnCount())),
167        apposition(empty(rightLower.getRowCount(), leftUpper.getColumnCount()), rightLower));
168  }
169
170  public static final BooleanMatrix substitutionSum(
171      final BooleanMatrix outer,
172      final BooleanMatrix inner,
173      final long row,
174      final long column,
175      final Collection<Integer> gI,
176      final Collection<Integer> mI) {
177    final BooleanMatrix lu, ru, ll, rl;
178    // ( G\{g}, M\{m}, I )
179    lu = outer;
180    System.out.println(lu);
181    // ( H, N, J )
182    rl = inner;
183    System.out.println(rl);
184    // ( G\{g}, N, (m'\{g}) x N )
185    ru = empty(lu.getRowCount(), rl.getColumnCount());
186    ru.selectRows(Ret.LINK, mI).not(Ret.ORIG);
187    System.out.println(ru);
188    // ( H, M\{m}, H x (g'\{m}) )
189    ll = empty(rl.getRowCount(), lu.getColumnCount());
190    ll.selectColumns(Ret.LINK, gI).not(Ret.ORIG);
191    System.out.println(ll);
192    return quadPosition(lu, ru, ll, rl).deleteRows(Ret.NEW, row).deleteColumns(Ret.NEW, column).toBooleanMatrix();
193  }
194
195  public static final BooleanMatrix directProduct(final BooleanMatrix m1, final BooleanMatrix m2) {
196    return (BooleanMatrix) scale(m1, m2.getRowCount(), m2.getColumnCount())
197        .or(Ret.NEW, duplicate(m2, m1.getRowCount(), m1.getColumnCount()));
198  }
199
200  public static final BooleanMatrix biProduct(final BooleanMatrix m1, final BooleanMatrix m2) {
201    return (BooleanMatrix) scale(m1, m2.getRowCount(), m2.getColumnCount())
202        .and(Ret.NEW, duplicate(m2, m1.getRowCount(), m1.getColumnCount()));
203  }
204
205  public static final BooleanMatrix semiProduct(final BooleanMatrix m1, final BooleanMatrix m2) {
206    return apposition(scaleV(m1, m2.getRowCount()), duplicateV(m2, m1.getRowCount()));
207  }
208
209  private static final BooleanMatrix duplicate(final BooleanMatrix m, final long rowFactor, final long columnFactor) {
210    return duplicateV(duplicateH(m, columnFactor), rowFactor);
211  }
212
213  private static final BooleanMatrix duplicateH(final BooleanMatrix m, final long columnFactor) {
214    BooleanMatrix h = null;
215    for (int i = 0; i < columnFactor; i++)
216      h = apposition(h, m);
217    return h;
218  }
219
220  private static final BooleanMatrix duplicateV(final BooleanMatrix m, final long rowFactor) {
221    BooleanMatrix v = null;
222    for (int i = 0; i < rowFactor; i++)
223      v = subposition(v, m);
224    return v;
225  }
226
227  private static final BooleanMatrix scale(final BooleanMatrix m, final long rowFactor, final long columnFactor) {
228    return scaleV(scaleH(m, columnFactor), rowFactor);
229  }
230
231  private static final BooleanMatrix scaleH(final BooleanMatrix m, final long columnFactor) {
232    BooleanMatrix h = null;
233    for (long j = 0; j < m.getColumnCount(); j++)
234      for (int f = 0; f < columnFactor; f++)
235        h = apposition(h, (BooleanMatrix) m.selectColumns(Ret.NEW, j));
236    return h;
237  }
238
239  private static final BooleanMatrix scaleV(final BooleanMatrix m, final long rowFactor) {
240    BooleanMatrix v = null;
241    for (long i = 0; i < m.getRowCount(); i++)
242      for (int f = 0; f < rowFactor; f++)
243        v = subposition(v, (BooleanMatrix) m.selectRows(Ret.NEW, i));
244    return v;
245  }
246
247  public static final BooleanMatrix andCol(final BooleanMatrix m, final Iterable<Integer> columns) {
248//    return StreamSupport.stream(columns.spliterator(), false).map(column -> m.selectColumns(Ret.NEW, column)).collect(
249//        () -> full(m.getRowCount(), 1),
250//        (x, y) -> x.and(Ret.ORIG, y),
251//        (x, y) -> x.and(Ret.ORIG, y));
252    final Iterator<Integer> it = columns.iterator();
253    if (it.hasNext()) {
254      final BooleanMatrix andCol = (BooleanMatrix) m.selectColumns(Ret.NEW, it.next());
255      while (it.hasNext())
256        andCol.and(Ret.ORIG, m.selectColumns(Ret.LINK, it.next()));
257      return andCol;
258    }
259    return full(m.getRowCount(), 1);
260  }
261
262  public static final BooleanMatrix andRow(final BooleanMatrix m, final Iterable<Integer> rows) {
263//    return StreamSupport.stream(rows.spliterator(), false).map(row -> m.selectRows(Ret.NEW, row)).collect(
264//        () -> full(1, m.getColumnCount()),
265//        (x, y) -> x.and(Ret.ORIG, y),
266//        (x, y) -> x.and(Ret.ORIG, y));
267    final Iterator<Integer> it = rows.iterator();
268    if (it.hasNext()) {
269      final BooleanMatrix andRow = (BooleanMatrix) m.selectRows(Ret.NEW, it.next());
270      while (it.hasNext())
271        andRow.and(Ret.ORIG, m.selectRows(Ret.LINK, it.next()));
272      return andRow;
273    }
274    return full(1, m.getColumnCount());
275  }
276
277  public static final BooleanMatrix andCol(final BooleanMatrix m, final Integer... columns) {
278    return andCol(m, Arrays.<Integer> asList(columns));
279  }
280
281  public static final BooleanMatrix andRow(final BooleanMatrix m, final Integer... rows) {
282    return andRow(m, Arrays.<Integer> asList(rows));
283  }
284
285  private static final boolean isSquare(final BooleanMatrix m) {
286    return m.getRowCount() == m.getColumnCount();
287  }
288
289  public static final BooleanMatrix product(final BooleanMatrix m1, final BooleanMatrix m2) {
290    return m1.mtimes(Ret.NEW, false, m2).toBooleanMatrix();
291  }
292
293  public static final BooleanMatrix power(final BooleanMatrix m, final int n) {
294    if (!isSquare(m))
295      throw new IllegalArgumentException();
296    if (n < 0)
297      throw new IllegalArgumentException();
298    if (n == 0)
299      return identity(m.getRowCount());
300    if (n == 1)
301      return clone(m);
302    if (n == 2)
303      return product(m, m);
304    return product(power(m, n - 1), m);
305  }
306
307  public static final BooleanMatrix reflexiveClosure(final BooleanMatrix m) {
308    if (!isSquare(m))
309      return null;
310    return m.or(Ret.NEW, identity(m.getRowCount())).toBooleanMatrix();
311  }
312
313  public static final BooleanMatrix reflexiveReduction(final BooleanMatrix m) {
314    if (!isSquare(m))
315      return null;
316    return m.and(Ret.NEW, negativeIdentity(m.getRowCount())).toBooleanMatrix();
317  }
318
319  public static final BooleanMatrix transitiveClosure(final BooleanMatrix m) {
320    if (!isSquare(m))
321      return null;
322    BooleanMatrix t = clone(m);
323    BooleanMatrix power = clone(m);
324    BooleanMatrix last = null;
325    do {
326      power = product(power, m);
327      last = clone(t);
328      t = t.or(Ret.NEW, power).toBooleanMatrix();
329    } while (!t.equals(last));
330    return t;
331  }
332
333  public static final BooleanMatrix transitiveReduction(final BooleanMatrix m) {
334    if (!isSquare(m))
335      return null;
336    return m.and(Ret.NEW, m.mtimes(Ret.NEW, false, transitiveClosure(m)).not(Ret.NEW)).toBooleanMatrix();
337  }
338}