001 package aima.logic.fol.kb.data; 002 003 import java.util.ArrayList; 004 import java.util.Collections; 005 import java.util.Comparator; 006 import java.util.HashMap; 007 import java.util.HashSet; 008 import java.util.LinkedHashMap; 009 import java.util.LinkedHashSet; 010 import java.util.List; 011 import java.util.Map; 012 import java.util.Set; 013 014 import aima.logic.fol.StandardizeApart; 015 import aima.logic.fol.StandardizeApartIndexical; 016 import aima.logic.fol.StandardizeApartIndexicalFactory; 017 import aima.logic.fol.SubstVisitor; 018 import aima.logic.fol.SubsumptionElimination; 019 import aima.logic.fol.Unifier; 020 import aima.logic.fol.VariableCollector; 021 import aima.logic.fol.inference.proof.ProofStep; 022 import aima.logic.fol.inference.proof.ProofStepClauseBinaryResolvent; 023 import aima.logic.fol.inference.proof.ProofStepClauseFactor; 024 import aima.logic.fol.inference.proof.ProofStepPremise; 025 import aima.logic.fol.parsing.FOLVisitor; 026 import aima.logic.fol.parsing.ast.AtomicSentence; 027 import aima.logic.fol.parsing.ast.ConnectedSentence; 028 import aima.logic.fol.parsing.ast.Constant; 029 import aima.logic.fol.parsing.ast.Function; 030 import aima.logic.fol.parsing.ast.NotSentence; 031 import aima.logic.fol.parsing.ast.Predicate; 032 import aima.logic.fol.parsing.ast.QuantifiedSentence; 033 import aima.logic.fol.parsing.ast.Sentence; 034 import aima.logic.fol.parsing.ast.Term; 035 import aima.logic.fol.parsing.ast.TermEquality; 036 import aima.logic.fol.parsing.ast.Variable; 037 import aima.util.MixedRadixNumber; 038 039 /** 040 * A Clause: A disjunction of literals. 041 * 042 */ 043 044 /** 045 * @author Ciaran O'Reilly 046 * 047 */ 048 public class Clause { 049 // 050 private static StandardizeApartIndexical _saIndexical = StandardizeApartIndexicalFactory 051 .newStandardizeApartIndexical('c'); 052 private static Unifier _unifier = new Unifier(); 053 private static SubstVisitor _substVisitor = new SubstVisitor(); 054 private static VariableCollector _variableCollector = new VariableCollector(); 055 private static StandardizeApart _standardizeApart = new StandardizeApart(); 056 private static LiteralsSorter _literalSorter = new LiteralsSorter(); 057 // 058 private final Set<Literal> literals = new LinkedHashSet<Literal>(); 059 private final List<Literal> positiveLiterals = new ArrayList<Literal>(); 060 private final List<Literal> negativeLiterals = new ArrayList<Literal>(); 061 private boolean immutable = false; 062 private boolean saCheckRequired = true; 063 private String equalityIdentity = ""; 064 private Set<Clause> factors = null; 065 private Set<Clause> nonTrivialFactors = null; 066 private String stringRep = null; 067 private ProofStep proofStep = null; 068 069 public Clause() { 070 // i.e. the empty clause 071 } 072 073 public Clause(List<Literal> lits) { 074 this.literals.addAll(lits); 075 for (Literal l : literals) { 076 if (l.isPositiveLiteral()) { 077 this.positiveLiterals.add(l); 078 } else { 079 this.negativeLiterals.add(l); 080 } 081 } 082 recalculateIdentity(); 083 } 084 085 public Clause(List<Literal> lits1, List<Literal> lits2) { 086 literals.addAll(lits1); 087 literals.addAll(lits2); 088 for (Literal l : literals) { 089 if (l.isPositiveLiteral()) { 090 this.positiveLiterals.add(l); 091 } else { 092 this.negativeLiterals.add(l); 093 } 094 } 095 recalculateIdentity(); 096 } 097 098 public ProofStep getProofStep() { 099 if (null == proofStep) { 100 // Assume was a premise 101 proofStep = new ProofStepPremise(this); 102 } 103 return proofStep; 104 } 105 106 public void setProofStep(ProofStep proofStep) { 107 this.proofStep = proofStep; 108 } 109 110 public boolean isImmutable() { 111 return immutable; 112 } 113 114 public void setImmutable() { 115 immutable = true; 116 } 117 118 public boolean isStandardizedApartCheckRequired() { 119 return saCheckRequired; 120 } 121 122 public void setStandardizedApartCheckNotRequired() { 123 saCheckRequired = false; 124 } 125 126 public boolean isEmpty() { 127 return literals.size() == 0; 128 } 129 130 public boolean isUnitClause() { 131 return literals.size() == 1; 132 } 133 134 public boolean isDefiniteClause() { 135 // A Definite Clause is a disjunction of literals of which exactly 1 is 136 // positive. 137 return !isEmpty() && positiveLiterals.size() == 1; 138 } 139 140 public boolean isImplicationDefiniteClause() { 141 // An Implication Definite Clause is a disjunction of literals of 142 // which exactly 1 is positive and there is 1 or more negative 143 // literals. 144 return isDefiniteClause() && negativeLiterals.size() >= 1; 145 } 146 147 public boolean isHornClause() { 148 // A Horn clause is a disjunction of literals of which at most one is 149 // positive. 150 return !isEmpty() && positiveLiterals.size() <= 1; 151 } 152 153 public boolean isTautology() { 154 155 for (Literal pl : positiveLiterals) { 156 // Literals in a clause must be exact complements 157 // for tautology elimination to apply. Do not 158 // remove non-identical literals just because 159 // they are complements under unification, see pg16: 160 // http://logic.stanford.edu/classes/cs157/2008/notes/chap09.pdf 161 for (Literal nl : negativeLiterals) { 162 if (pl.getAtomicSentence().equals(nl.getAtomicSentence())) { 163 return true; 164 } 165 } 166 } 167 168 return false; 169 } 170 171 public void addLiteral(Literal literal) { 172 if (isImmutable()) { 173 throw new IllegalStateException( 174 "Clause is immutable, cannot be updated."); 175 } 176 int origSize = literals.size(); 177 literals.add(literal); 178 if (literals.size() > origSize) { 179 if (literal.isPositiveLiteral()) { 180 positiveLiterals.add(literal); 181 } else { 182 negativeLiterals.add(literal); 183 } 184 } 185 recalculateIdentity(); 186 } 187 188 public void addPositiveLiteral(AtomicSentence atom) { 189 addLiteral(new Literal(atom)); 190 } 191 192 public void addNegativeLiteral(AtomicSentence atom) { 193 addLiteral(new Literal(atom, true)); 194 } 195 196 public int getNumberLiterals() { 197 return literals.size(); 198 } 199 200 public int getNumberPositiveLiterals() { 201 return positiveLiterals.size(); 202 } 203 204 public int getNumberNegativeLiterals() { 205 return negativeLiterals.size(); 206 } 207 208 public Set<Literal> getLiterals() { 209 return Collections.unmodifiableSet(literals); 210 } 211 212 public List<Literal> getPositiveLiterals() { 213 return Collections.unmodifiableList(positiveLiterals); 214 } 215 216 public List<Literal> getNegativeLiterals() { 217 return Collections.unmodifiableList(negativeLiterals); 218 } 219 220 public Set<Clause> getFactors() { 221 if (null == factors) { 222 calculateFactors(null); 223 } 224 return Collections.unmodifiableSet(factors); 225 } 226 227 public Set<Clause> getNonTrivialFactors() { 228 if (null == nonTrivialFactors) { 229 calculateFactors(null); 230 } 231 return Collections.unmodifiableSet(nonTrivialFactors); 232 } 233 234 public boolean subsumes(Clause othC) { 235 boolean subsumes = false; 236 237 // Equality is not subsumption 238 if (!(this == othC)) { 239 // Ensure this has less literals total and that 240 // it is a subset of the other clauses positive and negative counts 241 if (this.getNumberLiterals() < othC.getNumberLiterals() 242 && this.getNumberPositiveLiterals() <= othC 243 .getNumberPositiveLiterals() 244 && this.getNumberNegativeLiterals() <= othC 245 .getNumberNegativeLiterals()) { 246 247 Map<String, List<Literal>> thisToTry = collectLikeLiterals(this.literals); 248 Map<String, List<Literal>> othCToTry = collectLikeLiterals(othC.literals); 249 // Ensure all like literals from this clause are a subset 250 // of the other clause. 251 if (othCToTry.keySet().containsAll(thisToTry.keySet())) { 252 boolean isAPossSubset = true; 253 // Ensure that each set of same named literals 254 // from this clause is a subset of the other 255 // clauses same named literals. 256 for (String pk : thisToTry.keySet()) { 257 if (thisToTry.get(pk).size() > othCToTry.get(pk).size()) { 258 isAPossSubset = false; 259 break; 260 } 261 } 262 if (isAPossSubset) { 263 // At this point I know this this Clause's 264 // literal/arity names are a subset of the 265 // other clauses literal/arity names 266 subsumes = checkSubsumes(othC, thisToTry, othCToTry); 267 } 268 } 269 } 270 } 271 272 return subsumes; 273 } 274 275 // Note: Applies binary resolution rule and factoring 276 // Note: returns a set with an empty clause if both clauses 277 // are empty, otherwise returns a set of binary resolvents. 278 public Set<Clause> binaryResolvents(Clause othC) { 279 Set<Clause> resolvents = new LinkedHashSet<Clause>(); 280 // Resolving two empty clauses 281 // gives you an empty clause 282 if (isEmpty() && othC.isEmpty()) { 283 resolvents.add(new Clause()); 284 return resolvents; 285 } 286 287 // Ensure Standardized Apart 288 // Before attempting binary resolution 289 othC = saIfRequired(othC); 290 291 List<Literal> allPosLits = new ArrayList<Literal>(); 292 List<Literal> allNegLits = new ArrayList<Literal>(); 293 allPosLits.addAll(this.positiveLiterals); 294 allPosLits.addAll(othC.positiveLiterals); 295 allNegLits.addAll(this.negativeLiterals); 296 allNegLits.addAll(othC.negativeLiterals); 297 298 List<Literal> trPosLits = new ArrayList<Literal>(); 299 List<Literal> trNegLits = new ArrayList<Literal>(); 300 List<Literal> copyRPosLits = new ArrayList<Literal>(); 301 List<Literal> copyRNegLits = new ArrayList<Literal>(); 302 303 for (int i = 0; i < 2; i++) { 304 trPosLits.clear(); 305 trNegLits.clear(); 306 307 if (i == 0) { 308 // See if this clauses positives 309 // unify with the other clauses 310 // negatives 311 trPosLits.addAll(this.positiveLiterals); 312 trNegLits.addAll(othC.negativeLiterals); 313 } else { 314 // Try the other way round now 315 trPosLits.addAll(othC.positiveLiterals); 316 trNegLits.addAll(this.negativeLiterals); 317 } 318 319 // Now check to see if they resolve 320 Map<Variable, Term> copyRBindings = new LinkedHashMap<Variable, Term>(); 321 for (Literal pl : trPosLits) { 322 for (Literal nl : trNegLits) { 323 copyRBindings.clear(); 324 if (null != _unifier.unify(pl.getAtomicSentence(), nl 325 .getAtomicSentence(), copyRBindings)) { 326 copyRPosLits.clear(); 327 copyRNegLits.clear(); 328 boolean found = false; 329 for (Literal l : allPosLits) { 330 if (!found && pl.equals(l)) { 331 found = true; 332 continue; 333 } 334 copyRPosLits.add(_substVisitor.subst(copyRBindings, 335 l)); 336 } 337 found = false; 338 for (Literal l : allNegLits) { 339 if (!found && nl.equals(l)) { 340 found = true; 341 continue; 342 } 343 copyRNegLits.add(_substVisitor.subst(copyRBindings, 344 l)); 345 } 346 // Ensure the resolvents are standardized apart 347 Map<Variable, Term> renameSubstitituon = _standardizeApart 348 .standardizeApart(copyRPosLits, copyRNegLits, 349 _saIndexical); 350 Clause c = new Clause(copyRPosLits, copyRNegLits); 351 c.setProofStep(new ProofStepClauseBinaryResolvent(c, 352 this, othC, copyRBindings, renameSubstitituon)); 353 if (isImmutable()) { 354 c.setImmutable(); 355 } 356 if (!isStandardizedApartCheckRequired()) { 357 c.setStandardizedApartCheckNotRequired(); 358 } 359 resolvents.add(c); 360 } 361 } 362 } 363 } 364 365 return resolvents; 366 } 367 368 public String toString() { 369 if (null == stringRep) { 370 List<Literal> sortedLiterals = new ArrayList<Literal>(literals); 371 Collections.sort(sortedLiterals, _literalSorter); 372 373 stringRep = sortedLiterals.toString(); 374 } 375 return stringRep; 376 } 377 378 public int hashCode() { 379 return equalityIdentity.hashCode(); 380 } 381 382 public boolean equals(Object othObj) { 383 if (null == othObj) { 384 return false; 385 } 386 if (this == othObj) { 387 return true; 388 } 389 if (!(othObj instanceof Clause)) { 390 return false; 391 } 392 Clause othClause = (Clause) othObj; 393 394 return equalityIdentity.equals(othClause.equalityIdentity); 395 } 396 397 public String getEqualityIdentity() { 398 return equalityIdentity; 399 } 400 401 // 402 // PRIVATE METHODS 403 // 404 private void recalculateIdentity() { 405 synchronized (equalityIdentity) { 406 407 // Sort the literals first based on negation, atomic sentence, 408 // constant, function and variable. 409 List<Literal> sortedLiterals = new ArrayList<Literal>(literals); 410 Collections.sort(sortedLiterals, _literalSorter); 411 412 // All variables are considered the same as regards 413 // sorting. Therefore, to determine if two clauses 414 // are equivalent you need to determine 415 // the # of unique variables they contain and 416 // there positions across the clauses 417 ClauseEqualityIdentityConstructor ceic = new ClauseEqualityIdentityConstructor( 418 sortedLiterals, _literalSorter); 419 420 equalityIdentity = ceic.getIdentity(); 421 422 // Reset, these as will need to re-calcualte 423 // if requested for again, best to only 424 // access lazily. 425 factors = null; 426 nonTrivialFactors = null; 427 // Reset the objects string representation 428 // until it is requested for. 429 stringRep = null; 430 } 431 } 432 433 private void calculateFactors(Set<Clause> parentFactors) { 434 nonTrivialFactors = new LinkedHashSet<Clause>(); 435 436 Map<Variable, Term> theta = new HashMap<Variable, Term>(); 437 List<Literal> lits = new ArrayList<Literal>(); 438 for (int i = 0; i < 2; i++) { 439 lits.clear(); 440 if (i == 0) { 441 // Look at the positive literals 442 lits.addAll(positiveLiterals); 443 } else { 444 // Look at the negative literals 445 lits.addAll(negativeLiterals); 446 } 447 for (int x = 0; x < lits.size(); x++) { 448 for (int y = x + 1; y < lits.size(); y++) { 449 Literal litX = lits.get(x); 450 Literal litY = lits.get(y); 451 452 theta.clear(); 453 Map<Variable, Term> substitution = _unifier.unify(litX 454 .getAtomicSentence(), litY.getAtomicSentence(), 455 theta); 456 if (null != substitution) { 457 List<Literal> posLits = new ArrayList<Literal>(); 458 List<Literal> negLits = new ArrayList<Literal>(); 459 if (i == 0) { 460 posLits 461 .add(_substVisitor 462 .subst(substitution, litX)); 463 } else { 464 negLits 465 .add(_substVisitor 466 .subst(substitution, litX)); 467 } 468 for (Literal pl : positiveLiterals) { 469 if (pl == litX || pl == litY) { 470 continue; 471 } 472 posLits.add(_substVisitor.subst(substitution, pl)); 473 } 474 for (Literal nl : negativeLiterals) { 475 if (nl == litX || nl == litY) { 476 continue; 477 } 478 negLits.add(_substVisitor.subst(substitution, nl)); 479 } 480 // Ensure the non trivial factor is standardized apart 481 _standardizeApart.standardizeApart(posLits, negLits, 482 _saIndexical); 483 Clause c = new Clause(posLits, negLits); 484 c.setProofStep(new ProofStepClauseFactor(c, this)); 485 if (isImmutable()) { 486 c.setImmutable(); 487 } 488 if (!isStandardizedApartCheckRequired()) { 489 c.setStandardizedApartCheckNotRequired(); 490 } 491 if (null == parentFactors) { 492 c.calculateFactors(nonTrivialFactors); 493 nonTrivialFactors.addAll(c.getFactors()); 494 } else { 495 if (!parentFactors.contains(c)) { 496 c.calculateFactors(nonTrivialFactors); 497 nonTrivialFactors.addAll(c.getFactors()); 498 } 499 } 500 } 501 } 502 } 503 } 504 505 factors = new LinkedHashSet<Clause>(); 506 // Need to add self, even though a non-trivial 507 // factor. See: slide 30 508 // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture10.pdf 509 // for example of incompleteness when 510 // trivial factor not included. 511 factors.add(this); 512 factors.addAll(nonTrivialFactors); 513 } 514 515 private Clause saIfRequired(Clause othClause) { 516 517 // If performing resolution with self 518 // then need to standardize apart in 519 // order to work correctly. 520 if (isStandardizedApartCheckRequired() || this == othClause) { 521 Set<Variable> mVariables = _variableCollector 522 .collectAllVariables(this); 523 Set<Variable> oVariables = _variableCollector 524 .collectAllVariables(othClause); 525 526 Set<Variable> cVariables = new HashSet<Variable>(); 527 cVariables.addAll(mVariables); 528 cVariables.addAll(oVariables); 529 530 if (cVariables.size() < (mVariables.size() + oVariables.size())) { 531 othClause = _standardizeApart.standardizeApart(othClause, 532 _saIndexical); 533 } 534 } 535 536 return othClause; 537 } 538 539 private Map<String, List<Literal>> collectLikeLiterals(Set<Literal> literals) { 540 Map<String, List<Literal>> likeLiterals = new HashMap<String, List<Literal>>(); 541 for (Literal l : literals) { 542 // Want to ensure P(a, b) is considered different than P(a, b, c) 543 // i.e. consider an atom's arity P/#. 544 String literalName = (l.isNegativeLiteral() ? "~" : "") 545 + l.getAtomicSentence().getSymbolicName() + "/" 546 + l.getAtomicSentence().getArgs().size(); 547 List<Literal> like = likeLiterals.get(literalName); 548 if (null == like) { 549 like = new ArrayList<Literal>(); 550 likeLiterals.put(literalName, like); 551 } 552 like.add(l); 553 } 554 return likeLiterals; 555 } 556 557 private boolean checkSubsumes(Clause othC, 558 Map<String, List<Literal>> thisToTry, 559 Map<String, List<Literal>> othCToTry) { 560 boolean subsumes = false; 561 562 List<Term> thisTerms = new ArrayList<Term>(); 563 List<Term> othCTerms = new ArrayList<Term>(); 564 565 // Want to track possible number of permuations 566 List<Integer> radixs = new ArrayList<Integer>(); 567 for (String literalName : thisToTry.keySet()) { 568 int sizeT = thisToTry.get(literalName).size(); 569 int sizeO = othCToTry.get(literalName).size(); 570 571 if (sizeO > 1) { 572 // The following is being used to 573 // track the number of permutations 574 // that can be mapped from the 575 // other clauses like literals to this 576 // clauses like literals. 577 // i.e. n!/(n-r)! 578 // where n=sizeO and r =sizeT 579 for (int i = 0; i < sizeT; i++) { 580 int r = sizeO - i; 581 if (r > 1) { 582 radixs.add(r); 583 } 584 } 585 } 586 // Track the terms for this clause 587 for (Literal tl : thisToTry.get(literalName)) { 588 thisTerms.addAll(tl.getAtomicSentence().getArgs()); 589 } 590 } 591 592 MixedRadixNumber permutation = null; 593 long numPermutations = 1L; 594 if (radixs.size() > 0) { 595 permutation = new MixedRadixNumber(0, radixs); 596 numPermutations = permutation.getMaxAllowedValue() + 1; 597 } 598 // Want to ensure none of the othCVariables are 599 // part of the key set of a unification as 600 // this indicates it is not a legal subsumption. 601 Set<Variable> othCVariables = _variableCollector 602 .collectAllVariables(othC); 603 Map<Variable, Term> theta = new LinkedHashMap<Variable, Term>(); 604 List<Literal> literalPermuations = new ArrayList<Literal>(); 605 for (long l = 0L; l < numPermutations; l++) { 606 // Track the other clause's terms for this 607 // permutation. 608 othCTerms.clear(); 609 int radixIdx = 0; 610 for (String literalName : thisToTry.keySet()) { 611 int sizeT = thisToTry.get(literalName).size(); 612 literalPermuations.clear(); 613 literalPermuations.addAll(othCToTry.get(literalName)); 614 int sizeO = literalPermuations.size(); 615 616 if (sizeO > 1) { 617 for (int i = 0; i < sizeT; i++) { 618 int r = sizeO - i; 619 if (r > 1) { 620 // If not a 1 to 1 mapping then you need 621 // to use the correct permuation 622 int numPos = permutation 623 .getCurrentNumeralValue(radixIdx); 624 othCTerms.addAll(literalPermuations.remove(numPos) 625 .getAtomicSentence().getArgs()); 626 radixIdx++; 627 } else { 628 // is the last mapping, therefore 629 // won't be on the radix 630 othCTerms.addAll(literalPermuations.get(0) 631 .getAtomicSentence().getArgs()); 632 } 633 } 634 } else { 635 // a 1 to 1 mapping 636 othCTerms.addAll(literalPermuations.get(0) 637 .getAtomicSentence().getArgs()); 638 } 639 } 640 641 // Note: on unifier 642 // unifier.unify(P(w, x), P(y, z)))={w=y, x=z} 643 // unifier.unify(P(y, z), P(w, x)))={y=w, z=x} 644 // Therefore want this clause to be the first 645 // so can do the othCVariables check for an invalid 646 // subsumes. 647 theta.clear(); 648 if (null != _unifier.unify(thisTerms, othCTerms, theta)) { 649 boolean containsAny = false; 650 for (Variable v : theta.keySet()) { 651 if (othCVariables.contains(v)) { 652 containsAny = true; 653 break; 654 } 655 } 656 if (!containsAny) { 657 subsumes = true; 658 break; 659 } 660 } 661 662 // If there is more than 1 mapping 663 // keep track of where I am in the 664 // possible number of mapping permutations. 665 if (null != permutation) { 666 permutation.increment(); 667 } 668 } 669 670 return subsumes; 671 } 672 } 673 674 class LiteralsSorter implements Comparator<Literal> { 675 public int compare(Literal o1, Literal o2) { 676 int rVal = 0; 677 // If literals are not negated the same 678 // then positive literals are considered 679 // (by convention here) to be of higher 680 // order than negative literals 681 if (o1.isPositiveLiteral() != o2.isPositiveLiteral()) { 682 if (o1.isPositiveLiteral()) { 683 return 1; 684 } 685 return -1; 686 } 687 688 // Check their symbolic names for order first 689 rVal = o1.getAtomicSentence().getSymbolicName().compareTo( 690 o2.getAtomicSentence().getSymbolicName()); 691 692 // If have same symbolic names 693 // then need to compare individual arguments 694 // for order. 695 if (0 == rVal) { 696 rVal = compareArgs(o1.getAtomicSentence().getArgs(), o2 697 .getAtomicSentence().getArgs()); 698 } 699 700 return rVal; 701 } 702 703 private int compareArgs(List<Term> args1, List<Term> args2) { 704 int rVal = 0; 705 706 // Compare argument sizes first 707 rVal = args1.size() - args2.size(); 708 709 if (0 == rVal && args1.size() > 0) { 710 // Move forward and compare the 711 // first arguments 712 Term t1 = args1.get(0); 713 Term t2 = args2.get(0); 714 715 if (t1.getClass() == t2.getClass()) { 716 // Note: Variables are considered to have 717 // the same order 718 if (t1 instanceof Constant) { 719 rVal = t1.getSymbolicName().compareTo(t2.getSymbolicName()); 720 } else if (t1 instanceof Function) { 721 rVal = t1.getSymbolicName().compareTo(t2.getSymbolicName()); 722 if (0 == rVal) { 723 // Same function names, therefore 724 // compare the function arguments 725 rVal = compareArgs(t1.getArgs(), t2.getArgs()); 726 } 727 } 728 729 // If the first args are the same 730 // then compare the ordering of the 731 // remaining arguments 732 if (0 == rVal) { 733 rVal = compareArgs(args1.subList(1, args1.size()), args2 734 .subList(1, args2.size())); 735 } 736 } else { 737 // Order for different Terms is: 738 // Constant > Function > Variable 739 if (t1 instanceof Constant) { 740 rVal = 1; 741 } else if (t2 instanceof Constant) { 742 rVal = -1; 743 } else if (t1 instanceof Function) { 744 rVal = 1; 745 } else { 746 rVal = -1; 747 } 748 } 749 } 750 751 return rVal; 752 } 753 } 754 755 class ClauseEqualityIdentityConstructor implements FOLVisitor { 756 private StringBuilder identity = new StringBuilder(); 757 private int noVarPositions = 0; 758 private int[] clauseVarCounts = null; 759 private int currentLiteral = 0; 760 private Map<String, List<Integer>> varPositions = new HashMap<String, List<Integer>>(); 761 762 public ClauseEqualityIdentityConstructor(List<Literal> literals, 763 LiteralsSorter sorter) { 764 765 clauseVarCounts = new int[literals.size()]; 766 767 for (Literal l : literals) { 768 if (l.isNegativeLiteral()) { 769 identity.append("~"); 770 } 771 identity.append(l.getAtomicSentence().getSymbolicName()); 772 identity.append("("); 773 boolean firstTerm = true; 774 for (Term t : l.getAtomicSentence().getArgs()) { 775 if (firstTerm) { 776 firstTerm = false; 777 } else { 778 identity.append(","); 779 } 780 t.accept(this, null); 781 } 782 identity.append(")"); 783 currentLiteral++; 784 } 785 786 int min, max; 787 min = max = 0; 788 for (int i = 0; i < literals.size(); i++) { 789 int incITo = i; 790 int next = i + 1; 791 max += clauseVarCounts[i]; 792 while (next < literals.size()) { 793 if (0 != sorter.compare(literals.get(i), literals.get(next))) { 794 break; 795 } 796 max += clauseVarCounts[next]; 797 incITo = next; // Need to skip to the end of the range 798 next++; 799 } 800 // This indicates two or more literals are identical 801 // except for variable naming (note: identical 802 // same name would be removed as are working 803 // with sets so don't need to worry about this). 804 if ((next - i) > 1) { 805 // Need to check each variable 806 // and if it has a position within the 807 // current min/max range then need 808 // to include its alternative 809 // sort order positions as well 810 for (String key : varPositions.keySet()) { 811 List<Integer> positions = varPositions.get(key); 812 List<Integer> additPositions = new ArrayList<Integer>(); 813 // Add then subtract for all possible 814 // positions in range 815 for (int pos : positions) { 816 if (pos >= min && pos < max) { 817 int pPos = pos; 818 int nPos = pos; 819 for (int candSlot = i; candSlot < (next - 1); candSlot++) { 820 pPos += clauseVarCounts[i]; 821 if (pPos >= min && pPos < max) { 822 if (!positions.contains(pPos) 823 && !additPositions.contains(pPos)) { 824 additPositions.add(pPos); 825 } 826 } 827 nPos -= clauseVarCounts[i]; 828 if (nPos >= min && nPos < max) { 829 if (!positions.contains(nPos) 830 && !additPositions.contains(nPos)) { 831 additPositions.add(nPos); 832 } 833 } 834 } 835 } 836 } 837 positions.addAll(additPositions); 838 } 839 } 840 min = max; 841 i = incITo; 842 } 843 844 // Determine the maxWidth 845 int maxWidth = 1; 846 while (noVarPositions >= 10) { 847 noVarPositions = noVarPositions / 10; 848 maxWidth++; 849 } 850 String format = "%0" + maxWidth + "d"; 851 852 // Sort the individual position lists 853 // And then add their string representations 854 // together 855 List<String> varOffsets = new ArrayList<String>(); 856 for (String key : varPositions.keySet()) { 857 List<Integer> positions = varPositions.get(key); 858 Collections.sort(positions); 859 StringBuilder sb = new StringBuilder(); 860 for (int pos : positions) { 861 sb.append(String.format(format, pos)); 862 } 863 varOffsets.add(sb.toString()); 864 } 865 Collections.sort(varOffsets); 866 for (int i = 0; i < varOffsets.size(); i++) { 867 identity.append(varOffsets.get(i)); 868 if (i < (varOffsets.size() - 1)) { 869 identity.append(","); 870 } 871 } 872 } 873 874 public String getIdentity() { 875 return identity.toString(); 876 } 877 878 // 879 // START-FOLVisitor 880 public Object visitVariable(Variable var, Object arg) { 881 // All variables will be marked with an * 882 identity.append("*"); 883 884 List<Integer> positions = varPositions.get(var.getValue()); 885 if (null == positions) { 886 positions = new ArrayList<Integer>(); 887 varPositions.put(var.getValue(), positions); 888 } 889 positions.add(noVarPositions); 890 891 noVarPositions++; 892 clauseVarCounts[currentLiteral]++; 893 return var; 894 } 895 896 public Object visitConstant(Constant constant, Object arg) { 897 identity.append(constant.getValue()); 898 return constant; 899 } 900 901 public Object visitFunction(Function function, Object arg) { 902 boolean firstTerm = true; 903 identity.append(function.getFunctionName()); 904 identity.append("("); 905 for (Term t : function.getTerms()) { 906 if (firstTerm) { 907 firstTerm = false; 908 } else { 909 identity.append(","); 910 } 911 t.accept(this, arg); 912 } 913 identity.append(")"); 914 915 return function; 916 } 917 918 public Object visitPredicate(Predicate predicate, Object arg) { 919 throw new IllegalStateException("Should not be called"); 920 } 921 922 public Object visitTermEquality(TermEquality equality, Object arg) { 923 throw new IllegalStateException("Should not be called"); 924 } 925 926 public Object visitQuantifiedSentence(QuantifiedSentence sentence, 927 Object arg) { 928 throw new IllegalStateException("Should not be called"); 929 } 930 931 public Object visitNotSentence(NotSentence sentence, Object arg) { 932 throw new IllegalStateException("Should not be called"); 933 } 934 935 public Object visitConnectedSentence(ConnectedSentence sentence, Object arg) { 936 throw new IllegalStateException("Should not be called"); 937 } 938 939 // END-FOLVisitor 940 // 941 }