elk-reasoner/src/main/java/org/semanticweb/elk/reasoner/saturation/properties/SubPropertyExplorer.java
static Multimap<IndexedObjectProperty, IndexedObjectProperty> getLeftSubComposableSubPropertiesByRightProperties(
			IndexedObjectProperty input,
			Producer<? super SubPropertyChainInference> inferenceProducer,
			final PropertyHierarchyCompositionState.Dispatcher dispatcher) {
		SaturatedPropertyChain saturation = input.getSaturated();
		if (saturation.leftSubComposableSubPropertiesByRightPropertiesComputed)
			return saturation.leftSubComposableSubPropertiesByRightProperties;
		// else
		synchronized (saturation) {
			if (saturation.leftSubComposableSubPropertiesByRightProperties == null)
				saturation.leftSubComposableSubPropertiesByRightProperties = new HashSetMultimap<IndexedObjectProperty, IndexedObjectProperty>();
		}
		synchronized (saturation.leftSubComposableSubPropertiesByRightProperties) {
			if (saturation.leftSubComposableSubPropertiesByRightPropertiesComputed)
				return saturation.leftSubComposableSubPropertiesByRightProperties;
			// else compute it
			Set<IndexedObjectProperty> subProperties = getSubProperties(input,
					inferenceProducer, dispatcher);
			for (IndexedPropertyChain subPropertyChain : getSubPropertyChains(
					input, inferenceProducer, dispatcher)) {
				if (subPropertyChain instanceof IndexedComplexPropertyChain) {
					IndexedComplexPropertyChain composition = (IndexedComplexPropertyChain) subPropertyChain;
					Set<IndexedObjectProperty> leftSubProperties = getSubProperties(
							composition.getFirstProperty(), inferenceProducer,
							dispatcher);
					Set<IndexedObjectProperty> commonSubProperties = new LazySetIntersection<IndexedObjectProperty>(
							subProperties, leftSubProperties);
					if (commonSubProperties.isEmpty())
						continue;
					// else
					for (IndexedObjectProperty rightSubProperty : getSubProperties(
							composition.getSuffixChain(), inferenceProducer,
							dispatcher))
						for (IndexedObjectProperty commonLeft : commonSubProperties)
							saturation.leftSubComposableSubPropertiesByRightProperties
									.add(rightSubProperty, commonLeft);
				}
			}
			saturation.leftSubComposableSubPropertiesByRightPropertiesComputed = true;
		}
		return saturation.leftSubComposableSubPropertiesByRightProperties;
	}
static Multimap<IndexedObjectProperty, IndexedObjectProperty> getLeftSubComposableSubPropertiesByRightProperties(
			IndexedObjectProperty input,
			Producer<? super SubPropertyChainInference> inferenceProducer,
			final PropertyHierarchyCompositionState.Dispatcher dispatcher) {
		SaturatedPropertyChain saturation = input.getSaturated();
		if (saturation.leftSubComposableSubPropertiesByRightPropertiesComputed)
			return saturation.leftSubComposableSubPropertiesByRightProperties;
		// else
		synchronized (saturation) {
			if (saturation.leftSubComposableSubPropertiesByRightProperties == null)
				saturation.leftSubComposableSubPropertiesByRightProperties = new HashSetMultimap<IndexedObjectProperty, IndexedObjectProperty>();
		}
		synchronized (saturation.leftSubComposableSubPropertiesByRightProperties) {
			if (saturation.leftSubComposableSubPropertiesByRightPropertiesComputed)
				return saturation.leftSubComposableSubPropertiesByRightProperties;
			// else compute it
			Set<IndexedObjectProperty> subProperties = getSubProperties(input,
					inferenceProducer, dispatcher);
			for (IndexedPropertyChain subPropertyChain : getSubPropertyChains(
					input, inferenceProducer, dispatcher)) {
				if (subPropertyChain instanceof IndexedComplexPropertyChain) {
					IndexedComplexPropertyChain composition = (IndexedComplexPropertyChain) subPropertyChain;
					Set<IndexedObjectProperty> leftSubProperties = getSubProperties(
							composition.getFirstProperty(), inferenceProducer,
							dispatcher);
					Set<IndexedObjectProperty> commonSubProperties = new LazySetIntersection<IndexedObjectProperty>(
							subProperties, leftSubProperties);
					if (commonSubProperties.isEmpty())
						continue;
					// else
					for (IndexedObjectProperty rightSubProperty : getSubProperties(
							composition.getSuffixChain(), inferenceProducer,
							dispatcher))
						for (IndexedObjectProperty commonLeft : commonSubProperties)
							saturation.leftSubComposableSubPropertiesByRightProperties
									.add(rightSubProperty, commonLeft);
				}
			}
			saturation.leftSubComposableSubPropertiesByRightPropertiesComputed = true;
		}
		return saturation.leftSubComposableSubPropertiesByRightProperties;
	}
Given an {@link IndexedObjectProperty} Computes a {@link Multimap} from
{@link IndexedObjectProperty}s to {@link IndexedObjectProperty}
consisting of the the assignments T -> S such that both S and
ObjectPropertyChain(S, T) are sub-properties of the given
{@link IndexedObjectProperty}. The provided {@link Producer} is used to
record all {@link ObjectPropertyInference}s that are applied in this
computation of sub-{@link IndexedPropertyChain}s involved. It is ensured
that the computation is performed only once.

@param input
a given {@link IndexedObjectProperty}
@param inferenceProducer
@return a multimap consisting of assignments T -> S such that both S and
ObjectPropertyChain(S, T) are sub-properties of the given
{@link IndexedObjectProperty}