Skip to content

Commit

Permalink
Added Xcfa2CHC transformation
Browse files Browse the repository at this point in the history
  • Loading branch information
leventeBajczi committed Jul 9, 2024
1 parent 05f4293 commit b50dffd
Show file tree
Hide file tree
Showing 10 changed files with 912 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ xcfa.c
xcfa.dot
xcfa.json
*.plantuml
xcfa-*.smt2
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ include(
"xcfa/c2xcfa",
"xcfa/litmus2xcfa",
"xcfa/llvm2xcfa",
"xcfa/xcfa2chc",
"xcfa/xcfa-analysis",
"xcfa/xcfa-cli",

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import hu.bme.mit.theta.common.container.Containers;
import hu.bme.mit.theta.core.decl.ConstDecl;
import hu.bme.mit.theta.core.decl.Decl;
import hu.bme.mit.theta.core.decl.IndexedConstDecl;
import hu.bme.mit.theta.core.decl.ParamDecl;
import hu.bme.mit.theta.core.decl.VarDecl;
import hu.bme.mit.theta.core.model.ImmutableValuation;
Expand All @@ -27,6 +28,8 @@
import hu.bme.mit.theta.core.type.anytype.RefExpr;
import hu.bme.mit.theta.core.type.booltype.AndExpr;
import hu.bme.mit.theta.core.type.booltype.BoolType;
import hu.bme.mit.theta.core.type.booltype.ExistsExpr;
import hu.bme.mit.theta.core.type.booltype.ForallExpr;
import hu.bme.mit.theta.core.type.booltype.NotExpr;
import hu.bme.mit.theta.core.utils.IndexedVars.Builder;
import hu.bme.mit.theta.core.utils.indexings.VarIndexing;
Expand All @@ -35,12 +38,15 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkNotNull;
import static hu.bme.mit.theta.core.utils.TypeUtils.cast;

/**
* Utility functions related to expressions.
Expand Down Expand Up @@ -112,6 +118,69 @@ public static Collection<Expr<BoolType>> getConjuncts(final Expr<BoolType> expr)
}
}

/**
* Collect params of an expression into a given collection.
*
* @param expr Expression
* @param collectTo Collection where the params should be put
*/
public static void collectParams(final Expr<?> expr, final Collection<ParamDecl<?>> collectTo) {
if (expr instanceof RefExpr<?> refExpr) {
final Decl<?> decl = refExpr.getDecl();
if (decl instanceof ParamDecl<?> param) {
collectTo.add(param);
return;
}
}

if (expr instanceof ForallExpr forall) {
Set<ParamDecl<?>> params = new LinkedHashSet<>(getParams(forall.getOp()));
forall.getParamDecls().forEach(params::remove);
collectTo.addAll(params);
} else if (expr instanceof ExistsExpr exists) {
Set<ParamDecl<?>> params = new LinkedHashSet<>(getParams(exists.getOp()));
exists.getParamDecls().forEach(params::remove);
collectTo.addAll(params);
} else {
expr.getOps().forEach(op -> collectParams(op, collectTo));
}
}

/**
* Collect params from expressions into a given collection.
*
* @param exprs Expressions
* @param collectTo Collection where the variables should be put
*/
public static void collectParams(final Iterable<? extends Expr<?>> exprs, final Collection<ParamDecl<?>> collectTo) {
exprs.forEach(e -> collectParams(e, collectTo));
}

/**
* Get variables of an expression.
*
* @param expr Expression
* @return Set of variables appearing in the expression
*/
public static Set<ParamDecl<?>> getParams(final Expr<?> expr) {
final Set<ParamDecl<?>> vars = Containers.createSet();
collectParams(expr, vars);
return vars;
}

/**
* Get variables of expressions.
*
* @param exprs Expressions
* @return Set of variables appearing in the expressions
*/
public static Set<ParamDecl<?>> getParams(final Iterable<? extends Expr<?>> exprs) {
final Set<ParamDecl<?>> vars = Containers.createSet();
collectParams(exprs, vars);
return vars;
}


/**
* Collect variables of an expression into a given collection.
*
Expand Down Expand Up @@ -165,6 +234,59 @@ public static Set<VarDecl<?>> getVars(final Iterable<? extends Expr<?>> exprs) {
return vars;
}

/**
* Collect indexed constants of an expression into a given collection.
*
* @param expr Expression
* @param collectTo Collection where the constants should be put
*/
public static void collectIndexedConstants(final Expr<?> expr, final Collection<IndexedConstDecl<?>> collectTo) {
if (expr instanceof RefExpr) {
final RefExpr<?> refExpr = (RefExpr<?>) expr;
final Decl<?> decl = refExpr.getDecl();
if (decl instanceof IndexedConstDecl<?>) {
final IndexedConstDecl<?> constDecl = (IndexedConstDecl<?>) decl;
collectTo.add(constDecl);
return;
}
}
expr.getOps().forEach(op -> collectIndexedConstants(op, collectTo));
}

/**
* Collect indexed constants from expressions into a given collection.
*
* @param exprs Expressions
* @param collectTo Collection where the constants should be put
*/
public static void collectIndexedConstants(final Iterable<? extends Expr<?>> exprs, final Collection<IndexedConstDecl<?>> collectTo) {
exprs.forEach(e -> collectIndexedConstants(e, collectTo));
}

/**
* Get indexed constants of an expression.
*
* @param expr Expression
* @return Set of constants appearing in the expression
*/
public static Set<IndexedConstDecl<?>> getIndexedConstants(final Expr<?> expr) {
final Set<IndexedConstDecl<?>> consts = new HashSet<>();
collectIndexedConstants(expr, consts);
return consts;
}

/**
* Get indexed constants of expressions.
*
* @param exprs Expressions
* @return Set of constants appearing in the expressions
*/
public static Set<IndexedConstDecl<?>> getIndexedConstants(final Iterable<? extends Expr<?>> exprs) {
final Set<IndexedConstDecl<?>> consts = new HashSet<>();
collectIndexedConstants(exprs, consts);
return consts;
}

/**
* Collect constants of an expression into a given collection.
*
Expand Down Expand Up @@ -363,4 +485,22 @@ public static int nodeCountSize(final Expr<?> expr) {
return 1 + expr.getOps().stream().map(ExprUtils::nodeCountSize).reduce(0, (x, y) -> x + y);
}

/**
* Change fixed subexpressions using a lookup
*
* @param expr the expr to change subexpressions in
* @param lookup the lookup mapping subexpression to replacements
* @return the changed expression
*/
public static <T extends Type> Expr<T> changeSubexpr(Expr<T> expr, Map<Expr<?>, Expr<?>> lookup) {
if (lookup.containsKey(expr)) {
return cast(lookup.get(expr), expr.getType());
} else {
return expr.map(e -> changeSubexpr(e, lookup));
}
}

public static <T extends Type> Expr<T> changeDecls(Expr<T> expr, Map<? extends Decl<?>, ? extends Decl<?>> lookup) {
return changeSubexpr(expr, lookup.entrySet().stream().map(entry -> Map.entry(entry.getKey().getRef(), entry.getValue().getRef())).collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
}
}
1 change: 1 addition & 0 deletions subprojects/xcfa/xcfa-cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation(project(":theta-analysis"))
implementation(project(":theta-xcfa"))
implementation(project(":theta-xcfa-analysis"))
implementation(project(":theta-xcfa2chc"))
implementation(project(":theta-c2xcfa"))
implementation(project(":theta-solver-z3"))
implementation(project(":theta-solver-z3-legacy"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import hu.bme.mit.theta.xcfa.passes.LbePass
import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass
import hu.bme.mit.theta.xcfa.passes.StaticCoiPass
import hu.bme.mit.theta.xcfa.toC
import hu.bme.mit.theta.xcfa2chc.toSMT2CHC
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.random.Random
Expand Down Expand Up @@ -245,6 +246,13 @@ private fun preVerificationLogging(
"Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n"
)

if (!config.outputConfig.chcOutputConfig.disable) {
xcfa.procedures.forEach {
val chcFile = File(resultFolder, "xcfa-${it.name}.smt2")
chcFile.writeText(it.toSMT2CHC())
}
}

if (!config.outputConfig.xcfaOutputConfig.disable) {
val xcfaDotFile = File(resultFolder, "xcfa.dot")
xcfaDotFile.writeText(xcfa.toDot())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ data class OutputConfig(

val cOutputConfig: COutputConfig = COutputConfig(),
val xcfaOutputConfig: XcfaOutputConfig = XcfaOutputConfig(),
val chcOutputConfig: ChcOutputConfig = ChcOutputConfig(),
val witnessConfig: WitnessConfig = WitnessConfig(),
val argConfig: ArgConfig = ArgConfig(),
) : Config {
Expand All @@ -347,6 +348,11 @@ data class XcfaOutputConfig(
var disable: Boolean = false,
) : Config

data class ChcOutputConfig(
@Parameter(names = ["--disable-chc-serialization"])
var disable: Boolean = false,
) : Config

data class COutputConfig(
@Parameter(names = ["--disable-c-serialization"])
var disable: Boolean = false,
Expand Down
25 changes: 25 additions & 0 deletions subprojects/xcfa/xcfa2chc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2024 Budapest University of Technology and Economics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
plugins {
id("kotlin-common")
}

dependencies {
implementation(project(":theta-common"))
implementation(project(":theta-core"))
implementation(project(":theta-xcfa"))
implementation(project(":theta-solver-smtlib"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2024 Budapest University of Technology and Economics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package hu.bme.mit.theta.xcfa2chc

import com.google.common.base.Preconditions.checkArgument
import hu.bme.mit.theta.core.decl.Decls.Const
import hu.bme.mit.theta.core.decl.Decls.Param
import hu.bme.mit.theta.core.decl.ParamDecl
import hu.bme.mit.theta.core.type.Expr
import hu.bme.mit.theta.core.type.Type
import hu.bme.mit.theta.core.type.booltype.BoolExprs.*
import hu.bme.mit.theta.core.type.booltype.BoolType
import hu.bme.mit.theta.core.type.functype.FuncExprs
import hu.bme.mit.theta.core.type.functype.FuncType
import hu.bme.mit.theta.core.utils.ExprUtils
import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibSymbolTable
import hu.bme.mit.theta.solver.smtlib.impl.generic.GenericSmtLibTransformationManager
import java.util.*

/*
We want to be able to write logical derivations in the following way:
head_name(vars) += // derivations
expr(vars).
head_name(grounds) // facts
!head_name(vars) with exprs(vars) // queries
*/

open class Relation(val name: String, vararg paramTypes: Type) {
companion object {
private fun funcType(params: List<Type>, finalType: Type): FuncType<*, *> {
return if(params.size == 1) {
FuncType.of(params[0], finalType)
} else if(params.size > 1) {
FuncType.of(params[0], funcType(params.subList(1, params.size), finalType))
} else {
error("Nullary functions aren't handled here.")
}
}
}
val arity: Int = paramTypes.size
val rules: MutableList<Rule> = LinkedList()
val constDecl = if(arity == 0) Const(name, Bool()) else Const(name, funcType(paramTypes.toList(), Bool()))
open operator fun invoke(params: List<Expr<*>>) = RelationApp(this, params)
open operator fun invoke(vararg params: Expr<*>) = RelationApp(this, params.toList())
}

data class RelationApp(val relation: Relation, val params: List<Expr<*>>, val constraints: List<Expr<BoolType>> = emptyList()) {
init {
checkArgument(params.size == relation.arity)
}
val expr: Expr<BoolType> by lazy {
val coreExpr = if(params.size >= 1) {
FuncExprs.App(relation.constDecl.ref as Expr<FuncType<in Type, BoolType>>, params.map { it })
} else {
relation.constDecl.ref as Expr<BoolType>
}
if(constraints.isEmpty()) {
coreExpr
} else {
And(constraints + coreExpr)
}
}

operator fun plusAssign(constraints: List<Expr<BoolType>>) { relation.rules.add(Rule(expr, constraints)) }
operator fun plusAssign(constraint: Expr<BoolType>) { relation.rules.add(Rule(expr, listOf(constraint))) }

operator fun not() { relation.rules.add(Rule(False(), listOf(expr))) }
operator fun unaryPlus() { relation.rules.add(Rule(expr, listOf())) }
infix fun with(constraints: List<Expr<BoolType>>) = copy(constraints = this.constraints + constraints)
infix fun with(constraint: Expr<BoolType>) = copy(constraints = this.constraints + constraint)
}
data class Rule(val head: Expr<BoolType>, val constraints: List<Expr<BoolType>>) {
fun toExpr() = Forall(ExprUtils.getParams(head) + ExprUtils.getParams(constraints), Imply(And(constraints), head))
}

operator fun Expr<BoolType>.plus(other: Expr<BoolType>) = listOf(this, other)

data class ParamHolder<T: Type>(private val type: T) {
private val lookup = LinkedHashMap<Int, ParamDecl<T>>()
operator fun get(i: Int) = lookup.getOrPut(i) { Param("P$i", type) }.ref
}

fun List<Relation>.toSMT2(): String {
val symbolTable = GenericSmtLibSymbolTable()
val transformationManager = GenericSmtLibTransformationManager(symbolTable)
val terms = flatMap { it.rules.map { "(assert " + transformationManager.toTerm(it.toExpr()) + ")" } }
val decls = map { symbolTable.getDeclaration(it.constDecl) }

return """
; generated by Theta
; https://github.com/ftsrg/theta/
(set-logic HORN)
; declarations
${decls.joinToString("\n")}
; facts, rules, queries
${terms.joinToString("\n")}
(check-sat)
(exit)
""".trimIndent()
}
fun Relation.toSMT2() = listOf(this).toSMT2()
Loading

0 comments on commit b50dffd

Please sign in to comment.