001package org.dllearner.algorithms.pattern; 002 003import org.dllearner.core.owl.OWLObjectIntersectionOfImplExt; 004import org.dllearner.core.owl.OWLObjectUnionOfImplExt; 005import org.semanticweb.owlapi.model.*; 006import org.semanticweb.owlapi.util.DefaultPrefixManager; 007import org.semanticweb.owlapi.util.OWLClassExpressionVisitorExAdapter; 008import uk.ac.manchester.cs.owl.owlapi.OWLClassImpl; 009import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl; 010 011import javax.annotation.Nonnull; 012import java.lang.reflect.Constructor; 013import java.lang.reflect.InvocationTargetException; 014import java.util.*; 015import java.util.concurrent.atomic.AtomicInteger; 016import java.util.function.BinaryOperator; 017import java.util.function.Function; 018import java.util.function.Supplier; 019import java.util.stream.Collectors; 020import java.util.stream.IntStream; 021import java.util.stream.LongStream; 022import java.util.stream.Stream; 023 024/** 025 * @author Lorenz Buehmann 026 */ 027public class OWLClassExpressionGeneralizer extends OWLClassExpressionVisitorExAdapter<Stream<OWLClassExpression>> { 028 029 private AtomicInteger cnt = new AtomicInteger(1); 030 031 private OWLDataFactory df; 032 private String NS = "http://dl-learner.org/pattern/"; 033 private PrefixManager pm = new DefaultPrefixManager(); 034 035 private final OWLClass CE = new OWLClassImpl(IRI.create(NS + "CE")); 036 private final OWLClass INTERSECTION = new OWLClassImpl(IRI.create(NS + "INTERSECTION")); 037 private final OWLClass UNION = new OWLClassImpl(IRI.create(NS + "UNION")); 038 039 public OWLClassExpressionGeneralizer(OWLDataFactory df) { 040 super(Stream.empty()); 041 this.df = df; 042 043 pm.setDefaultPrefix(NS); 044 } 045 046 @Nonnull 047 @Override 048 protected Stream<OWLClassExpression> doDefault(@Nonnull OWLClassExpression ce) { 049 return Stream.of(ce); 050 } 051 052 public OWLClassExpressionGeneralizer() { 053 this(new OWLDataFactoryImpl()); 054 } 055 056 Function<OWLEntity, OWLEntity> classRenamingFn = k -> k; 057 058 public Set<OWLClassExpression> generalize(OWLClassExpression ce) { 059 cnt.set(1); 060 061 Stream<OWLClassExpression> genCEs = ce.accept(this); 062 063// OWLClassExpressionRenamer renamer = new OWLClassExpressionRenamer(df, new HashMap<>()); 064// renamer.setClassRenamingFn(classRenamingFn); 065 066 return genCEs.map(genCE -> { 067 OWLClassExpressionRenamer renamer = new OWLClassExpressionRenamer(df, new HashMap<>()); 068 renamer.setClassRenamingFn(classRenamingFn); 069 renamer.reset(); 070 return renamer.rename(genCE); 071 }).collect(Collectors.toCollection(TreeSet::new)); 072 } 073 074 @Override 075 public Stream<OWLClassExpression> visit(OWLClass ce) { 076 return Stream.of(newCE()); 077 } 078 079 @Override 080 public Stream<OWLClassExpression> visit(OWLObjectSomeValuesFrom ce) { 081 Stream<OWLClassExpression> res = ce.getFiller().accept(this) 082 .map(f -> df.getOWLObjectSomeValuesFrom(ce.getProperty(), f)); 083// res.add(newCE()); 084 return res; 085 } 086 087 @Override 088 public Stream<OWLClassExpression> visit(OWLDataSomeValuesFrom ce) { 089 return Stream.of(ce); 090 } 091 092 @Override 093 public Stream<OWLClassExpression> visit(OWLObjectAllValuesFrom ce) { 094 Stream<OWLClassExpression> res = ce.getFiller().accept(this) 095 .map(f -> df.getOWLObjectAllValuesFrom(ce.getProperty(), f)); 096// res.add(newCE()); 097 return res; 098 } 099 100 @Override 101 public Stream<OWLClassExpression> visit(OWLDataAllValuesFrom ce) { 102 return Stream.of(ce); 103 } 104 105 @Override 106 public Stream<OWLClassExpression> visit(OWLObjectMinCardinality ce) { 107 return (ce.getCardinality() == 1) 108 ? df.getOWLObjectSomeValuesFrom(ce.getProperty(), ce.getFiller()).accept(this) 109 : ce.getFiller().accept(this) 110 .map(f -> df.getOWLObjectMinCardinality(ce.getCardinality(), ce.getProperty(), f)); 111 } 112 113 @Override 114 public Stream<OWLClassExpression> visit(OWLObjectMaxCardinality ce) { 115 return ce.getFiller().accept(this) 116 .map(f -> df.getOWLObjectMaxCardinality(ce.getCardinality(), ce.getProperty(), f)); 117 } 118 119 @Override 120 public Stream<OWLClassExpression> visit(OWLObjectExactCardinality ce) { 121 return ce.getFiller().accept(this) 122 .map(f -> df.getOWLObjectExactCardinality(ce.getCardinality(), ce.getProperty(), f)); 123 } 124 125 @Override 126 public Stream<OWLClassExpression> visit(OWLDataMinCardinality ce) { 127 return Stream.of(ce); 128 } 129 130 @Override 131 public Stream<OWLClassExpression> visit(OWLDataMaxCardinality ce) { 132 return Stream.of(ce); 133 } 134 135 @Override 136 public Stream<OWLClassExpression> visit(OWLDataExactCardinality ce) { 137 return Stream.of(ce); 138 } 139 140 @Override 141 public Stream<OWLClassExpression> visit(OWLObjectIntersectionOf ce) { 142 return Stream.concat(processNaryBooleanClassExpression(ce), Stream.of(newAnd())); 143 } 144 145 private OWLClass newAnd() { 146 return df.getOWLClass("AND", pm); 147 } 148 149 @Override 150 public Stream<OWLClassExpression> visit(OWLObjectUnionOf ce) { 151 return Stream.concat(processNaryBooleanClassExpression(ce), Stream.of(UNION)); 152 } 153 154 private Stream<OWLClassExpression> processNaryBooleanClassExpression(OWLNaryBooleanClassExpression ce) { 155 try { 156 List<OWLClassExpression> operands = ce.getOperandsAsList(); 157 158 // split by atomic class and complex CE 159 Map<Boolean, List<OWLClassExpression>> operandsSplit = operands.stream().collect(Collectors.partitioningBy(OWLClassExpression::isAnonymous)); 160 161 List<OWLClassExpression> operandsAtomic = operandsSplit.get(false); 162 List<OWLClassExpression> operandsComplex = operandsSplit.get(true); 163 164 // compute the generalizations for the complex CEs 165 Map<OWLClassExpression, List<OWLClassExpression>> operandToGeneralizations = operandsComplex.stream().collect( 166 Collectors.toMap( 167 Function.identity(), 168 op -> op.accept(OWLClassExpressionGeneralizer.this).collect(Collectors.toList()))); 169 170 if(!operandsAtomic.isEmpty()) { 171 operandsComplex.add(operandsAtomic.get(0)); 172 operandToGeneralizations.put(operandsAtomic.get(0), Collections.singletonList(CE)); 173 } 174 175 Stream<List<OWLClassExpression>> combinations = getCombinationsStream(operandsComplex); 176 177 final Constructor<? extends OWLNaryBooleanClassExpression> constructor = ce.getClass().getConstructor(Set.class); 178 179 return combinations.flatMap(c -> { 180 List<List<OWLClassExpression>> opsGen = c.stream().map(op -> operandToGeneralizations.get(op)).collect(Collectors.toList()); 181 182 Set<List<OWLClassExpression>> operandCombinations = getCombinations(opsGen); 183 184 return operandCombinations.stream().map(opC -> { 185 // add placeholder CE for missing operands 186 if (opC.size() < operands.size()) { 187 opC.add(newCE()); 188 } 189 190 Set<OWLClassExpression> newOperands = new HashSet<>(opC); 191 192 try { 193 return (newOperands.size() == 1) ? newOperands.iterator().next() : 194 ((ce.getClassExpressionType() == ClassExpressionType.OBJECT_INTERSECTION_OF) 195 ? new OWLObjectIntersectionOfImplExt(opC) 196 : new OWLObjectUnionOfImplExt(opC)); 197// (OWLClassExpression)constructor.newInstance(newOperands); 198 } catch (Exception e) { 199 e.printStackTrace(); 200 } 201 return null; 202 }); 203 204 }); 205 } catch (NoSuchMethodException e) { 206 e.printStackTrace(); 207 } 208 throw new RuntimeException("Failed to process boolean CE"); 209 } 210 211 private OWLClassExpression newCE() { 212// return df.getOWLClass("CE" + cnt.getAndIncrement(), pm); 213 return CE; 214 } 215 216 public static <T> Stream<List<T>> getCombinationsStream(List<T> list) { 217 // there are 2 ^ list.size() possible combinations 218 // stream through them and map the number of the combination to the combination 219 return LongStream.range(1 , 1 << list.size()) 220 .mapToObj(l -> bitMapToList(l, list)); 221 } 222 223 public static <T> List<T> bitMapToList(long bitmap, List<T> list) { 224 // use the number of the combination (bitmap) as a bitmap to filter the input list 225 return IntStream.range(0, list.size()) 226 .filter(i -> 0 != ((1 << i) & bitmap)) 227 .mapToObj(list::get) 228 .collect(Collectors.toList()); 229 } 230 231 public static <T> Set<List<T>> getCombinations(List<List<T>> lists) { 232 Set<List<T>> combinations = new HashSet<List<T>>(); 233 Set<List<T>> newCombinations; 234 235 int index = 0; 236 237 // extract each of the integers in the first list 238 // and add each to ints as a new list 239 for(T i: lists.get(0)) { 240 List<T> newList = new ArrayList<T>(); 241 newList.add(i); 242 combinations.add(newList); 243 } 244 index++; 245 while(index < lists.size()) { 246 List<T> nextList = lists.get(index); 247 newCombinations = new HashSet<List<T>>(); 248 for(List<T> first: combinations) { 249 for(T second: nextList) { 250 List<T> newList = new ArrayList<T>(); 251 newList.addAll(first); 252 newList.add(second); 253 newCombinations.add(newList); 254 } 255 } 256 combinations = newCombinations; 257 258 index++; 259 } 260 261 return combinations; 262 } 263 264 private static <T> Stream<T> cartesian(BinaryOperator<T> aggregator, 265 Supplier<Stream<T>>... streams) { 266 return Arrays.stream(streams) 267 .reduce((s1, s2) -> 268 () -> s1.get().flatMap(t1 -> s2.get().map(t2 -> aggregator.apply(t1, t2)))) 269 .orElse(Stream::empty).get(); 270 } 271}