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

it.unich.scalafix.graphs.GraphBody.scala Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/**
 * Copyright 2015, 2016, 2017, 2022 Gianluca Amato 
 *
 * This file is part of ScalaFix. ScalaFix is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * ScalaFix is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of a MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * ScalaFix. If not, see .
 */

package it.unich.scalafix.graphs

import it.unich.scalafix.*
import it.unich.scalafix.utils.Domain
import it.unich.scalafix.utils.Relation

/**
 * The effect of an edge `e` of a graph over an assignment `rho`.
 *
 * @tparam U
 *   the type for the unknowns of the assignment `rho`.
 * @tparam V
 *   the type for the values assumed by the unknowns in `rho`.
 * @tparam E
 *   the type for edges.
 */
type EdgeAction[U, V, E] = Assignment[U, V] => E => V

/**
 * An body generated by an hyper-graph, i.e., a graph where each edge has
 * several source nodes and a single target node. Unknowns are nodes of the
 * graph. Given an assignment, each hyper-edge produces a partial value. These
 * values are combined with the upper bound operation of the domain `V`.
 *
 * It is required that if `edgeAction(rho)(e)` depends on `rho(x)`, then `x`
 * shoud be in the sources of edge `e`. There are obvious coherence conditions
 * among `sources`, `target`, `ingoing` and `outgoing`.
 *
 * An unknown `x` which has no incoming edges corresponds to an equation `x=x`,
 * with no influence from `x` to itself. This is useful for having an unknown
 * which keeps its value fixed to that specificied in the initial assignment.
 *
 * @tparam U
 *   the type for the unknowns, which are also the nodes of the graph.
 * @tparam V
 *   the type for the values assumed by the unknowns, which is also the type of
 *   the values generated by each edge trough the `edgeAction` function.
 * @tparam E
 *   type of edges.
 */
trait GraphBody[U, V, E] extends Body[U, V]:

  /** Map each edge to its source nodes */
  def sources: Relation[E, U]

  /** Map each edge to its target node. */
  def target: E => U

  /** Map each unknown to its outgoing edges. */
  def outgoing: Relation[U, E]

  /** Map each unknown to its ingoing edges. */
  def ingoing: Relation[U, E]

  /** Returns the edge action of the graph. */
  def edgeAction: EdgeAction[U, V, E]

  /**
   * Returns the operation used from combining the contributions of different
   * edges.
   */
  def combiner: (V, V) => V

  /**
   * Returns a new graph-based body obtained by adding combos to this graph in a
   * localized way.
   *
   * @param combos
   *   the assignment of combos to unknowns.
   * @param unknownOrdering
   *   an ordering on unknowns used to decide the edges where combos need to be
   *   applied.
   */
  def addLocalizedCombos(
      combos: ComboAssignment[U, V],
      unknownOrdering: Ordering[U]
  ): GraphBody[U, V, E]

  /**
   * Returns a new graph-based body obtained by adding warrowings to this graph
   * in a localized way. Localized warrowings require a different procedure than
   * standard localized widenings or narrowings. Moreover, it is not entirely
   * clear whether this works as intended or not.
   *
   * @param widenings
   *   the assignment of widenings to unknowns.
   * @param narrowings
   *   the assignment of narrowings to unknowns.
   * @param unknownOrdering
   *   an ordering on unknowns, used to decide the edges where combos need to be
   *   applied.
   * @param valuesParialOrdering
   *   a partial ordering on values, used to decide whether widening or
   *   narrowing should be applied.
   */
  def addLocalizedWarrowing(
      widenings: ComboAssignment[U, V],
      narrowings: ComboAssignment[U, V],
      unknownOrdering: Ordering[U]
  )(using Domain[V]): Body[U, V]

/**
 * Standard implementation of a `GraphBody` where all the data about the graph
 * is provided explicitly.
 *
 * @param sources
 *   maps each edge to its source unknowns.
 * @param target
 *   each edge to its target unknown.
 * @param ingoing
 *   maps each unknown to the collection of edges departing from it.
 * @param outgoing
 *   maps each unknown to the collection of edges arriving on it.
 * @param edgeAction
 *   the action of and edge over an assignment.
 * @param combiner
 *   rhe operation used from combining the contributions of different edges.
 */
case class SimpleGraphBody[U, V, E](
    sources: Relation[E, U],
    target: E => U,
    outgoing: Relation[U, E],
    ingoing: Relation[U, E],
    edgeAction: EdgeAction[U, V, E],
    combiner: (V, V) => V
) extends GraphBody[U, V, E]:

  override def apply(rho: Assignment[U, V]) = (x: U) =>
    val in = ingoing(x)
    if in.isEmpty
    then rho(x)
    else
      val contributions = for e <- in yield edgeAction(rho)(e)
      contributions reduce combiner

  override def addLocalizedCombos(
      combos: ComboAssignment[U, V],
      unknownOrdering: Ordering[U]
  ): GraphBody[U, V, E] =
    val newEdgeAction =
      (rho: Assignment[U, V]) =>
        (e: E) =>
          val x = target(e)
          if combos.isDefinedAt(x) && sources(e).exists(unknownOrdering.lteq(x, _))
          then combos(x)(rho(x), edgeAction(rho)(e))
          else edgeAction(rho)(e)
    if combos.combosAreIdempotent
    then copy(edgeAction = newEdgeAction)
    else
      val newSources = (e: E) =>
        val x = target(e)
        if combos.isDefinedAt(x) && sources(e).exists(unknownOrdering.lteq(x, _))
        then sources(e) + x
        else sources(e)
      val newOutgoing = (u: U) =>
        if combos.isDefinedAt(u)
        then
          val edges = ingoing(u).filter(e => sources(e).exists(unknownOrdering.lteq(u, _)))
          outgoing(u) ++ edges
        else outgoing(u)
      copy(
        edgeAction = newEdgeAction,
        sources = Relation(newSources),
        outgoing = Relation(newOutgoing)
      )

  override def addLocalizedWarrowing(
      widenings: ComboAssignment[U, V],
      narrowings: ComboAssignment[U, V],
      unknownOrdering: Ordering[U]
  )(using Domain[V]): Body[U, V] =
    (rho: Assignment[U, V]) =>
      (x: U) =>
        val in = ingoing(x)
        if in.isEmpty then rho(x)
        else
          val contributions = for e <- in yield
            val contrib = edgeAction(rho)(e)
            val comboapply = sources(e)
              .exists(unknownOrdering.lteq(x, _)) && !(contrib <= rho(x))
            (contrib, comboapply)
          val result = contributions reduce ((x: (V, Boolean), y: (V, Boolean)) =>
            (combiner(x._1, y._1), x._2 || y._2)
          )
          if result._2 then widenings(x)(rho(x), result._1)
          else if result._1 < rho(x) then narrowings(x)(rho(x), result._1)
          else result._1

/** Collection of factory methods for graph-based bodies. */
object GraphBody:
  /**
   * Standard implementation of a `GraphBody` where all the data about the graph
   * is provided explicitly.
   *
   * @see
   *   [[SimpleGraphBody]] for the meaning of all the parameters.
   */
  def apply[U, V, E](
      sources: Relation[E, U],
      target: E => U,
      outgoing: Relation[U, E],
      ingoing: Relation[U, E],
      edgeAction: EdgeAction[U, V, E],
      combiner: (V, V) => V
  ): GraphBody[U, V, E] =
    SimpleGraphBody(sources, target, outgoing, ingoing, edgeAction, combiner)

/**
 * Standard implementation of a `GraphBody` where all the data about the graph
 * is provided explicitly. The `combiner` parameter is given by the upper bound
 * of the domain `V`.
 *
 * @see
 *   [[SimpleGraphBody]] for the meaning of all the parameters.
 */
def apply[U, V: Domain, E](
    sources: Relation[E, U],
    target: E => U,
    outgoing: Relation[U, E],
    ingoing: Relation[U, E],
    edgeAction: EdgeAction[U, V, E]
): GraphBody[U, V, E] =
  SimpleGraphBody(sources, target, outgoing, ingoing, edgeAction, summon[Domain[V]].upperBound)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy