All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unive.lisa.analysis.nonRedundantSet.NonRedundantPowerset Maven / Gradle / Ivy

The newest version!
package it.unive.lisa.analysis.nonRedundantSet;

import it.unive.lisa.analysis.Lattice;
import it.unive.lisa.analysis.ScopeToken;
import it.unive.lisa.analysis.SemanticDomain;
import it.unive.lisa.analysis.SemanticException;
import it.unive.lisa.analysis.SemanticOracle;
import it.unive.lisa.analysis.lattices.Satisfiability;
import it.unive.lisa.analysis.lattices.SetLattice;
import it.unive.lisa.program.cfg.ProgramPoint;
import it.unive.lisa.symbolic.SymbolicExpression;
import it.unive.lisa.symbolic.value.Identifier;
import it.unive.lisa.util.representation.StringRepresentation;
import it.unive.lisa.util.representation.StructuredRepresentation;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Predicate;

/**
 * This abstract class generalize the abstract domain lattice whose domain is
 * the set of all the non redundant set in the power set of the domain of
 * another lattice (in this case the other lattice is {@code }). It also
 * defines the basic lattice operation for this domain (such as lub, glb,
 * widening, lessOrEqual and other operations needed for the calculations of the
 * previous ones). This implementations follows the guidelines of this
 * paper.
 * 
 * @param  the concrete type of NonRedundantPowerset
 * @param  the concrete type of the elements in the sets
 * @param  the type of {@link SymbolicExpression} that {@code } and in
 *                turn this domain, can process
 * @param  the type of {@link Identifier} that {@code } and in turn this
 *                domain, handle
 */
public abstract class NonRedundantPowerset,
		T extends SemanticDomain & Lattice,
		E extends SymbolicExpression,
		I extends Identifier> extends SetLattice implements SemanticDomain {

	/**
	 * An instance of the underlying lattice from which top and bottom can be
	 * retrieved. It is necessary in certain basic lattice operation.
	 */
	public final T valueDomain;

	/**
	 * Create an instance of non redundant set of elements of the type of
	 * valueDomain with the elements contained in elements.
	 * 
	 * @param elements    the elements to include in the lattice element
	 * @param isTop       whether or not this element should be the top element
	 *                        or not
	 * @param valueDomain an instance of the underlying lattice
	 */
	public NonRedundantPowerset(
			SortedSet elements,
			boolean isTop,
			T valueDomain) {
		super(elements, isTop);
		this.valueDomain = valueDomain;
	}

	/**
	 * Yields a new concrete set of elements equivalent to this but that is not
	 * redundant. An element x of a subset S of the domain of a lattice is
	 * redundant iff exists another element y in S such that x ≤ y. Given a
	 * redundant set is always possible to construct an equivalent set that is
	 * not redundant. This operator is usually called omega reduction
	 * (represented as Ω). Given a subset S of a domain of a lattice
	 * Ω(S) = S \ {s ∋ S | ( s = bottom ) OR ( ∃ s' ∋ S. s ≤
	 * s' )}
	 *
	 * @return an equivalent element that is not redundant.
	 * 
	 * @throws SemanticException if an error occurs during the computation
	 */
	protected C removeRedundancy() throws SemanticException {
		Set newElementsSet = new TreeSet<>();
		for (T element : this.elements) {
			if (!element.isBottom()) {
				boolean toRemove = false;
				for (T otherElement : this.elements)
					if (element.lessOrEqual(otherElement) && !otherElement.lessOrEqual(element))
						toRemove = true;
				if (!toRemove)
					newElementsSet.add(element);
			}
		}
		return mk(newElementsSet);
	}

	@Override
	public C top() {
		return mk(new TreeSet<>(), true, this.valueDomain);
	}

	@Override
	public C bottom() {
		return mk(new TreeSet<>(), false, this.valueDomain);
	}

	@Override
	public boolean isBottom() {
		return !this.isTop && this.elements.isEmpty();
	}

	@Override
	public boolean isTop() {
		return this.isTop && this.elements.isEmpty();
	}

	/**
	 * Performs the least upper bound between this non redundant set and the
	 * given one. The lub between two non redundant sets is the union of the two
	 * removed of redundancy.
	 */
	@Override
	public C lubAux(
			C other)
			throws SemanticException {
		Set lubSet = new HashSet(this.elements);
		lubSet.addAll(other.elements);
		return mk(lubSet).removeRedundancy();
	}

	@Override
	public C glbAux(
			C other)
			throws SemanticException {
		Set glbSet = new TreeSet<>();
		for (T s1 : this.elements)
			for (T s2 : other.elements)
				glbSet.add(s1.glb(s2));
		return mk(glbSet).removeRedundancy();
	}

	/**
	 * An Egli-Milner connector is an upper bound operator for the
	 * {@link #lessOrEqualEgliMilner(NonRedundantPowerset) Egli-Milner relation
	 * ≤EM}. An Egli-Milner connector is represented as
	 * +EM. Given two subsets S1 and S2 of a
	 * domain of a lattice S1 +EM S2 =
	 * S3 such that ( S1EM S3 )
	 * AND ( S1EM S3 ). The default
	 * implementation just performs the lub on the union of the two sets.
	 * 
	 * @param other the other concrete element
	 * 
	 * @return a new set that is ≤EM than both this and other
	 * 
	 * @throws SemanticException if an error occurs during the computation
	 */
	protected C EgliMilnerConnector(
			C other)
			throws SemanticException {
		Set unionSet = new HashSet(this.elements);
		unionSet.addAll(other.elements);
		T completeLub = valueDomain.bottom();
		if (!unionSet.isEmpty()) {
			for (T element : unionSet) {
				completeLub = completeLub.lub(element);
			}
		}
		Set newSet = new TreeSet<>();
		if (completeLub != null)
			newSet.add(completeLub);
		return mk(newSet);
	}

	/**
	 * This method implements the widening-connected hextrapolation heuristic
	 * proposed in the
	 * paper
	 * (represented as h). Given two subsets S1 and
	 * S2 of a domain of a lattice:
	 * 

* h( S1, S2) = LUB (S2, * Ω({ s1 ∇ s2 | s1 ∋ * S1, s2 ∋ S2, s1 < * s2})) *

* where *
    *
  • Ω is the {@link #removeRedundancy() omega reduction} operator * that removes redundancy from a set,
  • *
  • ∇ is the widening operator of the underlying lattice,
  • *
  • < is the strict partial order relation of the underlying * lattice,
  • *
  • LUB is the {@link #lubAux(NonRedundantPowerset) least upper bound} * operator between non redundant subsets of the domain of the underlying * lattice.
  • *
* * @param other the other set to perform the extrapolation with * * @return the new extrapolated set * * @throws SemanticException if an error occurs during the computation */ protected C extrapolationHeuristic( C other) throws SemanticException { Set extrapolatedSet = new TreeSet<>(); for (T s1 : this.elements) { for (T s2 : other.elements) { if (s1.lessOrEqual(s2) && !s2.lessOrEqual(s1)) extrapolatedSet.add(s1.widening(s2)); } } return mk(extrapolatedSet).removeRedundancy().lub(other); } /** * Perform the wideninig operation between two finite non redundant subsets * of the domain of a lattice following the Egli-Milner widening * implementation shown in this * paper. * Given two subset S1 and S2 of the domain of a * lattice widening(S1, S2) = * h(S1, T2), where * h is a widenining-connected extrapolation heuristic and * T2 is equal to: *
    *
  • S2         if * S1EM S2
  • *
  • S1 +EM S2   otherwise
  • *
* where ≤EM is the * {@link #lessOrEqualEgliMilner(NonRedundantPowerset) Egli-Milner relation} * and +EM is an Egli-Milner connector. */ @Override public C wideningAux( C other) throws SemanticException { if (lessOrEqualEgliMilner(other)) { return extrapolationHeuristic(other).removeRedundancy(); } else { return extrapolationHeuristic(EgliMilnerConnector(other)).removeRedundancy(); } } /** * For two subset S1 and S2 of the domain of a lattice * S1S S2 iff: ∀ s1 * ∋ S1, ∃ s2 ∋ S2 : * s1 ≤ s2. */ @Override public boolean lessOrEqualAux( C other) throws SemanticException { for (T s1 : this.elements) { boolean existsGreaterElement = false; for (T s2 : other.elements) { if (s1.lessOrEqual(s2)) { existsGreaterElement = true; break; } } if (!existsGreaterElement) return false; } return true; } /** * Yields {@code true} if and only if this element is in Egli-Milner * relation with the given one (represented as ≤EM). For two * subset S1 and S2 of the domain of a lattice * S1EM S2 iff: ( S1 * ≤S S2 ) AND ( ∀ s2 ∋ * S2, ∃ s1 ∋ S1 : s1 * ≤ s2 ). Where {@link #lessOrEqualAux(NonRedundantPowerset) * ≤S} is the less or equal relation between sets. This * operation is not commutative. * * @param other the other concrete element * * @return {@code true} if and only if that condition holds * * @throws SemanticException if an error occurs during the computation */ public boolean lessOrEqualEgliMilner( C other) throws SemanticException { if (lessOrEqual(other)) { if (!isBottom()) { for (T s2 : other.elements) { boolean existsLowerElement = false; for (T s1 : this.elements) { if (s1.lessOrEqual(s2)) { existsLowerElement = true; break; } } if (!existsLowerElement) return false; } } } else return false; return true; } @Override public C assign( I id, E expression, ProgramPoint pp, SemanticOracle oracle) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.assign(id, expression, pp, oracle)); } return mk(newElements).removeRedundancy(); } @Override public C smallStepSemantics( E expression, ProgramPoint pp, SemanticOracle oracle) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.smallStepSemantics(expression, pp, oracle)); } return mk(newElements).removeRedundancy(); } @Override public C assume( E expression, ProgramPoint src, ProgramPoint dest, SemanticOracle oracle) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.assume(expression, src, dest, oracle)); } return mk(newElements).removeRedundancy(); } @Override public C forgetIdentifier( Identifier id) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.forgetIdentifier(id)); } return mk(newElements).removeRedundancy(); } @Override public C forgetIdentifiersIf( Predicate test) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.forgetIdentifiersIf(test)); } return mk(newElements).removeRedundancy(); } @Override public C pushScope( ScopeToken token) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.pushScope(token)); } return mk(newElements).removeRedundancy(); } @Override public C popScope( ScopeToken token) throws SemanticException { Set newElements = new TreeSet<>(); for (T elem : this.elements) { newElements.add(elem.popScope(token)); } return mk(newElements).removeRedundancy(); } @Override public StructuredRepresentation representation() { if (isBottom()) return Lattice.bottomRepresentation(); String representation = "["; boolean first = true; for (T element : this.elements) { if (!first) representation += ", "; else first = false; representation += element.representation(); } representation += "]"; return new StringRepresentation(representation); } @Override public C mk( Set set) { SortedSet sorted = set instanceof SortedSet ? (SortedSet) set : new TreeSet<>(set); if (set.isEmpty()) return mk(sorted, false, this.valueDomain); for (T elem : set) { if (elem.isTop()) return mk(new TreeSet<>(), true, this.valueDomain); } return mk(sorted, false, this.valueDomain); } /** * Utility used for creating a concrete instance of * {@link NonRedundantPowerset} given a set, whether or not the element is * the top element and an instance of the the underlying lattice. * * @param set the set containing the elements that must be included * in the lattice instance * @param isTop wheter or not the element is top * @param valueDomain an instance of the underlying lattice * * @return a new concrete instance of {@link NonRedundantPowerset} with the * given configuration passed */ public abstract C mk( SortedSet set, boolean isTop, T valueDomain); @Override public Satisfiability satisfies( E expression, ProgramPoint pp, SemanticOracle oracle) throws SemanticException { Set setSatisf = new HashSet(); for (T element : this.elements) setSatisf.add(element.satisfies(expression, pp, oracle)); if ((setSatisf.contains(Satisfiability.SATISFIED) && setSatisf.contains(Satisfiability.NOT_SATISFIED)) || setSatisf.contains(Satisfiability.UNKNOWN)) return Satisfiability.UNKNOWN; else if (setSatisf.contains(Satisfiability.SATISFIED)) return Satisfiability.SATISFIED; else if (setSatisf.contains(Satisfiability.NOT_SATISFIED)) return Satisfiability.NOT_SATISFIED; return Satisfiability.UNKNOWN; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.elements == null) ? 0 : this.elements.hashCode()); result = prime * result + ((valueDomain == null) ? 0 : valueDomain.hashCode()); result = prime * result + (isTop ? 1231 : 1237); return result; } @Override public boolean equals( Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; NonRedundantPowerset other = (NonRedundantPowerset) obj; if (this.elements == null) { if (other.elements != null) return false; } else if (!this.elements.equals(other.elements)) return false; if (valueDomain == null) { if (other.valueDomain != null) return false; } else if (!valueDomain.equals(other.valueDomain)) return false; if (isTop != other.isTop) return false; return true; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy