001/** 002 * @author Francesco.Kriegel@gmx.de 003 */ 004package conexp.fx.core.context; 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.Collections; 031import java.util.HashSet; 032import java.util.Set; 033import java.util.stream.Collectors; 034import java.util.stream.Stream; 035 036import org.ujmp.core.Matrix; 037import org.ujmp.core.booleanmatrix.BooleanMatrix; 038import org.ujmp.core.calculation.Calculation.Ret; 039 040import com.google.common.base.Function; 041import com.google.common.base.Functions; 042import com.google.common.base.Predicate; 043import com.google.common.base.Predicates; 044import com.google.common.collect.Collections2; 045import com.google.common.collect.Sets; 046import com.google.common.primitives.Ints; 047 048import conexp.fx.core.algorithm.nextclosure.NextConcept; 049import conexp.fx.core.collections.Collections3; 050import conexp.fx.core.collections.Pair; 051import conexp.fx.core.collections.relation.AbstractRelation; 052import conexp.fx.core.collections.relation.MatrixRelation; 053import conexp.fx.core.collections.relation.Relation; 054import conexp.fx.core.collections.relation.RelationEvent; 055import conexp.fx.core.collections.setlist.HashSetArrayList; 056import conexp.fx.core.collections.setlist.SetList; 057import conexp.fx.core.collections.setlist.SetLists; 058import conexp.fx.core.math.BooleanMatrices; 059import conexp.fx.core.math.GuavaIsomorphism; 060import conexp.fx.core.util.Constants; 061import javafx.beans.property.SimpleStringProperty; 062import javafx.beans.property.StringProperty; 063 064public class MatrixContext<G, M> extends MatrixRelation<G, M> implements Context<G, M> { 065 066 public enum Incidence { 067 CROSS(Constants.CROSS_CHARACTER), 068 DOWN_ARROW(Constants.DOWN_ARROW_CHARACTER), 069 UP_ARROW(Constants.UP_ARROW_CHARACTER), 070 BOTH_ARROW(Constants.BOTH_ARROW_CHARACTER), 071 DOWN_PATH(Constants.DOUBLE_DOWN_ARROW_CHARACTER), 072 UP_PATH(Constants.DOUBLE_UP_ARROW_CHARACTER), 073 BOTH_PATH(Constants.DOUBLE_BOTH_ARROW_CHARACTER), 074 NO_CROSS(Constants.NO_CROSS_CHARACTER); 075 076 private final String character; 077 078 private Incidence(final String character) { 079 this.character = character; 080 } 081 082 public final String toString() { 083 return character; 084 } 085 } 086 087 public final StringProperty id = new SimpleStringProperty(null); 088 public MatrixContext<G, M> selection = this; 089 public final SetList<Set<Integer>> _objects = new HashSetArrayList<Set<Integer>>(); 090 public final SetList<Set<Integer>> _attributes = new HashSetArrayList<Set<Integer>>(); 091 private final Collection<Integer> i = 092 Collections2.transform(_objects, Collections3.<Integer> firstElement()); 093 private final Collection<Integer> j = 094 Collections2.transform(_attributes, Collections3.<Integer> firstElement()); 095 public final Context<Set<Integer>, Set<Integer>> _cleaned = 096 new AbstractContext<Set<Integer>, Set<Integer>>(_objects, _attributes, false) { 097 098 @SuppressWarnings("unchecked") 099 public final boolean contains( 100 final Object _i, 101 final Object _j) { 102 return matrix.getBoolean( 103 (long) Collections3 104 .firstElement( 105 (Set<Integer>) _i), 106 (long) Collections3 107 .firstElement( 108 (Set<Integer>) _j)); 109 } 110 111 public final 112 MatrixContext<Set<Integer>, Set<Integer>> 113 clone() { 114 return new MatrixContext<Set<Integer>, Set<Integer>>( 115 _objects, 116 _attributes, 117 (BooleanMatrix) matrix 118 .selectRows(Ret.NEW, i) 119 .selectColumns( 120 Ret.NEW, 121 j), 122 false); 123 } 124 }; 125 public Relation<Set<Integer>, Set<Integer>> _downArrows = 126 new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) { 127 128 public final boolean contains( 129 final Object object, 130 final Object attribute) { 131 @SuppressWarnings("unchecked") 132 final int rowIndex = 133 Collections3.firstElement( 134 (Set<Integer>) object); 135 @SuppressWarnings("unchecked") 136 final int columnIndex = 137 Collections3.firstElement( 138 (Set<Integer>) attribute); 139 if (!matrix.getBoolean( 140 rowIndex, 141 columnIndex)) 142 return _col(columnIndex, i) 143 .containsAll( 144 _extent( 145 Collections 146 .singleton( 147 rowIndex), 148 Collections2.filter( 149 i, 150 Predicates.not( 151 Predicates 152 .equalTo( 153 rowIndex))), 154 j)); 155 return false; 156 } 157 }; 158 public Relation<Set<Integer>, Set<Integer>> _upArrows = 159 new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) { 160 161 public final boolean contains( 162 final Object object, 163 final Object attribute) { 164 @SuppressWarnings("unchecked") 165 final int _i = 166 Collections3.firstElement( 167 (Set<Integer>) object); 168 @SuppressWarnings("unchecked") 169 final int _j = 170 Collections3.firstElement( 171 (Set<Integer>) attribute); 172 if (!matrix.getBoolean(_i, _j)) 173 return _row(_i, j).containsAll( 174 _intent( 175 Collections 176 .singleton(_j), 177 i, 178 Collections2.filter( 179 j, 180 Predicates.not( 181 Predicates 182 .equalTo( 183 _j))))); 184 return false; 185 } 186 };; 187 public Relation<Set<Integer>, Set<Integer>> _downPaths; 188 public Relation<Set<Integer>, Set<Integer>> _upPaths; 189 private final Predicate<Set<Integer>> _isIrreducibleObject = new Predicate<Set<Integer>>() { 190 191 public final boolean 192 apply(final Set<Integer> i) { 193 for (Set<Integer> j : _attributes) 194 if (_downArrows.contains(i, j)) 195 return true; 196 return false; 197 } 198 }; 199 private final Predicate<Set<Integer>> _isIrreducibleAttribute = new Predicate<Set<Integer>>() { 200 201 public final boolean 202 apply(final Set<Integer> j) { 203 for (Set<Integer> i : _objects) 204 if (_upArrows.contains(i, j)) 205 return true; 206 return false; 207 } 208 }; 209 public final SetList<Set<Integer>> _irreducibleObjects = new HashSetArrayList<Set<Integer>>(); 210 public final SetList<Set<Integer>> _irreducibleAttributes = new HashSetArrayList<Set<Integer>>(); 211 public final Context<Set<Integer>, Set<Integer>> _reduced = 212 new AbstractContext<Set<Integer>, Set<Integer>>(_irreducibleObjects, _irreducibleAttributes, false) { 213 214 @SuppressWarnings("unchecked") 215 public final boolean contains( 216 final Object _i, 217 final Object _j) { 218 return matrix.getBoolean( 219 (long) Collections3 220 .firstElement( 221 (Set<Integer>) _i), 222 (long) Collections3 223 .firstElement( 224 (Set<Integer>) _j)); 225 } 226 227 public final 228 MatrixContext<Set<Integer>, Set<Integer>> 229 clone() { 230 return new MatrixContext<Set<Integer>, Set<Integer>>( 231 _irreducibleObjects, 232 _irreducibleAttributes, 233 (BooleanMatrix) matrix 234 .selectRows( 235 Ret.NEW, 236 Collections2 237 .transform( 238 _irreducibleObjects, 239 Collections3 240 .<Integer> firstElement())) 241 .selectColumns( 242 Ret.NEW, 243 Collections2 244 .transform( 245 _irreducibleAttributes, 246 Collections3 247 .<Integer> firstElement())), 248 false); 249 } 250 };; 251 public final Function<Iterable<Integer>, G> _firstObject = 252 Functions.compose(rowHeads.indexGuava().inverse(), Collections3.<Integer> firstElement()); 253 public final Function<Iterable<Integer>, M> _firstAttribute = 254 Functions.compose(colHeads.indexGuava().inverse(), Collections3.<Integer> firstElement()); 255 public final GuavaIsomorphism<Set<Integer>, Set<G>> _allObjects = 256 new GuavaIsomorphism<Set<Integer>, Set<G>>() { 257 258 public final Set<G> 259 apply(final Set<Integer> set) { 260 return Collections3.transform( 261 set, 262 GuavaIsomorphism.invert( 263 rowHeads().indexGuava())); 264 } 265 266 public Set<Integer> 267 invert(final Set<G> set) { 268 return Collections3.transform( 269 set, 270 rowHeads().indexGuava()); 271 } 272 }; 273 public final GuavaIsomorphism<Set<Integer>, Set<M>> _allAttributes = 274 new GuavaIsomorphism<Set<Integer>, Set<M>>() { 275 276 public final Set<M> 277 apply(final Set<Integer> set) { 278 return Collections3.transform( 279 set, 280 GuavaIsomorphism.invert( 281 colHeads().indexGuava())); 282 } 283 284 public Set<Integer> 285 invert(final Set<M> set) { 286 return Collections3.transform( 287 set, 288 colHeads().indexGuava()); 289 } 290 }; 291 public final AbstractContext<Set<G>, Set<M>> cleaned = new AbstractContext<Set<G>, Set<M>>( 292 SetLists.transform(_objects, _allObjects), 293 SetLists.transform(_attributes, _allAttributes), 294 false) { 295 296 @SuppressWarnings("unchecked") 297 public boolean 298 contains(Object o1, Object o2) { 299 return _cleaned.contains( 300 _allObjects.invert((Set<G>) o1), 301 _allAttributes 302 .invert((Set<M>) o2)); 303 } 304 305 public MatrixContext<Set<G>, Set<M>> 306 clone() { 307 return new MatrixContext<Set<G>, Set<M>>( 308 this.rowHeads().clone(), 309 this.colHeads().clone(), 310 _cleaned.clone().matrix, 311 false); 312 }; 313 }; 314 public final AbstractContext<Set<G>, Set<M>> reduced = new AbstractContext<Set<G>, Set<M>>( 315 SetLists.transform(_irreducibleObjects, _allObjects), 316 SetLists.transform(_irreducibleAttributes, _allAttributes), 317 false) { 318 319 @SuppressWarnings("unchecked") 320 public boolean 321 contains(Object o1, Object o2) { 322 return _reduced.contains( 323 _allObjects.invert((Set<G>) o1), 324 _allAttributes 325 .invert((Set<M>) o2)); 326 } 327 }; 328 public final AbstractRelation<Set<G>, Set<M>> downArrows = new AbstractRelation<Set<G>, Set<M>>( 329 SetLists.transform(_objects, _allObjects), 330 SetLists.transform(_attributes, _allAttributes), 331 false) { 332 333 @SuppressWarnings("unchecked") 334 public boolean 335 contains(Object o1, Object o2) { 336 return _downArrows.contains( 337 _allObjects.invert((Set<G>) o1), 338 _allAttributes 339 .invert((Set<M>) o2)); 340 } 341 }; 342 public final AbstractRelation<Set<G>, Set<M>> upArrows = new AbstractRelation<Set<G>, Set<M>>( 343 SetLists.transform(_objects, _allObjects), 344 SetLists.transform(_attributes, _allAttributes), 345 false) { 346 347 @SuppressWarnings("unchecked") 348 public boolean 349 contains(Object o1, Object o2) { 350 return _upArrows.contains( 351 _allObjects.invert((Set<G>) o1), 352 _allAttributes 353 .invert((Set<M>) o2)); 354 } 355 }; 356 public final AbstractRelation<G, M> DownArrows = 357 new AbstractRelation<G, M>(rowHeads, colHeads, false) { 358 359 public boolean 360 contains(Object o1, Object o2) { 361 return _downArrows.contains( 362 _objectEquivalence().col( 363 rowHeads().indexOf(o1)), 364 _attributeEquivalence().col( 365 colHeads().indexOf(o2))); 366 } 367 }; 368 public final AbstractRelation<G, M> UpArrows = 369 new AbstractRelation<G, M>(rowHeads, colHeads, false) { 370 371 public boolean 372 contains(Object o1, Object o2) { 373 return _upArrows.contains( 374 _objectEquivalence().col( 375 rowHeads().indexOf(o1)), 376 _attributeEquivalence().col( 377 colHeads().indexOf(o2))); 378 } 379 }; 380 public final AbstractRelation<Set<G>, Set<M>> downPaths = new AbstractRelation<Set<G>, Set<M>>( 381 SetLists.transform(_objects, _allObjects), 382 SetLists.transform(_attributes, _allAttributes), 383 false) { 384 385 @SuppressWarnings("unchecked") 386 public boolean 387 contains(Object o1, Object o2) { 388 return _downPaths.contains( 389 _allObjects.invert((Set<G>) o1), 390 _allAttributes 391 .invert((Set<M>) o2)); 392 } 393 }; 394 public final AbstractRelation<Set<G>, Set<M>> upPaths = new AbstractRelation<Set<G>, Set<M>>( 395 SetLists.transform(_objects, _allObjects), 396 SetLists.transform(_attributes, _allAttributes), 397 false) { 398 399 @SuppressWarnings("unchecked") 400 public boolean 401 contains(Object o1, Object o2) { 402 return _upPaths.contains( 403 _allObjects.invert((Set<G>) o1), 404 _allAttributes 405 .invert((Set<M>) o2)); 406 } 407 }; 408 public final AbstractRelation<G, M> DownPaths = 409 new AbstractRelation<G, M>(rowHeads, colHeads, false) { 410 411 public boolean 412 contains(Object o1, Object o2) { 413 return _downPaths.contains( 414 _objectEquivalence().col( 415 rowHeads().indexOf(o1)), 416 _attributeEquivalence().col( 417 colHeads().indexOf(o2))); 418 } 419 }; 420 public final AbstractRelation<G, M> UpPaths = 421 new AbstractRelation<G, M>(rowHeads, colHeads, false) { 422 423 public boolean 424 contains(Object o1, Object o2) { 425 return _upPaths.contains( 426 _objectEquivalence().col( 427 rowHeads().indexOf(o1)), 428 _attributeEquivalence().col( 429 colHeads().indexOf(o2))); 430 } 431 }; 432 private final Set<M> ignoredAttributes = new HashSet<M>(); 433 private final Set<G> ignoredObjects = new HashSet<G>(); 434 435 public MatrixContext(final boolean homogen) { 436 super(homogen); 437 initHandlers(true, MatrixContext.AutomaticMode.REDUCE); 438 } 439 440 public MatrixContext(final SetList<G> objects, final SetList<M> attributes, final boolean homogen) { 441 super(objects, attributes, homogen); 442 initHandlers(true, MatrixContext.AutomaticMode.REDUCE); 443 } 444 445 public MatrixContext( 446 final SetList<G> objects, 447 final SetList<M> attributes, 448 final BooleanMatrix matrix, 449 final boolean homogen) { 450 super(objects, attributes, matrix, homogen); 451 initHandlers(true, MatrixContext.AutomaticMode.REDUCE); 452 } 453 454 public MatrixContext(final boolean homogen, final MatrixContext.AutomaticMode automaticMode) { 455 super(homogen); 456 initHandlers(true, automaticMode); 457 } 458 459 public MatrixContext( 460 final SetList<G> objects, 461 final SetList<M> attributes, 462 final boolean homogen, 463 final MatrixContext.AutomaticMode automaticMode) { 464 super(objects, attributes, homogen); 465 initHandlers(true, automaticMode); 466 } 467 468 public MatrixContext( 469 final SetList<G> objects, 470 final SetList<M> attributes, 471 final BooleanMatrix matrix, 472 final boolean homogen, 473 final MatrixContext.AutomaticMode automaticMode) { 474 super(objects, attributes, matrix, homogen); 475 initHandlers(true, automaticMode); 476 } 477 478 private boolean lock = false; 479 480 public final void lock() { 481 lock = true; 482 } 483 484 public final void unlock() { 485 lock = false; 486 } 487 488 public enum AutomaticMode { 489 NONE, 490 CLEAN, 491 REDUCE; 492 493 public final static AutomaticMode fromSize(final int objs, final int atts) { 494 if (objs > 1000 || atts > 1000) 495 return NONE; 496 else if (objs * atts > 10000) 497 return CLEAN; 498 return REDUCE; 499 } 500 } 501 502 public void initHandlers(final boolean selfSelecting, final MatrixContext.AutomaticMode auto) { 503 if (selfSelecting) { 504 addEventHandler(__ -> select(), RelationEvent.SELECTION_CHANGED); 505 select(); 506 } 507 switch (auto) { 508 case REDUCE: 509 addEventHandler(__ -> { 510 if (!lock) { 511 reduce(); 512 } 513 }, RelationEvent.ALL_CHANGED, RelationEvent.ENTRIES); 514 reduce(); 515 break; 516 case CLEAN: 517 addEventHandler(__ -> { 518 if (!lock) { 519 clean(); 520 } 521 }, RelationEvent.ALL_CHANGED, RelationEvent.ENTRIES); 522 clean(); 523 break; 524 case NONE: 525 default: 526 } 527 } 528 529 public synchronized final void select() { 530 final SetList<M> selectedColHeads = SetLists.difference(colHeads, ignoredAttributes).clone(); 531 final SetList<G> selectedRowHeads = SetLists.difference(rowHeads, ignoredObjects).clone(); 532 if (ignoredAttributes.isEmpty() && ignoredObjects.isEmpty()) { 533 selection = this; 534 reduce(); 535 } else if (selectedColHeads.isEmpty() && selectedRowHeads.isEmpty()) 536 selection = new MatrixContext<G, M>(false); 537 else 538 selection = new MatrixContext<G, M>( 539 selectedRowHeads, 540 selectedColHeads, 541 (BooleanMatrix) matrix 542 .selectColumns(Ret.NEW, MatrixContext.this.colHeads().indicesOf(selectedColHeads, false)) 543 .selectRows(Ret.NEW, MatrixContext.this.rowHeads().indicesOf(selectedRowHeads, false)), 544 false); 545 } 546 547 public synchronized final void clean() { 548 _objects.clear(); 549 _objects.addAll(_objectEquivalence().equivalenceClasses()); 550 _attributes.clear(); 551 _attributes.addAll(_attributeEquivalence().equivalenceClasses()); 552 } 553 554 public synchronized final void reduce() { 555 clean(); 556 _downArrows = new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) { 557 558 public final boolean contains(final Object object, final Object attribute) { 559 @SuppressWarnings("unchecked") 560 final int rowIndex = Collections3.firstElement((Set<Integer>) object); 561 @SuppressWarnings("unchecked") 562 final int columnIndex = Collections3.firstElement((Set<Integer>) attribute); 563 if (!matrix.getBoolean(rowIndex, columnIndex)) 564 return _col(columnIndex, i).containsAll( 565 _extent( 566 Collections.singleton(rowIndex), 567 Collections2.filter(i, Predicates.not(Predicates.equalTo(rowIndex))), 568 j)); 569 return false; 570 } 571 };// .clone(); 572 _upArrows = new AbstractRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, false) { 573 574 public final boolean contains(final Object object, final Object attribute) { 575 @SuppressWarnings("unchecked") 576 final int _i = Collections3.firstElement((Set<Integer>) object); 577 @SuppressWarnings("unchecked") 578 final int _j = Collections3.firstElement((Set<Integer>) attribute); 579 if (!matrix.getBoolean(_i, _j)) 580 return _row(_i, j).containsAll( 581 _intent(Collections.singleton(_j), i, Collections2.filter(j, Predicates.not(Predicates.equalTo(_j))))); 582 return false; 583 } 584 };// .clone(); 585 _irreducibleObjects.clear(); 586 _irreducibleObjects.addAll(_objects.filter(_isIrreducibleObject)); 587 _irreducibleAttributes.clear(); 588 _irreducibleAttributes.addAll(_attributes.filter(_isIrreducibleAttribute).clone()); 589// try { 590// final int rows = _objects.size(); 591// final int cols = _attributes.size(); 592// if (rows == 0 || cols == 0) 593// return; 594// @SuppressWarnings("deprecation") 595// final BooleanMatrix arrowPaths = 596// BooleanMatrices.transitiveClosure(BooleanMatrices.reflexiveClosure(BooleanMatrices.quadPosition( 597// BooleanMatrices.empty(rows), 598// _downArrows.clone().matrix(), 599// BooleanMatrices.dual(_upArrows.clone().matrix()), 600// BooleanMatrices.empty(cols)))); 601// final BooleanMatrix downPaths = (BooleanMatrix) arrowPaths.subMatrix(Ret.NEW, 0, rows, rows - 1, rows + cols - 1); 602// final BooleanMatrix upPaths = 603// (BooleanMatrix) BooleanMatrices.dual((BooleanMatrix) arrowPaths.subMatrix( 604// Ret.NEW, 605// rows, 606// 0, 607// rows + cols - 1, 608// rows - 1)); 609// _downPaths = new MatrixRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, downPaths, false); 610// _upPaths = new MatrixRelation<Set<Integer>, Set<Integer>>(_objects, _attributes, upPaths, false); 611// } catch (Exception e) { 612// e.printStackTrace(); 613// } 614 } 615 616 public final Pair<MatrixContext.Incidence, MatrixContext.Incidence> 617 getValue(final G g, final M m, final boolean... withArrows) { 618 if (withArrows.length == 0 || withArrows[0]) { 619 MatrixContext.Incidence first; 620 MatrixContext.Incidence second; 621 boolean down = false; 622 boolean up = false; 623 if (contains(g, m)) 624 first = MatrixContext.Incidence.CROSS; 625 else { 626 down = DownArrows.contains(g, m); 627 up = UpArrows.contains(g, m); 628 if (down && up) 629 first = MatrixContext.Incidence.BOTH_ARROW; 630 else if (down) 631 first = MatrixContext.Incidence.DOWN_ARROW; 632 else if (up) 633 first = MatrixContext.Incidence.UP_ARROW; 634 else 635 first = MatrixContext.Incidence.NO_CROSS; 636 } 637 second = null; 638 if (withArrows.length == 0 || (withArrows.length > 1 && withArrows[1])) { 639 final boolean Down = DownPaths.contains(g, m); 640 final boolean Up = UpPaths.contains(g, m); 641 if (up || down) 642 second = null; 643 else if (Down && Up) 644 second = MatrixContext.Incidence.BOTH_PATH; 645 else if (Down) 646 second = MatrixContext.Incidence.DOWN_PATH; 647 else if (Up) 648 second = MatrixContext.Incidence.UP_PATH; 649 } 650 return Pair.of(first, second); 651 } else { 652 if (contains(g, m)) 653 return Pair.of(MatrixContext.Incidence.CROSS, null); 654 else 655 return Pair.of(MatrixContext.Incidence.NO_CROSS, null); 656 } 657 } 658 659 public final Iterable<Concept<G, M>> concepts() { 660 return new NextConcept<G, M>(this); 661 } 662 663 public final Relation<G, G> objectQuasiOrder() { 664 return new AbstractRelation<G, G>(rowHeads(), rowHeads(), true) { 665 666 public final boolean contains(final Object object1, final Object object2) { 667 return !matrix 668 .selectRows(Ret.LINK, rowHeads().indexOf(object1)) 669 .ge(Ret.LINK, matrix.selectRows(Ret.LINK, rowHeads().indexOf(object2))) 670 .containsBoolean(false); 671 } 672 }; 673 } 674 675 public final Relation<M, M> attributeQuasiOrder() { 676 return new AbstractRelation<M, M>(colHeads(), colHeads(), true) { 677 678 public final boolean contains(final Object attribute1, final Object attribute2) { 679 return !matrix 680 .selectColumns(Ret.LINK, colHeads().indexOf(attribute1)) 681 .ge(Ret.LINK, matrix.selectColumns(Ret.LINK, colHeads().indexOf(attribute2))) 682 .containsBoolean(false); 683 } 684 }; 685 } 686 687 public final Relation<Integer, Integer> _objectEquivalence() { 688 return new AbstractRelation<Integer, Integer>( 689 SetLists.integers(MatrixContext.this.rowHeads().size()), 690 SetLists.integers(MatrixContext.this.rowHeads().size()), 691 true) { 692 693 public final boolean contains(final Object i1, final Object i2) { 694 return matrix.selectRows(Ret.LINK, (Integer) i1).equals(matrix.selectRows(Ret.LINK, (Integer) i2)); 695 } 696 697 public final Set<Integer> col(final Object i1) { 698 final Matrix row = matrix.selectRows(Ret.LINK, (Integer) i1); 699 return Stream 700 .iterate(0, Math::incrementExact) 701 .limit(MatrixContext.this.rowHeads.size()) 702 .parallel() 703 .filter(i2 -> row.equals(matrix.selectRows(Ret.LINK, i2))) 704 .collect(Collectors.toSet()); 705// return Collections3.fromIterator( 706// () -> Iterators.filter( 707// ListIterators.integers(0, MatrixContext.this.rowHeads.size()), 708// i2 -> row.equals(matrix.selectRows(Ret.LINK, i2)))); 709 } 710 711 public final Set<Integer> row(final Object i2) { 712 return col(i2); 713 } 714 }; 715 } 716 717 public final Relation<Integer, Integer> _attributeEquivalence() { 718 return new AbstractRelation<Integer, Integer>( 719 SetLists.integers(MatrixContext.this.colHeads().size()), 720 SetLists.integers(MatrixContext.this.colHeads().size()), 721 true) { 722 723 public final boolean contains(final Object j1, final Object j2) { 724 return matrix.selectColumns(Ret.LINK, (Integer) j1).equals(matrix.selectColumns(Ret.LINK, (Integer) j2)); 725 } 726 727 public final Set<Integer> col(final Object j1) { 728 final Matrix col = matrix.selectColumns(Ret.LINK, (Integer) j1); 729 return Stream 730 .iterate(0, Math::incrementExact) 731 .limit(MatrixContext.this.colHeads.size()) 732 .parallel() 733 .filter(j2 -> col.equals(matrix.selectColumns(Ret.LINK, j2))) 734 .collect(Collectors.toSet()); 735// return Collections3.fromIterator( 736// () -> Iterators.filter( 737// ListIterators.integers(0, MatrixContext.this.colHeads.size()), 738// j2 -> col.equals(matrix.selectColumns(Ret.LINK, j2)))); 739 } 740 741 public final Set<Integer> row(final Object i2) { 742 return col(i2); 743 } 744 }; 745 } 746 747 public final Set<M> infimumIrreducibles() { 748 return new HashSet<M>(SetLists.transform(_irreducibleAttributes, _firstAttribute)); 749 } 750 751 public final Set<G> supremumIrreducibles() { 752 return new HashSet<G>(SetLists.transform(_irreducibleObjects, _firstObject)); 753 } 754 755// public final boolean downArrow(final Object object, final Object attribute) { 756// return _downArrows.contains( 757// _objectEquivalence().col(rowHeads().indexOf(object)), 758// _attributeEquivalence().col(colHeads().indexOf(attribute))); 759// } 760// 761// public final boolean upArrow(final Object object, final Object attribute) { 762// return _upArrows.contains( 763// _objectEquivalence().col(rowHeads().indexOf(object)), 764// _attributeEquivalence().col(colHeads().indexOf(attribute))); 765// } 766// public final boolean _downArrow(final int rowIndex, final int columnIndex) { 767// return _downArrows.contains(_objectEquivalence().col(rowIndex), _attributeEquivalence().col(columnIndex)); 768// } 769// 770// public final boolean _upArrow(final int rowIndex, final int columnIndex) { 771// return _upArrows.contains(_objectEquivalence().col(rowIndex), _attributeEquivalence().col(columnIndex)); 772// } 773 public final Set<G> extent(final Object... objects) { 774 return extent(Arrays.asList(objects)); 775 } 776 777 public final Set<M> intent(final Object... attributes) { 778 return intent(Arrays.asList(attributes)); 779 } 780 781 public final Set<G> extent(final Collection<?> objects) { 782 return Collections3 783 .fromIterator(() -> rowHeads.getAll(_extent(rowHeads.indicesOf(objects, true)), true).iterator()); 784 } 785 786 public final Set<M> intent(final Collection<?> attributes) { 787 return Collections3 788 .fromIterator(() -> colHeads.getAll(_intent(colHeads.indicesOf(attributes, true)), true).iterator()); 789 } 790 791 public final Collection<Integer> _extent(final int... rowIndices) { 792 return _extent(Ints.asList(rowIndices)); 793 } 794 795 public final Collection<Integer> _intent(final int... columnIndices) { 796 return _intent(Ints.asList(columnIndices)); 797 } 798 799 public final Collection<Integer> _extent(final Iterable<Integer> rowIndices) { 800 return _colAnd(_rowAnd(rowIndices)); 801 } 802 803 public final Collection<Integer> _intent(final Iterable<Integer> columnIndices) { 804 return _rowAnd(_colAnd(columnIndices)); 805 } 806 807 public final Collection<Integer> _extent( 808 final Collection<Integer> rowIndices, 809 final Collection<Integer> subRowIndices, 810 final Collection<Integer> subColumnIndices) { 811 return _colAnd(_rowAnd(rowIndices, subColumnIndices), subRowIndices); 812 } 813 814 public final Collection<Integer> _intent( 815 final Collection<Integer> columnIndices, 816 final Collection<Integer> subRowIndices, 817 final Collection<Integer> subColumnIndices) { 818 return _rowAnd(_colAnd(columnIndices, subRowIndices), subColumnIndices); 819 } 820 821 public final Concept<G, M> topConcept() { 822 final Set<G> extent = new HashSet<G>(rowHeads()); 823 return new Concept<G, M>(extent, rowAnd(extent)); 824 } 825 826 public final Concept<G, M> bottomConcept() { 827 final Set<M> intent = new HashSet<M>(colHeads()); 828 return new Concept<G, M>(colAnd(intent), intent); 829 } 830 831 public final Concept<G, M> objectConcept(final G g) { 832 final Set<M> intent = new HashSet<M>(row(g)); 833 return new Concept<G, M>(colAnd(intent), intent); 834 } 835 836 public final Concept<G, M> attributeConcept(final M m) { 837 final Set<G> extent = new HashSet<G>(col(m)); 838 return new Concept<G, M>(extent, rowAnd(extent)); 839 } 840 841 public final SetList<G> objectLabels(final Set<G> extent, final Set<M> intent) { 842 return SetLists.filter(SetLists.intersection(rowHeads(), extent), new Predicate<G>() { 843 844 public final boolean apply(final G object) { 845 return row(object).equals(intent); 846 } 847 }); 848 } 849 850 public final SetList<M> attributeLabels(final Set<G> extent, final Set<M> intent) { 851 return SetLists.filter(SetLists.intersection(colHeads(), intent), new Predicate<M>() { 852 853 public final boolean apply(final M attribute) { 854 return col(attribute).equals(extent); 855 } 856 }); 857 } 858 859 public final void selectAttribute(final M attribute) { 860 ignoredAttributes.remove(attribute); 861 select(); 862 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 863 } 864 865 public final void deselectAttribute(final M attribute) { 866 ignoredAttributes.add(attribute); 867 select(); 868 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 869 } 870 871 public final void selectAllAttributes() { 872 ignoredAttributes.clear(); 873 select(); 874 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 875 } 876 877 public final void deselectAllAttributes() { 878 ignoredAttributes.addAll(colHeads()); 879 select(); 880 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 881 } 882 883 public final Set<M> selectedAttributes() { 884 return Sets.difference(colHeads, ignoredAttributes); 885 } 886 887 public final void selectObject(final G object) { 888 ignoredObjects.remove(object); 889 select(); 890 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 891 } 892 893 public final void deselectObject(final G object) { 894 ignoredObjects.add(object); 895 select(); 896 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 897 } 898 899 public final void selectAllObjects() { 900 ignoredObjects.clear(); 901 select(); 902 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 903 } 904 905 public final void deselectAllObjects() { 906 ignoredObjects.addAll(rowHeads()); 907 select(); 908 push(new RelationEvent<G, M>(RelationEvent.SELECTION_CHANGED)); 909 } 910 911 public final Set<G> selectedObjects() { 912 return Sets.difference(rowHeads, ignoredObjects); 913 } 914 915 @Override 916 public final MatrixContext<G, M> getSelection() { 917 return selection; 918 } 919 920 public final Context<G, M> subRelation(final Collection<?> objects, final Collection<?> attributes) { 921 return new AbstractContext<G, M>( 922 SetLists.intersection(rowHeads, objects), 923 SetLists.intersection(colHeads, attributes), 924 false) { 925 926 public final boolean contains(final Object object, final Object attribute) { 927 return rowHeads().contains(object) && colHeads().contains(attribute) 928 && MatrixContext.this.contains(object, attribute); 929 } 930 931 public final MatrixContext<G, M> clone() { 932 // those two lines are wrong, they produce always empty subcontexts, independent of delivered input arguments 933// if (rowHeads().isEmpty() || colHeads().isEmpty()) 934// return new MatrixContext<G, M>(false); 935 return new MatrixContext<G, M>( 936 rowHeads(), 937 colHeads(), 938 (BooleanMatrix) MatrixContext.this.matrix 939 .selectRows(Ret.NEW, MatrixContext.this.rowHeads().indicesOf(rowHeads(), false)) 940 .selectColumns(Ret.NEW, MatrixContext.this.colHeads().indicesOf(colHeads(), false)), 941 false); 942 } 943 }; 944 } 945 946 public final Context<M, G> dual() { 947 return new AbstractContext<M, G>(colHeads(), rowHeads(), false) { 948 949 public final boolean contains(final Object attribute, final Object object) { 950 return !MatrixContext.this.contains(object, attribute); 951 } 952 953 public final MatrixContext<M, G> clone() { 954 return new MatrixContext<M, G>( 955 MatrixContext.this.colHeads().clone(), 956 MatrixContext.this.rowHeads().clone(), 957 BooleanMatrices.dual(matrix), 958 false); 959 } 960 }; 961 } 962 963 public final Context<G, M> complement() { 964 return new AbstractContext<G, M>(rowHeads(), colHeads(), false) { 965 966 public final boolean contains(final Object object, final Object attribute) { 967 return !MatrixContext.this.contains(object, attribute); 968 } 969 970 public final MatrixContext<G, M> clone() { 971 return new MatrixContext<G, M>( 972 MatrixContext.this.rowHeads().clone(), 973 MatrixContext.this.colHeads().clone(), 974 BooleanMatrices.complement(matrix), 975 false); 976 } 977 }; 978 } 979 980 public final Context<M, G> contrary() { 981 return new AbstractContext<M, G>(colHeads(), rowHeads(), false) { 982 983 public final boolean contains(final Object attribute, final Object object) { 984 return !MatrixContext.this.contains(object, attribute); 985 } 986 987 public final MatrixContext<M, G> clone() { 988 return new MatrixContext<M, G>( 989 MatrixContext.this.colHeads().clone(), 990 MatrixContext.this.rowHeads().clone(), 991 BooleanMatrices.complement(BooleanMatrices.dual(matrix)), 992 false); 993 } 994 }; 995 } 996 997 public final Relation<Pair<G, M>, Pair<G, M>> ferrersGraph() { 998 final SetList<Pair<G, M>> vertices = 999 SetLists.cartesianProduct(MatrixContext.this.rowHeads(), MatrixContext.this.colHeads()).filter( 1000 new Predicate<Pair<G, M>>() { 1001 1002 public final boolean apply(final Pair<G, M> p) { 1003 return !MatrixContext.this.contains(p.x(), p.y()); 1004 } 1005 }); 1006 return new AbstractRelation<Pair<G, M>, Pair<G, M>>(vertices, vertices, false) { 1007 1008 public final boolean contains(final Object o1, final Object o2) { 1009 return MatrixContext.this.contains(((Pair<?, ?>) o1).x(), ((Pair<?, ?>) o2).y()) 1010 && MatrixContext.this.contains(((Pair<?, ?>) o2).x(), ((Pair<?, ?>) o1).y()); 1011 } 1012 }; 1013 } 1014 1015 public MatrixContext<G, M> clone() { 1016 return new MatrixContext<G, M>(rowHeads(), colHeads(), BooleanMatrices.clone(matrix), homogen); 1017 } 1018 1019 public String toString() { 1020 if (id.get() == null) 1021 return super.toString(); 1022 return id.get(); 1023 } 1024 1025 public Set<M> getEquivalenceClass(final M m) { 1026 return new HashSet<M>(attributeQuasiOrder().col(m)); 1027 } 1028 1029 public boolean isIrreducible(final M m) { 1030 final int j = colHeads.indexOf(m); 1031 return infimumIrreducibles().contains(m) || _irreducibleAttributes.parallelStream().anyMatch( 1032 // s -> s.parallelStream().map(colHeads.index().inverseToJavaFunction()).anyMatch(n -> n.equals(m))); 1033 s -> s.parallelStream().anyMatch(i -> i.equals(j))); 1034 } 1035 1036// public Set<M> getReducingAttributes(final M m) { 1037// if (isIrreducible(m)) 1038// return Collections.singleton(m); 1039// final Set<M> _m = getEquivalenceClass(m); 1040// } 1041}