diff --git a/pom.xml b/pom.xml index d317bc29..e2140368 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ 0.14-r6 0.9.31-r8 - 0.37.0-SNAPSHOT + 0.36.0 4.7.6 diff --git a/reasoner/reasoner-elk-snomed/src/test/java/dev/ikm/tinkar/reasoner/elksnomed/ElkSnomedClassifierTestBase.java b/reasoner/reasoner-elk-snomed/src/test/java/dev/ikm/tinkar/reasoner/elksnomed/ElkSnomedClassifierTestBase.java index c22c9f6b..aa3f338f 100644 --- a/reasoner/reasoner-elk-snomed/src/test/java/dev/ikm/tinkar/reasoner/elksnomed/ElkSnomedClassifierTestBase.java +++ b/reasoner/reasoner-elk-snomed/src/test/java/dev/ikm/tinkar/reasoner/elksnomed/ElkSnomedClassifierTestBase.java @@ -74,6 +74,7 @@ public void nnfService() throws Exception { int nid = ElkSnomedData.getNid(404785009); LogicalExpression nnf = rs.getNecessaryNormalForm(nid); LOG.info("NNF: " + nid + " " + nnf); + rs.writeInferredResults(); } private HashMap nid_sctid_map; diff --git a/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/InferredResultsWriter.java b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/InferredResultsWriter.java new file mode 100644 index 00000000..f5e7c568 --- /dev/null +++ b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/InferredResultsWriter.java @@ -0,0 +1,218 @@ +package dev.ikm.tinkar.reasoner.service; + +import java.util.Arrays; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.list.ImmutableList; +import org.eclipse.collections.api.list.primitive.ImmutableIntList; +import org.eclipse.collections.api.set.primitive.ImmutableIntSet; +import org.eclipse.collections.impl.factory.primitive.IntLists; +import org.eclipse.collections.impl.factory.primitive.IntSets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import dev.ikm.tinkar.common.id.IntIdSet; +import dev.ikm.tinkar.common.id.IntIds; +import dev.ikm.tinkar.common.service.PrimitiveData; +import dev.ikm.tinkar.common.sets.ConcurrentHashSet; +import dev.ikm.tinkar.common.util.time.MultipleEndpointTimer; +import dev.ikm.tinkar.common.util.uuid.UuidT5Generator; +import dev.ikm.tinkar.coordinate.stamp.calculator.Latest; +import dev.ikm.tinkar.coordinate.view.ViewCoordinateRecord; +import dev.ikm.tinkar.entity.Entity; +import dev.ikm.tinkar.entity.EntityService; +import dev.ikm.tinkar.entity.EntityVersion; +import dev.ikm.tinkar.entity.RecordListBuilder; +import dev.ikm.tinkar.entity.SemanticEntityVersion; +import dev.ikm.tinkar.entity.SemanticRecord; +import dev.ikm.tinkar.entity.SemanticRecordBuilder; +import dev.ikm.tinkar.entity.SemanticVersionRecord; +import dev.ikm.tinkar.entity.StampEntity; +import dev.ikm.tinkar.entity.graph.DiTreeEntity; +import dev.ikm.tinkar.entity.graph.adaptor.axiom.LogicalExpression; +import dev.ikm.tinkar.entity.graph.isomorphic.IsomorphicResults; +import dev.ikm.tinkar.entity.transaction.Transaction; +import dev.ikm.tinkar.terms.State; +import dev.ikm.tinkar.terms.TinkarTerm; + +public class InferredResultsWriter { + + private static final Logger LOG = LoggerFactory.getLogger(InferredResultsWriter.class); + + private ReasonerService rs; + + private Transaction updateTransaction; + + private int updateStampNid; + + private int inferredPatternNid; + + private int inferredNavigationPatternNid; + + private MultipleEndpointTimer multipleEndpointTimer; + + private ConcurrentHashSet equivalentSets; + + private ConcurrentHashSet conceptsWithInferredChanges; + + private ConcurrentHashSet conceptsWithNavigationChanges; + + private AtomicInteger axiomDataNotFoundCounter; + + public InferredResultsWriter(ReasonerService rs) { + super(); + this.rs = rs; + } + + private ViewCoordinateRecord getViewCoordinateRecord() { + return rs.getViewCalculator().viewCoordinateRecord(); + } + + private void processSemantic(Entity entity) { + updateTransaction.addComponent(entity); + Entity.provider().putEntity(entity); + } + + public ClassifierResults write() { + updateTransaction = Transaction.make("Committing classification"); + EntityService.get().beginLoadPhase(); + try { + StampEntity updateStamp = updateTransaction.getStamp(State.ACTIVE, + getViewCoordinateRecord().getAuthorNidForChanges(), getViewCoordinateRecord().getDefaultModuleNid(), + getViewCoordinateRecord().getDefaultPathNid()); + updateStampNid = updateStamp.nid(); + inferredPatternNid = getViewCoordinateRecord().logicCoordinate().inferredAxiomsPatternNid(); + inferredNavigationPatternNid = TinkarTerm.INFERRED_NAVIGATION_PATTERN.nid(); + multipleEndpointTimer = new MultipleEndpointTimer<>(IsomorphicResults.EndPoints.class); + equivalentSets = new ConcurrentHashSet<>(); + conceptsWithInferredChanges = new ConcurrentHashSet<>(); + conceptsWithNavigationChanges = new ConcurrentHashSet<>(); + axiomDataNotFoundCounter = new AtomicInteger(); + rs.getReasonerConceptSet().primitiveParallelStream().forEach(conceptNid -> { + updateEquivalentSets(conceptNid); + writeNNF(conceptNid); + writeNavigation(conceptNid); + }); + updateTransaction.commit(); + } finally { + EntityService.get().endLoadPhase(); + } + LOG.info("Inferred changes: " + conceptsWithInferredChanges.size()); + LOG.info("Navigation changes: " + conceptsWithNavigationChanges.size()); + LOG.info("NavigationSemantics processed not in AxiomData: " + axiomDataNotFoundCounter.get()); + ViewCoordinateRecord commitCoordinate = getViewCoordinateRecord().withStampCoordinate( + getViewCoordinateRecord().stampCoordinate().withStampPositionTime(updateTransaction.commitTime())); + return new ClassifierResults(rs.getReasonerConceptSet(), + IntLists.immutable.ofAll(conceptsWithInferredChanges.stream().sorted().mapToInt(Integer::intValue)), + IntLists.immutable.ofAll(conceptsWithNavigationChanges.stream().sorted().mapToInt(Integer::intValue)), + equivalentSets, commitCoordinate); + } + + private void updateEquivalentSets(int conceptNid) { + ImmutableIntSet equivalentNids = rs.getEquivalent(conceptNid); + if (equivalentNids == null) { + LOG.error("Null node for: {} {} {} will be skipped in inferred results", conceptNid, + PrimitiveData.publicId(conceptNid).idString(), PrimitiveData.text(conceptNid)); + } else if (equivalentNids.size() > 1) { + equivalentSets.add(equivalentNids.toSortedList().toImmutable()); + } + } + + private void writeNNF(int conceptNid) { + LogicalExpression nnf = rs.getNecessaryNormalForm(conceptNid); + ImmutableList fields = Lists.immutable.of(nnf.sourceGraph()); + int[] inferredSemanticNids = PrimitiveData.get().semanticNidsForComponentOfPattern(conceptNid, + inferredPatternNid); + if (inferredSemanticNids.length == 0) { + UUID uuidForSemantic = UuidT5Generator.singleSemanticUuid(Entity.getFast(inferredPatternNid), + Entity.getFast(conceptNid)); + // Create new semantic... + RecordListBuilder versionRecords = RecordListBuilder.make(); + SemanticRecord semanticRecord = SemanticRecordBuilder.builder() + .leastSignificantBits(uuidForSemantic.getLeastSignificantBits()) + .mostSignificantBits(uuidForSemantic.getMostSignificantBits()) + .nid(PrimitiveData.nid(uuidForSemantic)).referencedComponentNid(conceptNid) + .patternNid(inferredPatternNid).versions(versionRecords).build(); + versionRecords.add(new SemanticVersionRecord(semanticRecord, updateStampNid, fields)); + processSemantic(semanticRecord); + conceptsWithInferredChanges.add(conceptNid); + } else if (inferredSemanticNids.length == 1) { + Latest latestInferredSemantic = rs.getViewCalculator() + .latest(inferredSemanticNids[0]); + boolean changed = true; + if (latestInferredSemantic.isPresent()) { + ImmutableList latestInferredFields = latestInferredSemantic.get().fieldValues(); + DiTreeEntity latestInferredTree = (DiTreeEntity) latestInferredFields.get(0); + DiTreeEntity correlatedTree = latestInferredTree.makeCorrelatedTree((DiTreeEntity) nnf.sourceGraph(), + conceptNid, multipleEndpointTimer.startNew()); + changed = correlatedTree != latestInferredTree; + } + if (changed) { + processSemantic(rs.getViewCalculator().updateFields(inferredSemanticNids[0], fields, updateStampNid)); + conceptsWithInferredChanges.add(conceptNid); + } + } else { + throw new IllegalStateException("More than one inferred semantic of pattern " + + PrimitiveData.text(inferredPatternNid) + "for component: " + PrimitiveData.text(conceptNid)); + } + } + + private void writeNavigation(int conceptNid) { + ImmutableIntSet parentNids = rs.getParents(conceptNid); + ImmutableIntSet childNids = rs.getChildren(conceptNid); + if (parentNids == null) { + parentNids = IntSets.immutable.of(); + childNids = IntSets.immutable.of(); + axiomDataNotFoundCounter.incrementAndGet(); + } + int[] inferredNavigationNids = PrimitiveData.get().semanticNidsForComponentOfPattern(conceptNid, + inferredNavigationPatternNid); + if (inferredNavigationNids.length == 0) { + if (parentNids.notEmpty() || childNids.notEmpty()) { + UUID uuidForSemantic = UuidT5Generator.singleSemanticUuid(Entity.getFast(inferredNavigationPatternNid), + Entity.getFast(conceptNid)); + // Create new semantic... + RecordListBuilder versionRecords = RecordListBuilder.make(); + SemanticRecord navigationRecord = SemanticRecordBuilder.builder() + .leastSignificantBits(uuidForSemantic.getLeastSignificantBits()) + .mostSignificantBits(uuidForSemantic.getMostSignificantBits()) + .nid(PrimitiveData.nid(uuidForSemantic)).referencedComponentNid(conceptNid) + .patternNid(inferredNavigationPatternNid).versions(versionRecords).build(); + IntIdSet parentIds = IntIds.set.of(parentNids.toArray()); + IntIdSet childrenIds = IntIds.set.of(childNids.toArray()); + versionRecords.add(new SemanticVersionRecord(navigationRecord, updateStampNid, + Lists.immutable.of(childrenIds, parentIds))); + processSemantic(navigationRecord); + conceptsWithNavigationChanges.add(conceptNid); + } + } else if (inferredNavigationNids.length == 1) { + Latest latestInferredNavigationSemantic = rs.getViewCalculator() + .latest(inferredNavigationNids[0]); + boolean navigationChanged = true; + if (latestInferredNavigationSemantic.isPresent()) { + ImmutableList latestInferredNavigationFields = latestInferredNavigationSemantic.get() + .fieldValues(); + IntIdSet childIds = (IntIdSet) latestInferredNavigationFields.get(0); + IntIdSet parentIds = (IntIdSet) latestInferredNavigationFields.get(1); + if (parentNids.equals(IntSets.immutable.of(parentIds.toArray())) + && childNids.equals(IntSets.immutable.of(childIds.toArray()))) { + navigationChanged = false; + } + } + if (navigationChanged) { + IntIdSet newParentIds = IntIds.set.of(parentNids.toArray()); + IntIdSet newChildIds = IntIds.set.of(childNids.toArray()); + processSemantic(rs.getViewCalculator().updateFields(inferredNavigationNids[0], + Lists.immutable.of(newChildIds, newParentIds), updateStampNid)); + conceptsWithNavigationChanges.add(conceptNid); + } + } else { + throw new IllegalStateException( + "More than one semantic of pattern " + PrimitiveData.text(inferredNavigationPatternNid) + + "for component: " + PrimitiveData.text(conceptNid)); + } + } + +} diff --git a/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerService.java b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerService.java index 6ec9a739..293ef27e 100644 --- a/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerService.java +++ b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerService.java @@ -57,6 +57,8 @@ public default String getName() { public void buildNecessaryNormalForm(); + public ClassifierResults writeInferredResults(); + public int getConceptCount(); public ImmutableIntList getReasonerConceptSet(); @@ -69,6 +71,7 @@ public default String getName() { public LogicalExpression getNecessaryNormalForm(int id); + @Deprecated public ClassifierResults processResults(TrackingCallable trackingCallable, boolean reinferAllHierarchy) throws Exception; diff --git a/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerServiceBase.java b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerServiceBase.java index 2730eea0..723d6672 100644 --- a/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerServiceBase.java +++ b/reasoner/reasoner-service/src/main/java/dev/ikm/tinkar/reasoner/service/ReasonerServiceBase.java @@ -80,7 +80,14 @@ public void init(ViewCalculator viewCalculator, PatternFacade statedAxiomPattern } @Override - public ClassifierResults processResults(TrackingCallable callable, boolean reinferAllHierarchy) throws Exception { + public ClassifierResults writeInferredResults() { + InferredResultsWriter nnfw = new InferredResultsWriter(this); + return nnfw.write(); + } + + @Override + public ClassifierResults processResults(TrackingCallable callable, boolean reinferAllHierarchy) + throws Exception { ProcessReasonerResults task = new ProcessReasonerResults(this, reinferAllHierarchy, callable); return task.compute(); }